s3bundler.cfn.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. ##
  2. ## Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. ##
  4. ## Licensed under the Amazon Software License (the "License"). You may not use this
  5. ## file except in compliance with the License. A copy of the License is located at
  6. ##
  7. ## http://aws.amazon.com/asl/
  8. ##
  9. ## or in the "license" file accompanying this file. This file is distributed on an
  10. ## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied.
  11. ## See the License for the specific language governing permissions and limitations
  12. ## under the License.
  13. ##
  14. import troposphere
  15. import troposphere.ec2
  16. import troposphere.s3
  17. import troposphere.cloudformation
  18. import troposphere.sqs
  19. import troposphere.logs
  20. import troposphere.iam
  21. import troposphere.ecs
  22. import ipaddress
  23. t = troposphere.Template()
  24. t.add_version("2010-09-09")
  25. S3ArchiveBucket = t.add_parameter(troposphere.Parameter(
  26. "S3ArchiveBucket",
  27. Default="",
  28. Description="S3 Bucket where you will store the output archives. If empty, it will be created. An existing bucket needs to notify S3BundlerQueueName on new manifests written by s3grouper.",
  29. Type="String",
  30. ))
  31. NeedsArchiveBucket = t.add_condition(
  32. "NeedsArchiveBucket",
  33. troposphere.Equals(troposphere.Ref(S3ArchiveBucket), ""))
  34. S3ArchivePrefix = t.add_parameter(troposphere.Parameter(
  35. "S3ArchivePrefix",
  36. Default="archive",
  37. Description="Prefix within S3 Bucket where you will store the output archives",
  38. Type="String",
  39. ))
  40. S3ManifestPrefix = t.add_parameter(troposphere.Parameter(
  41. "S3ManifestPrefix",
  42. Default="manifests",
  43. Description="Prefix in the S3 archive bucket where you will store the intermediate manifests.",
  44. Type="String",
  45. ))
  46. S3InventoryBucket = t.add_parameter(troposphere.Parameter(
  47. "S3InventoryBucket",
  48. Description="S3 Bucket where you sent your S3 Storage Inventory. http://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html",
  49. Type="String",
  50. ))
  51. S3InventoryPrefix = t.add_parameter(troposphere.Parameter(
  52. "S3InventoryPrefix",
  53. Default="",
  54. Description="Prefix within the S3 Bucket where you sent your S3 Storage Inventory. http://docs.aws.amazon.com/AmazonS3/latest/dev/storage-inventory.html",
  55. Type="String",
  56. ))
  57. S3SourceBuckets = t.add_parameter(troposphere.Parameter(
  58. "S3SourceBuckets",
  59. Default="*",
  60. Description="Comma separated list of S3 buckets in ARN format with trailing '/*' with objects that need to be compressed and bundled. Leave '*' for all buckets in the account. e.g. arn:aws:s3:::foo/*,arn:aws:s3:::bar/*,arn:aws:s3:::baz/*",
  61. Type="CommaDelimitedList"
  62. ))
  63. S3GrouperURL = t.add_parameter(troposphere.Parameter(
  64. "S3GrouperURL",
  65. Description="Docker URL for s3grouper container",
  66. Type="String",
  67. ))
  68. S3BundlerURL = t.add_parameter(troposphere.Parameter(
  69. "S3BundlerURL",
  70. Description="Docker URL for s3bundler container",
  71. Type="String",
  72. ))
  73. S3BundlerQueueARN = t.add_parameter(troposphere.Parameter(
  74. "S3BundlerQueueARN",
  75. Default="",
  76. Description="SQS queue to tell s3bundler what manifests to work on. Leave empty to create a new queue. If it already exists, it needs to allow S3ArchiveBucket to send it messages and to be configured to send messages to S3BundlerDLQ.",
  77. Type="String",
  78. ))
  79. NeedsQueue = t.add_condition(
  80. "NeedsQueue",
  81. troposphere.Equals(troposphere.Ref(S3BundlerQueueARN), ""))
  82. S3BundlerDLQARN = t.add_parameter(troposphere.Parameter(
  83. "S3BundlerDLQARN",
  84. Default="",
  85. Description="SQS queue for messages that s3bundler failed to process. Leave empty to create a new queue. If it already exists, it needs to allow S3BundlerQueue to send it messages.",
  86. Type="String",
  87. ))
  88. NeedsDLQ = t.add_condition(
  89. "NeedsDLQ",
  90. troposphere.Equals(troposphere.Ref(S3BundlerDLQARN), ""))
  91. MaxSize = t.add_parameter(troposphere.Parameter(
  92. "MaxSize",
  93. Type="String",
  94. Description="Objects greater than maxsize will be copied directly to the destination bucket. Metadata will be stored alongside them. Checksums will not be calculated.",
  95. Default="2GB",
  96. AllowedPattern="^\\d+(GB|G|MB|M|KB|K|gb|g|mb|m|kb|k)?$"
  97. ))
  98. Compress = t.add_parameter(troposphere.Parameter(
  99. "Compress",
  100. Type="String",
  101. Description="Compress archives with gzip",
  102. Default="true",
  103. AllowedValues=["true","false"]
  104. ))
  105. CompressTrue = t.add_condition(
  106. "CompressTrue",
  107. troposphere.Equals("true", troposphere.Ref(Compress)))
  108. sqsdlq = t.add_resource(troposphere.sqs.Queue(
  109. "sqsdlq",
  110. MessageRetentionPeriod=1209600,
  111. DeletionPolicy=troposphere.Retain,
  112. Condition="NeedsDLQ"
  113. ))
  114. DLQChoice = troposphere.If("NeedsDLQ",
  115. troposphere.GetAtt(sqsdlq, "Arn"),
  116. troposphere.Ref(S3BundlerDLQARN))
  117. ManifestQueue = t.add_resource(troposphere.sqs.Queue(
  118. "ManifestQueue",
  119. MessageRetentionPeriod=1209600,
  120. VisibilityTimeout=1800,
  121. RedrivePolicy=troposphere.sqs.RedrivePolicy(
  122. deadLetterTargetArn=DLQChoice,
  123. maxReceiveCount=10
  124. ),
  125. DeletionPolicy=troposphere.Retain,
  126. Condition="NeedsQueue"
  127. ))
  128. QueueChoice = troposphere.If("NeedsQueue",
  129. troposphere.GetAtt(ManifestQueue, "Arn"),
  130. troposphere.Ref(S3BundlerQueueARN))
  131. ManifestQueuePolicy = t.add_resource(troposphere.sqs.QueuePolicy(
  132. "ManifestQueuePolicy",
  133. Queues=[troposphere.Ref(ManifestQueue)],
  134. PolicyDocument={
  135. "Version": "2012-10-17",
  136. "Id": "manifestqueuepolicy",
  137. "Statement": [
  138. {
  139. "Sid": "q1",
  140. "Effect": "Allow",
  141. "Principal": {"Service": "s3.amazonaws.com"},
  142. "Action": "SQS:SendMessage",
  143. "Resource": troposphere.GetAtt(ManifestQueue,"Arn"),
  144. "Condition": troposphere.If("NeedsArchiveBucket",
  145. {"ForAllValues:ArnLike": {
  146. "aws:SourceArn": troposphere.Join("",
  147. ["arn:aws:s3:::",
  148. troposphere.Ref(troposphere.AWS_STACK_NAME),"-archivebucket-*"])
  149. }},
  150. {"ForAllValues:ArnEquals": {
  151. "aws:SourceArn": troposphere.Join("", ["arn:aws:s3:::", troposphere.Ref(S3ArchiveBucket)])
  152. }})
  153. }
  154. ]
  155. },
  156. DeletionPolicy=troposphere.Retain,
  157. Condition="NeedsQueue"
  158. ))
  159. DLQPolicy = t.add_resource(troposphere.sqs.QueuePolicy(
  160. "DLQPolicy",
  161. Queues=[troposphere.Ref(sqsdlq)],
  162. PolicyDocument={
  163. "Version": "2012-10-17",
  164. "Id": "dlqpolicy",
  165. "Statement": [
  166. {
  167. "Sid": "dlq1",
  168. "Effect": "Allow",
  169. "Principal": "*",
  170. "Action": "SQS:SendMessage",
  171. "Resource": troposphere.GetAtt(sqsdlq,"Arn"),
  172. "Condition": {
  173. "ArnEquals": {
  174. "aws:SourceArn": troposphere.GetAtt(ManifestQueue, "Arn")
  175. }
  176. }
  177. }
  178. ]
  179. },
  180. DeletionPolicy=troposphere.Retain,
  181. Condition="NeedsDLQ"
  182. ))
  183. ArchiveBucket = t.add_resource(troposphere.s3.Bucket(
  184. "ArchiveBucket",
  185. Condition="NeedsArchiveBucket",
  186. DependsOn="ManifestQueuePolicy",
  187. DeletionPolicy=troposphere.Retain,
  188. NotificationConfiguration=troposphere.s3.NotificationConfiguration(
  189. QueueConfigurations=[
  190. troposphere.s3.QueueConfigurations(
  191. Filter=troposphere.s3.Filter(
  192. S3Key=troposphere.s3.S3Key(
  193. Rules=[
  194. troposphere.s3.Rules(Name="prefix", Value=troposphere.Ref(S3ManifestPrefix)),
  195. troposphere.s3.Rules(Name="suffix", Value=".index")
  196. ]
  197. )
  198. ),
  199. Event="s3:ObjectCreated:Put",
  200. Queue=QueueChoice
  201. )
  202. ]
  203. ),
  204. AccessControl="BucketOwnerFullControl",
  205. ))
  206. ArchiveS3Choice = troposphere.If("NeedsArchiveBucket",
  207. troposphere.Ref(ArchiveBucket),
  208. troposphere.Ref(S3ArchiveBucket))
  209. TaskRole = t.add_resource(troposphere.iam.Role(
  210. "TaskRole",
  211. AssumeRolePolicyDocument={
  212. "Version": "2012-10-17",
  213. "Statement": [
  214. {
  215. "Principal": {
  216. "Service": "ecs-tasks.amazonaws.com"
  217. },
  218. "Action": "sts:AssumeRole",
  219. "Sid": "",
  220. "Effect": "Allow"
  221. }
  222. ]
  223. },
  224. Policies=[
  225. troposphere.iam.Policy(PolicyName="s3bundler", PolicyDocument={
  226. "Version": "2012-10-17",
  227. "Statement": [
  228. {
  229. "Sid": "readonly",
  230. "Effect": "Allow",
  231. "Action": [
  232. "s3:GetObject",
  233. "s3:GetObjectTagging",
  234. "s3:GetObjectVersion"
  235. ],
  236. "Resource": troposphere.Ref(S3SourceBuckets)
  237. },
  238. {
  239. "Sid": "inventory",
  240. "Effect": "Allow",
  241. "Action": [
  242. "s3:ListBucket",
  243. "s3:GetBucketTagging",
  244. "s3:GetBucketLocation",
  245. "s3:GetObject",
  246. "s3:GetObjectTagging",
  247. "s3:GetObjectVersion"
  248. ],
  249. "Resource": [
  250. troposphere.Join("", ["arn:aws:s3:::", troposphere.Join("/", [
  251. troposphere.Ref(S3InventoryBucket),
  252. troposphere.Ref(S3InventoryPrefix),
  253. "*"
  254. ])]),
  255. troposphere.Join("", ["arn:aws:s3:::", troposphere.Join("/", [
  256. troposphere.Ref(S3InventoryBucket),
  257. ])])
  258. ]
  259. },
  260. {
  261. "Sid": "archivebucket",
  262. "Effect": "Allow",
  263. "Action": [
  264. "s3:ListBucket",
  265. "s3:GetBucketLocation",
  266. "s3:GetObject",
  267. "s3:PutObject"
  268. ],
  269. "Resource": [
  270. troposphere.Join("", ["arn:aws:s3:::", troposphere.Join("/", [
  271. ArchiveS3Choice,
  272. troposphere.Ref(S3ArchivePrefix),
  273. "*"
  274. ])]),
  275. troposphere.Join("", ["arn:aws:s3:::", troposphere.Join("/", [
  276. ArchiveS3Choice,
  277. troposphere.Ref(S3ManifestPrefix),
  278. "*"
  279. ])]),
  280. troposphere.Join("", ["arn:aws:s3:::", troposphere.Join("/", [
  281. ArchiveS3Choice
  282. ])])
  283. ]
  284. },
  285. {
  286. "Sid": "s3bundlerqueueaccess",
  287. "Effect": "Allow",
  288. "Action": [
  289. "sqs:ChangeMessageVisibility",
  290. "sqs:DeleteMessage",
  291. "sqs:ReceiveMessage",
  292. "sqs:GetQueueUrl",
  293. "sqs:GetQueueAttributes"
  294. ],
  295. "Resource": [ QueueChoice, DLQChoice ]
  296. }
  297. ]
  298. }
  299. )]
  300. ))
  301. s3grouperlogs = t.add_resource(troposphere.logs.LogGroup(
  302. "s3grouperlogs",
  303. ))
  304. s3bundlerlogs = t.add_resource(troposphere.logs.LogGroup(
  305. "s3bundlerlogs",
  306. ))
  307. # LMF1MJK9 = t.add_resource(troposphere.logs.MetricFilter(
  308. # "LMF1MJK9",
  309. # LogGroupName=troposphere.Ref(s3grouperlogs),
  310. # ))
  311. #
  312. S3BundlerErrors = t.add_resource(troposphere.logs.MetricFilter(
  313. "S3BundlerErrors",
  314. LogGroupName=troposphere.Ref(s3bundlerlogs),
  315. FilterPattern="\"ERROR:\" -glaciered -deleted",
  316. MetricTransformations=[troposphere.logs.MetricTransformation(
  317. MetricNamespace="S3Bundler",
  318. MetricName="Errors",
  319. MetricValue="1"
  320. )]
  321. ))
  322. ECSCluster = t.add_resource(troposphere.ecs.Cluster(
  323. "ECSCluster",
  324. ))
  325. s3bundlertask = t.add_resource(troposphere.ecs.TaskDefinition(
  326. "s3bundlertask",
  327. TaskRoleArn=troposphere.Ref(TaskRole),
  328. NetworkMode="host",
  329. Family="s3bundler",
  330. Volumes=[troposphere.ecs.Volume(Name="data")],
  331. ContainerDefinitions=[troposphere.ecs.ContainerDefinition(
  332. Name="s3bundler",
  333. Cpu=170,
  334. MemoryReservation=128,
  335. Image=troposphere.Ref(S3BundlerURL),
  336. Environment=[
  337. troposphere.ecs.Environment(
  338. Name="AWS_DEFAULT_REGION",
  339. Value=troposphere.Ref(troposphere.AWS_REGION)),
  340. troposphere.ecs.Environment(
  341. Name="TMP",
  342. Value="/mnt" )
  343. ],
  344. Command=[troposphere.If("CompressTrue", "-c", ""),
  345. "-q",troposphere.Select("5", troposphere.Split(":", QueueChoice)),
  346. "-b",ArchiveS3Choice,
  347. "-p",troposphere.Ref(S3ArchivePrefix),
  348. "-s",troposphere.Ref(MaxSize)],
  349. DockerLabels= {"Project": "S3Bundler"},
  350. LogConfiguration=troposphere.ecs.LogConfiguration(
  351. LogDriver="awslogs",
  352. Options={
  353. "awslogs-group": troposphere.Ref(s3bundlerlogs),
  354. "awslogs-region": troposphere.Ref(troposphere.AWS_REGION)
  355. }),
  356. Essential=True,
  357. DisableNetworking=False,
  358. ReadonlyRootFilesystem=False,
  359. MountPoints=[troposphere.ecs.MountPoint(
  360. SourceVolume="data",
  361. ContainerPath="/mnt"
  362. )]
  363. )]
  364. ))
  365. s3groupertask = t.add_resource(troposphere.ecs.TaskDefinition(
  366. "s3groupertask",
  367. TaskRoleArn=troposphere.Ref(TaskRole),
  368. NetworkMode="host",
  369. Family="s3grouper",
  370. ContainerDefinitions=[troposphere.ecs.ContainerDefinition(
  371. Name="s3grouper",
  372. MemoryReservation=64,
  373. Image=troposphere.Ref(S3GrouperURL),
  374. Environment=[troposphere.ecs.Environment(Name="AWS_DEFAULT_REGION",
  375. Value=troposphere.Ref(troposphere.AWS_REGION))],
  376. DockerLabels= {"Project": "S3Bundler"},
  377. LogConfiguration=troposphere.ecs.LogConfiguration(
  378. LogDriver="awslogs",
  379. Options={
  380. "awslogs-group": troposphere.Ref(s3grouperlogs),
  381. "awslogs-region": troposphere.Ref(troposphere.AWS_REGION)
  382. }),
  383. Essential=True,
  384. DisableNetworking=False,
  385. ReadonlyRootFilesystem=False,
  386. )]
  387. ))
  388. s3bundlerservice = []
  389. for n in range(0,50):
  390. s3bundlerservice.append(t.add_resource(troposphere.ecs.Service(
  391. "s3bundlerservice{0}".format(str(n)),
  392. Cluster=troposphere.Ref(ECSCluster),
  393. TaskDefinition=troposphere.Ref(s3bundlertask),
  394. DesiredCount=0
  395. )))
  396. t.add_output(troposphere.Output(
  397. "ECSCluster",
  398. Value=troposphere.Ref(ECSCluster)
  399. ))
  400. t.add_output(troposphere.Output(
  401. "ArchiveBucket",
  402. Value=ArchiveS3Choice
  403. ))
  404. t.add_output(troposphere.Output(
  405. "ManifestQueue",
  406. Value=QueueChoice
  407. ))
  408. t.add_output(troposphere.Output(
  409. "S3GrouperTask",
  410. Value=troposphere.Ref(s3groupertask)
  411. ))
  412. print(t.to_json())