rubycfn 0.4.10 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/Gemfile.lock +1 -1
  4. data/README.md +43 -67
  5. data/bin/rubycfn +17 -73
  6. data/lib/cli_methods.rb +2 -2
  7. data/lib/rubycfn/version.rb +1 -1
  8. data/templates/.env +2 -0
  9. data/templates/.env.acceptance +1 -0
  10. data/templates/.env.dependencies.rspec +6 -0
  11. data/templates/.env.development +1 -0
  12. data/templates/.env.production +1 -0
  13. data/templates/.env.rspec +1 -0
  14. data/templates/.env.test +1 -0
  15. data/templates/{.gitignore.erb → .gitignore} +3 -0
  16. data/templates/{.rubocop.yml.erb → .rubocop.yml} +14 -1
  17. data/templates/{Gemfile.erb → Gemfile} +0 -1
  18. data/templates/README.md +57 -0
  19. data/templates/{Rakefile.erb → Rakefile} +15 -8
  20. data/templates/bootstrap/dependency_stack.rb +49 -0
  21. data/templates/config.yaml +65 -0
  22. data/templates/lib/aws_helper/aws_sdk.rb +30 -0
  23. data/templates/{compiler.rb.erb → lib/aws_helper/compiler.rb} +15 -9
  24. data/templates/{dependencies.rb.erb → lib/aws_helper/dependencies.rb} +5 -3
  25. data/templates/{deploy.rb.erb → lib/aws_helper/deploy.rb} +6 -4
  26. data/templates/lib/aws_helper/helpers.rb +3 -0
  27. data/templates/{main_aws_helper.rb.erb → lib/aws_helper/main.rb} +0 -0
  28. data/templates/{upload_stack.rb.erb → lib/aws_helper/upload_stack.rb} +15 -6
  29. data/templates/lib/core/applications.rb +479 -0
  30. data/templates/lib/core/assume_role.rb +40 -0
  31. data/templates/lib/core/classes.rb +25 -0
  32. data/templates/{core_compile.rb.erb → lib/core/compile.rb} +1 -0
  33. data/templates/lib/core/dependencies.rb +22 -0
  34. data/templates/{core_deploy.rb.erb → lib/core/deploy.rb} +20 -10
  35. data/templates/lib/core/git.rb +15 -0
  36. data/templates/lib/core/init.rb +173 -0
  37. data/templates/{core_upload.rb.erb → lib/core/upload.rb} +0 -0
  38. data/templates/{main.rb.erb → lib/main.rb} +8 -6
  39. data/templates/lib/shared_concerns/global_variables.rb +56 -0
  40. data/templates/{helper_methods.rb.erb → lib/shared_concerns/helper_functions.rb} +0 -0
  41. data/templates/lib/shared_concerns/helper_methods.rb +3 -0
  42. data/templates/{shared_methods.rb.erb → lib/shared_concerns/shared_methods.rb} +9 -0
  43. data/templates/lib/stacks/acm_stack/certificate_manager.rb +79 -0
  44. data/templates/{new_stack.rb.erb → lib/stacks/acm_stack/main.rb} +3 -4
  45. data/templates/lib/stacks/ecs_stack/ecs_cluster.rb +344 -0
  46. data/templates/lib/stacks/ecs_stack/lifecycle_hook.rb +188 -0
  47. data/templates/lib/stacks/ecs_stack/load_balancer.rb +68 -0
  48. data/templates/{ecs_stack.rb.erb → lib/stacks/ecs_stack/main.rb} +2 -1
  49. data/templates/{project_stack.rb.erb → lib/stacks/parent_stack/main.rb} +2 -2
  50. data/templates/lib/stacks/parent_stack/parent.rb +18 -0
  51. data/templates/lib/stacks/vpc_stack/infra_vpc.rb +193 -0
  52. data/templates/{vpc_stack.rb.erb → lib/stacks/vpc_stack/main.rb} +1 -2
  53. data/templates/{parent_stack_spec.rb.erb → spec/lib/parent_spec.rb} +2 -5
  54. data/templates/{spec_helper.rb.erb → spec/spec_helper.rb} +2 -2
  55. metadata +54 -44
  56. data/format.vim +0 -3
  57. data/templates/.env.erb +0 -4
  58. data/templates/.env.production.erb +0 -6
  59. data/templates/.env.rspec.erb +0 -6
  60. data/templates/.env.test.erb +0 -6
  61. data/templates/.gitlab-ci.yml.erb +0 -75
  62. data/templates/aws_sdk.rb.erb +0 -18
  63. data/templates/core_diff.rb.erb +0 -59
  64. data/templates/ecs_stack_concern.rb.erb +0 -20
  65. data/templates/global_variables.rb.erb +0 -16
  66. data/templates/helpers.rb.erb +0 -7
  67. data/templates/new_concern.rb.erb +0 -10
  68. data/templates/project_concern.rb.erb +0 -26
  69. data/templates/subnets.rb.erb +0 -18
  70. data/templates/vpc_concerns.rb.erb +0 -87
  71. data/templates/vpc_spec.rb.erb +0 -39
@@ -0,0 +1,479 @@
1
+ # Generate nested stacks for each defined application, and create module dynamically.
2
+ def create_applications
3
+ resource :applications_stack,
4
+ amount: infra_config["applications"].count,
5
+ type: "AWS::CloudFormation::Stack" do |r, index|
6
+ name = infra_config["applications"].keys[index]
7
+ resource_name = name.tr("-", "_").cfnize
8
+ simple_name = resource_name.downcase
9
+ app_config = infra_config["applications"].values[index]
10
+ warn "App config is empty" unless app_config # Rubocop didn't understand that the variable is actually used.
11
+ # request_certificate("Gateway", gateway_domains[environment], :asellion_com_hosted_zone_id.ref, "us-east-1")
12
+
13
+ r._id("#{resource_name}Stack")
14
+ r.property(:template_url) { "#{simple_name}stack" }
15
+ r.property(:parameters) do
16
+ {
17
+ "Vpc": :vpc_stack.ref("Outputs.SfsVpc"),
18
+ "Cluster": :ecs_stack.ref("Outputs.SfsEcsCluster"),
19
+ "Listener": :ecs_stack.ref("Outputs.EcsLoadBalancerListener"),
20
+ "EcsServiceAutoScalingRoleArn": :ecs_stack.ref("Outputs.SfsEcsAutoScalingRoleArn"),
21
+ "HostedZoneId": :route53_stack.ref("Outputs.HostedZoneId"),
22
+ "HostedZoneName": :route53_stack.ref("Outputs.HostedZoneName"),
23
+ "LoadBalancerDnsName": :ecs_stack.ref("Outputs.EcsLoadBalancerUrl"),
24
+ "CanonicalHostedZoneId": :ecs_stack.ref("Outputs.EcsLoadBalancerHostedZoneId"),
25
+ "CertificateProviderFunctionArn": :acm_stack.ref("Outputs.CertificateProviderFunctionArn")
26
+ }
27
+ end
28
+ Object.const_set("#{resource_name}Stack", Module.new).class_eval <<-RUBY
29
+ extend ActiveSupport::Concern
30
+ include Rubycfn
31
+
32
+ included do
33
+ def env_vars_to_array(val)
34
+ vars = []
35
+ val.keys.each_with_index do |object, index|
36
+ vars.push(
37
+ Name: object,
38
+ Value: val.values[index]
39
+ )
40
+ end
41
+ vars
42
+ end
43
+
44
+ parameter :vpc,
45
+ description: "The VPC that the ECS cluster is deployed to",
46
+ type: "AWS::EC2::VPC::Id"
47
+
48
+ parameter :cluster,
49
+ description: "ECS Cluster ID",
50
+ type: "String"
51
+
52
+ parameter :listener,
53
+ description: "The Application Load Balancer listener to register with",
54
+ type: "String"
55
+
56
+ parameter :ecs_service_auto_scaling_role_arn,
57
+ description: "The ECS service auto scaling role ARN",
58
+ type: "String"
59
+
60
+ parameter :hosted_zone_id,
61
+ description: "Hosted Zone ID",
62
+ type: "String"
63
+
64
+ parameter :hosted_zone_name,
65
+ description: "Hosted Zone Name",
66
+ type: "String"
67
+
68
+ parameter :load_balancer_dns_name,
69
+ description: "URL of the ECS ALB",
70
+ type: "String"
71
+
72
+ parameter :canonical_hosted_zone_id,
73
+ description: "Canonical Hosted Zone Id of ALB",
74
+ type: "String"
75
+
76
+ parameter :certificate_provider_function_arn,
77
+ description: "ARN of certificate provider",
78
+ type: "String"
79
+
80
+ variable :min,
81
+ value: app_config["min"].to_s
82
+
83
+ variable :max,
84
+ value: app_config["max"].to_s
85
+
86
+ variable :container_port,
87
+ value: app_config["container_port"].to_s
88
+
89
+ variable :memory,
90
+ value: app_config["mem"].to_s
91
+
92
+ variable :image,
93
+ value: app_config["image"]
94
+
95
+ variable :env_vars,
96
+ value: app_config["env"],
97
+ filter: :env_vars_to_array
98
+
99
+ variable :priority,
100
+ value: app_config["priority"].to_s
101
+
102
+
103
+ description generate_stack_description("#{resource_name}Stack")
104
+ resource :service,
105
+ type: "AWS::ECS::Service" do |r|
106
+
107
+ r.property(:cluster) { :cluster.ref }
108
+ r.property(:role) { :service_role.ref }
109
+ r.property(:desired_count) { min.to_i }
110
+ r.property(:task_definition) { :task_definition.ref }
111
+ r.property(:load_balancers) do
112
+ [
113
+ "ContainerName": "#{simple_name}-service",
114
+ "ContainerPort": container_port.to_i,
115
+ "TargetGroupArn": :target_group.ref
116
+ ]
117
+ end
118
+ end
119
+
120
+ resource :task_definition,
121
+ type: "AWS::ECS::TaskDefinition" do |r|
122
+ r.property(:family) { "#{simple_name}-service" }
123
+ r.property(:container_definitions) do
124
+ [
125
+ {
126
+ "Name": "#{simple_name}-service",
127
+ "Essential": true,
128
+ "Image": image,
129
+ "Memory": memory.to_i,
130
+ "Environment": env_vars,
131
+ "PortMappings": [
132
+ {
133
+ "ContainerPort": container_port.to_i
134
+ }
135
+ ],
136
+ "LogConfiguration": {
137
+ "LogDriver": "awslogs",
138
+ "Options": {
139
+ "awslogs-group": :cloud_watch_logs_group.ref,
140
+ "awslogs-region": "AWS::Region".ref
141
+ }
142
+ }
143
+ }
144
+ ]
145
+ end
146
+ end
147
+
148
+ resource :cloud_watch_logs_group,
149
+ type: "AWS::Logs::LogGroup" do |r|
150
+ r.property(:log_group_name) { ["AWS::StackName".ref, "-#{simple_name}"].fnjoin }
151
+ r.property(:retention_in_days) { 30 }
152
+ end
153
+
154
+ resource :target_group,
155
+ type: "AWS::ElasticLoadBalancingV2::TargetGroup" do |r|
156
+ r.property(:vpc_id) { :vpc.ref }
157
+ r.property(:port) { container_port.to_i }
158
+ r.property(:protocol) { "HTTP" }
159
+ r.property(:matcher) do
160
+ {
161
+ "HttpCode": "200-299"
162
+ }
163
+ end
164
+ r.property(:health_check_interval_seconds) { 10 }
165
+ r.property(:health_check_path) { "/" }
166
+ r.property(:health_check_protocol) { "HTTP" }
167
+ r.property(:health_check_timeout_seconds) { 5 }
168
+ r.property(:healthy_threshold_count) { 2 }
169
+ end
170
+
171
+ resource :listener_rule,
172
+ type: "AWS::ElasticLoadBalancingV2::ListenerRule" do |r|
173
+ r.property(:listener_arn) { :listener.ref }
174
+ r.property(:priority) { priority.to_i }
175
+ r.property(:conditions) do
176
+ [
177
+ {
178
+ "Field": "host-header",
179
+ "Values": [
180
+ ["origin", name.tr("_", "-"), :hosted_zone_name.ref].fnjoin("."),
181
+ [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".")
182
+ ]
183
+ }
184
+ ]
185
+ end
186
+ r.property(:actions) do
187
+ [
188
+ {
189
+ "TargetGroupArn": :target_group.ref,
190
+ "Type": "forward"
191
+ }
192
+ ]
193
+ end
194
+ end
195
+
196
+ assume_role_policy_document = {
197
+ "Statement": [
198
+ {
199
+ "Effect": "Allow",
200
+ "Principal": {
201
+ "Service": [
202
+ "ecs.amazonaws.com"
203
+ ]
204
+ },
205
+ "Action": [
206
+ "sts:AssumeRole"
207
+ ]
208
+ }
209
+ ]
210
+ }
211
+
212
+ resource :service_role,
213
+ type: "AWS::IAM::Role" do |r|
214
+ r.property(:role_name) { "ecs-service-${AWS::StackName}".fnsub }
215
+ r.property(:path) { "/" }
216
+ r.property(:assume_role_policy_document) { JSON.pretty_generate(assume_role_policy_document) }
217
+ r.property(:policies) do
218
+ [
219
+ {
220
+ "PolicyName": "ecs-service-${AWS::StackName}".fnsub,
221
+ "PolicyDocument": {
222
+ "Version": "2012-10-17",
223
+ "Statement": [
224
+ {
225
+ "Effect": "Allow",
226
+ "Action": [
227
+ "ec2:AuthorizeSecurityGroupIngress",
228
+ "ec2:Describe*",
229
+ "elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
230
+ "elasticloadbalancing:Describe*",
231
+ "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
232
+ "elasticloadbalancing:DeregisterTargets",
233
+ "elasticloadbalancing:DescribeTargetGroups",
234
+ "elasticloadbalancing:DescribeTargetHealth",
235
+ "elasticloadbalancing:RegisterTargets"
236
+ ],
237
+ "Resource": "*"
238
+ }
239
+ ]
240
+ }
241
+ }
242
+ ]
243
+ end
244
+ end
245
+
246
+ resource :application_load_balancer_record,
247
+ type: "AWS::Route53::RecordSet" do |r|
248
+ r.property(:type) { "A" }
249
+ r.property(:name) { ["origin", name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".") }
250
+ r.property(:hosted_zone_id) { :hosted_zone_id.ref }
251
+ r.property(:alias_target) do
252
+ {
253
+ "DNSName": :load_balancer_dns_name.ref,
254
+ "EvaluateTargetHealth": true,
255
+ "HostedZoneId": :canonical_hosted_zone_id.ref
256
+ }
257
+ end
258
+ end
259
+
260
+ resource :service_scalable_target,
261
+ type: "AWS::ApplicationAutoScaling::ScalableTarget" do |r|
262
+ r.property(:max_capacity) { max.to_i }
263
+ r.property(:min_capacity) { min.to_i }
264
+ r.property(:resource_id) { ["service", :cluster.ref, :service.ref(:name)].fnjoin("/") }
265
+ r.property(:role_arn) { :ecs_service_auto_scaling_role_arn.ref }
266
+ r.property(:scalable_dimension) { "ecs:service:DesiredCount" }
267
+ r.property(:service_namespace) { "ecs" }
268
+ end
269
+
270
+ resource :service_scale_out_policy,
271
+ type: "AWS::ApplicationAutoScaling::ScalingPolicy" do |r|
272
+ r.property(:policy_name) { "ServiceScaleOutPolicy" }
273
+ r.property(:policy_type) { "StepScaling" }
274
+ r.property(:scaling_target_id) { :service_scalable_target.ref }
275
+ r.property(:step_scaling_policy_configuration) do
276
+ {
277
+ "AdjustmentType": "ChangeInCapacity",
278
+ "Cooldown": 300,
279
+ "MetricAggregationType": "Average",
280
+ "StepAdjustments": [
281
+ {
282
+ "MetricIntervalLowerBound": 0,
283
+ "ScalingAdjustment": 1
284
+ }
285
+ ]
286
+ }
287
+ end
288
+ end
289
+
290
+ resource :service_scale_in_policy,
291
+ type: "AWS::ApplicationAutoScaling::ScalingPolicy" do |r|
292
+ r.property(:policy_name) { "ServiceScaleInPolicy" }
293
+ r.property(:policy_type) { "StepScaling" }
294
+ r.property(:scaling_target_id) { :service_scalable_target.ref }
295
+ r.property(:step_scaling_policy_configuration) do
296
+ {
297
+ "AdjustmentType": "ChangeInCapacity",
298
+ "Cooldown": 600,
299
+ "MetricAggregationType": "Average",
300
+ "StepAdjustments": [
301
+ {
302
+ "MetricIntervalUpperBound": 0,
303
+ "ScalingAdjustment": -1
304
+ }
305
+ ]
306
+ }
307
+ end
308
+ end
309
+
310
+ resource :cpu_scale_out_alarm,
311
+ type: "AWS::CloudWatch::Alarm" do |r|
312
+ r.property(:alarm_name) { "CPU utilization greater than 90% on #{simple_name}-#{environment}" }
313
+ r.property(:alarm_description) { "Alarm if cpu utilization greater than 90% of reserved cpu" }
314
+ r.property(:namespace) { "AWS/ECS" }
315
+ r.property(:metric_name) { "CPUUtilization" }
316
+ r.property(:dimensions) do
317
+ [
318
+ {
319
+ "Name": "ClusterName",
320
+ "Value": :cluster.ref
321
+ },
322
+ {
323
+ "Name": "ServiceName",
324
+ "Value": :service.ref(:name)
325
+ }
326
+ ]
327
+ end
328
+ r.property(:statistic) { "Maximum" }
329
+ r.property(:period) { "60" }
330
+ r.property(:evaluation_periods) { "3" }
331
+ r.property(:threshold) { "90" }
332
+ r.property(:comparison_operator) { "GreaterThanThreshold" }
333
+ r.property(:alarm_actions) { [:service_scale_out_policy.ref] }
334
+ end
335
+
336
+ resource :cpu_scale_in_alarm,
337
+ type: "AWS::CloudWatch::Alarm" do |r|
338
+ r.property(:alarm_name) { "CPU utilization less than 70% on #{simple_name}-#{environment}" }
339
+ r.property(:alarm_description) { "Alarm if cpu utilization greater than 70% of reserved cpu" }
340
+ r.property(:namespace) { "AWS/ECS" }
341
+ r.property(:metric_name) { "CPUUtilization" }
342
+ r.property(:dimensions) do
343
+ [
344
+ {
345
+ "Name": "ClusterName",
346
+ "Value": :cluster.ref
347
+ },
348
+ {
349
+ "Name": "ServiceName",
350
+ "Value": :service.ref(:name)
351
+ }
352
+ ]
353
+ end
354
+ r.property(:statistic) { "Maximum" }
355
+ r.property(:period) { "60" }
356
+ r.property(:evaluation_periods) { "10" }
357
+ r.property(:threshold) { "70" }
358
+ r.property(:comparison_operator) { "LessThanThreshold" }
359
+ r.property(:alarm_actions) { [:service_scale_in_policy.ref] }
360
+ end
361
+
362
+ resource :ecs_application_certificate,
363
+ type: "Custom::Certificate" do |r|
364
+ r.property(:service_token) { :certificate_provider_function_arn.ref }
365
+ r.property(:region) { "us-east-1" }
366
+ r.property(:domain_name) { [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".") }
367
+ r.property(:validation_method) { "DNS" }
368
+ end
369
+
370
+ resource :ecs_application_issued_certificate,
371
+ type: "Custom::IssuedCertificate" do |r|
372
+ r.property(:service_token) { :certificate_provider_function_arn.ref }
373
+ r.property(:region) { "us-east-1" }
374
+ r.property(:certificate_arn) { :ecs_application_certificate.ref }
375
+ end
376
+
377
+ resource :ecs_application_dns_record,
378
+ type: "Custom::CertificateDNSRecord" do |r|
379
+ r.property(:service_token) { :certificate_provider_function_arn.ref }
380
+ r.property(:region) { "us-east-1" }
381
+ r.property(:domain_name) { [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".") }
382
+ r.property(:certificate_arn) { :ecs_application_certificate.ref }
383
+ end
384
+
385
+ resource :ecs_application_validation_record,
386
+ type: "AWS::Route53::RecordSetGroup" do |r|
387
+ r.property(:hosted_zone_id) { :hosted_zone_id.ref }
388
+ r.property(:record_sets) do
389
+ [
390
+ {
391
+ "Name": :ecs_application_dns_record.ref(:name),
392
+ "Type": :ecs_application_dns_record.ref(:type),
393
+ "TTL": 60,
394
+ "Weight": 1,
395
+ "SetIdentifier": :ecs_application_certificate.ref,
396
+ "ResourceRecords": [
397
+ :ecs_application_dns_record.ref(:value)
398
+ ]
399
+ }
400
+ ]
401
+ end
402
+ end
403
+
404
+ resource :cloudfront_distribution,
405
+ depends_on: :ecs_application_issued_certificate,
406
+ type: "AWS::CloudFront::Distribution" do |r|
407
+ r.property(:distribution_config) do
408
+ {
409
+ "Comment": ["Distribution config for", [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".")].fnjoin(" "),
410
+ "Aliases": [
411
+ [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin(".")
412
+ ],
413
+ "DefaultCacheBehavior": {
414
+ "AllowedMethods": %w(GET HEAD OPTIONS PUT PATCH POST DELETE),
415
+ "DefaultTTL": 0,
416
+ "TargetOriginId": "EcsApplication",
417
+ "ForwardedValues": {
418
+ "QueryString": true,
419
+ "Cookies": {
420
+ "Forward": "all"
421
+ },
422
+ "Headers": ["*"]
423
+ },
424
+ "MaxTTL": 0,
425
+ "MinTTL": 0,
426
+ "ViewerProtocolPolicy": "redirect-to-https"
427
+ },
428
+ "Enabled": true,
429
+ "HttpVersion": "http2",
430
+ "IPV6Enabled": false,
431
+ "Origins": [
432
+ {
433
+ "DomainName": ["origin", name.tr("_", "-"), :hosted_zone_name.ref].fnjoin("."),
434
+ "Id": "EcsApplication",
435
+ "CustomOriginConfig": {
436
+ "OriginProtocolPolicy": "http-only",
437
+ "HTTPPort": container_port.to_i,
438
+ "OriginKeepaliveTimeout": 5,
439
+ "OriginReadTimeout": 30
440
+ }
441
+ }
442
+ ],
443
+ "PriceClass": "PriceClass_All",
444
+ "ViewerCertificate": {
445
+ "AcmCertificateArn": :ecs_application_certificate.ref,
446
+ "SslSupportMethod": "sni-only"
447
+ }
448
+ }
449
+ end
450
+ r.property(:tags) do
451
+ [
452
+ {
453
+ "Key": "Environment",
454
+ "Value": environment.to_s
455
+ }
456
+ ]
457
+ end
458
+ end
459
+
460
+ resource :application_cloudfront_dns_record,
461
+ type: "AWS::Route53::RecordSetGroup" do |r|
462
+ r.property(:hosted_zone_id) { :hosted_zone_id.ref }
463
+ r.property(:record_sets) do
464
+ [
465
+ {
466
+ "Name": [name.tr("_", "-"), :hosted_zone_name.ref].fnjoin("."),
467
+ "Type": "A",
468
+ "AliasTarget": {
469
+ "HostedZoneId": "Z2FDTNDATAQYW2",
470
+ "DNSName": :cloudfront_distribution.ref(:domain_name)
471
+ }
472
+ }
473
+ ]
474
+ end
475
+ end
476
+ end
477
+ RUBY
478
+ end
479
+ end
@@ -0,0 +1,40 @@
1
+ require "aws-sdk-core"
2
+ require "aws-sdk-s3"
3
+ require "aws-sdk-iam"
4
+ require "aws-sdk-organizations"
5
+
6
+ def generate_session_name
7
+ "assume_role_" + (0...12).map { ("a".."z").to_a[rand(26)] }.join
8
+ end
9
+
10
+ def assume_role(aws_accounts, to_account, from_account = nil)
11
+ session_name = generate_session_name
12
+ if from_account
13
+ from_credentials = aws_accounts[from_account][:credentials]
14
+ credentials = Aws::AssumeRoleCredentials.new(
15
+ client: Aws::STS::Client.new(
16
+ access_key_id: from_credentials[:access_key_id],
17
+ region: "eu-central-1",
18
+ secret_access_key: from_credentials[:secret_access_key],
19
+ session_token: from_credentials[:session_token]
20
+ ),
21
+ role_arn: "arn:aws:iam::#{aws_accounts[to_account][:account_id]}:role/#{aws_accounts[to_account][:role_name]}",
22
+ role_session_name: session_name
23
+ )
24
+ else
25
+ credentials = Aws::AssumeRoleCredentials.new(
26
+ client: Aws::STS::Client.new(region: aws_accounts[to_account][:region]),
27
+ role_arn: "arn:aws:iam::#{aws_accounts[to_account][:account_id]}:role/#{aws_accounts[to_account][:role_name]}",
28
+ role_session_name: session_name
29
+ )
30
+ end
31
+ aws_accounts[to_account][:credentials] = {
32
+ access_key_id: credentials.credentials.access_key_id,
33
+ credentials: credentials,
34
+ region: aws_accounts[to_account][:region],
35
+ secret_access_key: credentials.credentials.secret_access_key,
36
+ session_name: session_name,
37
+ session_token: credentials.credentials.session_token
38
+ }
39
+ aws_accounts
40
+ end
@@ -0,0 +1,25 @@
1
+ # Add convenient method to reference secrets.
2
+ # Would be nice for parameter store as well!
3
+ class String
4
+ def secret(argument)
5
+ [
6
+ "{{resolve:secretsmanager:",
7
+ self.ref,
8
+ ":SecretString:",
9
+ argument,
10
+ "}}"
11
+ ].fnjoin
12
+ end
13
+ end
14
+
15
+ class Symbol
16
+ def secret(argument)
17
+ [
18
+ "{{resolve:secretsmanager:",
19
+ self.ref,
20
+ ":SecretString:",
21
+ argument,
22
+ "}}"
23
+ ].fnjoin
24
+ end
25
+ end
@@ -2,5 +2,6 @@ require "digest/md5"
2
2
  require "fileutils"
3
3
 
4
4
  require_relative "../aws_helper/main"
5
+ require_relative "../core/git"
5
6
 
6
7
  compile_stacks
@@ -0,0 +1,22 @@
1
+ require "aws-sdk-core"
2
+ require "aws-sdk-cloudformation"
3
+ require "dotenv"
4
+
5
+ Dotenv.load(".env.private")
6
+ Dotenv.load(".env")
7
+ Dotenv.load(".env.#{ENV["ENVIRONMENT"]}")
8
+
9
+ ENV["AWS_DEFAULT_REGION"] ||= "eu-west-1"
10
+ ENV["AWS_REGION"] ||= ENV["AWS_DEFAULT_REGION"]
11
+
12
+ client = Aws::CloudFormation::Client.new(region: ENV["AWS_REGION"])
13
+ res = client.describe_stacks(
14
+ stack_name: "DependencyStack"
15
+ )
16
+
17
+ dep_file = File.open(".env.dependencies", "w")
18
+ res[:stacks].each do |stack|
19
+ stack[:outputs].each do |output|
20
+ dep_file.puts "#{output[:output_key].upcase}=#{output[:output_value]}"
21
+ end
22
+ end
@@ -6,6 +6,10 @@ require "aws-sdk"
6
6
 
7
7
  require_relative "../aws_helper/main"
8
8
 
9
+ Dotenv.load(".env.private")
10
+ Dotenv.load(".env.dependencies")
11
+ raise "CLOUDFORMATIONBUCKET not found in DependencyStack outputs" unless ENV["CLOUDFORMATIONBUCKET"]
12
+
9
13
  env_vars = load_env_vars
10
14
 
11
15
  set_aws_credentials(
@@ -15,12 +19,22 @@ set_aws_credentials(
15
19
  )
16
20
 
17
21
  s3_filename = get_parent_stack_s3_location(
18
- env_vars[:artifact_bucket],
22
+ ENV["CLOUDFORMATIONBUCKET"],
19
23
  env_vars[:environment]
20
24
  )
21
25
 
22
26
  client = Aws::CloudFormation::Client.new
23
27
 
28
+ parent_parameters = []
29
+ File.open(".env.dependencies").read.each_line do |line|
30
+ line.strip!
31
+ param, value = line.split("=")
32
+ parent_parameters.push(
33
+ parameter_key: param,
34
+ parameter_value: value
35
+ )
36
+ end
37
+
24
38
  # Store previous CloudFormation events in an array, so that we don't output
25
39
  # events from previous deploys.
26
40
 
@@ -47,15 +61,13 @@ if stack_exists
47
61
  stack_name: env_vars[:stack_name],
48
62
  template_url: s3_filename,
49
63
  capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND),
50
- # stack_policy_body: "StackPolicyBody",
51
- # stack_policy_url: "StackPolicyURL",
64
+ parameters: parent_parameters,
52
65
  tags: [
53
66
  {
54
67
  key: "team",
55
- value: "<%= name.downcase %>"
68
+ value: "infra"
56
69
  }
57
- ],
58
- # client_request_token: "ClientRequestToken",
70
+ ]
59
71
  )
60
72
  else
61
73
  client.create_stack(
@@ -64,15 +76,13 @@ else
64
76
  timeout_in_minutes: 60,
65
77
  capabilities: %w(CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND),
66
78
  on_failure: "ROLLBACK",
67
- # stack_policy_body: "StackPolicyBody",
68
- # stack_policy_url: "StackPolicyURL",
79
+ parameters: parent_parameters,
69
80
  tags: [
70
81
  {
71
82
  key: "team",
72
- value: "<%= name.downcase %>"
83
+ value: "infra"
73
84
  }
74
85
  ],
75
- # client_request_token: "ClientRequestToken",
76
86
  enable_termination_protection: false
77
87
  )
78
88
  end
@@ -0,0 +1,15 @@
1
+ require "git-revision"
2
+
3
+ module Git
4
+ class Revision
5
+ class << self
6
+ def dirty?
7
+ !`git diff --numstat | wc -l`.strip.to_i.zero?
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ def git_revision
14
+ "#{Git::Revision.commit}#{Git::Revision.dirty? ? "-dirty" : ""}"
15
+ end