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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +1 -1
- data/README.md +43 -67
- data/bin/rubycfn +17 -73
- data/lib/cli_methods.rb +2 -2
- data/lib/rubycfn/version.rb +1 -1
- data/templates/.env +2 -0
- data/templates/.env.acceptance +1 -0
- data/templates/.env.dependencies.rspec +6 -0
- data/templates/.env.development +1 -0
- data/templates/.env.production +1 -0
- data/templates/.env.rspec +1 -0
- data/templates/.env.test +1 -0
- data/templates/{.gitignore.erb → .gitignore} +3 -0
- data/templates/{.rubocop.yml.erb → .rubocop.yml} +14 -1
- data/templates/{Gemfile.erb → Gemfile} +0 -1
- data/templates/README.md +57 -0
- data/templates/{Rakefile.erb → Rakefile} +15 -8
- data/templates/bootstrap/dependency_stack.rb +49 -0
- data/templates/config.yaml +65 -0
- data/templates/lib/aws_helper/aws_sdk.rb +30 -0
- data/templates/{compiler.rb.erb → lib/aws_helper/compiler.rb} +15 -9
- data/templates/{dependencies.rb.erb → lib/aws_helper/dependencies.rb} +5 -3
- data/templates/{deploy.rb.erb → lib/aws_helper/deploy.rb} +6 -4
- data/templates/lib/aws_helper/helpers.rb +3 -0
- data/templates/{main_aws_helper.rb.erb → lib/aws_helper/main.rb} +0 -0
- data/templates/{upload_stack.rb.erb → lib/aws_helper/upload_stack.rb} +15 -6
- data/templates/lib/core/applications.rb +479 -0
- data/templates/lib/core/assume_role.rb +40 -0
- data/templates/lib/core/classes.rb +25 -0
- data/templates/{core_compile.rb.erb → lib/core/compile.rb} +1 -0
- data/templates/lib/core/dependencies.rb +22 -0
- data/templates/{core_deploy.rb.erb → lib/core/deploy.rb} +20 -10
- data/templates/lib/core/git.rb +15 -0
- data/templates/lib/core/init.rb +173 -0
- data/templates/{core_upload.rb.erb → lib/core/upload.rb} +0 -0
- data/templates/{main.rb.erb → lib/main.rb} +8 -6
- data/templates/lib/shared_concerns/global_variables.rb +56 -0
- data/templates/{helper_methods.rb.erb → lib/shared_concerns/helper_functions.rb} +0 -0
- data/templates/lib/shared_concerns/helper_methods.rb +3 -0
- data/templates/{shared_methods.rb.erb → lib/shared_concerns/shared_methods.rb} +9 -0
- data/templates/lib/stacks/acm_stack/certificate_manager.rb +79 -0
- data/templates/{new_stack.rb.erb → lib/stacks/acm_stack/main.rb} +3 -4
- data/templates/lib/stacks/ecs_stack/ecs_cluster.rb +344 -0
- data/templates/lib/stacks/ecs_stack/lifecycle_hook.rb +188 -0
- data/templates/lib/stacks/ecs_stack/load_balancer.rb +68 -0
- data/templates/{ecs_stack.rb.erb → lib/stacks/ecs_stack/main.rb} +2 -1
- data/templates/{project_stack.rb.erb → lib/stacks/parent_stack/main.rb} +2 -2
- data/templates/lib/stacks/parent_stack/parent.rb +18 -0
- data/templates/lib/stacks/vpc_stack/infra_vpc.rb +193 -0
- data/templates/{vpc_stack.rb.erb → lib/stacks/vpc_stack/main.rb} +1 -2
- data/templates/{parent_stack_spec.rb.erb → spec/lib/parent_spec.rb} +2 -5
- data/templates/{spec_helper.rb.erb → spec/spec_helper.rb} +2 -2
- metadata +54 -44
- data/format.vim +0 -3
- data/templates/.env.erb +0 -4
- data/templates/.env.production.erb +0 -6
- data/templates/.env.rspec.erb +0 -6
- data/templates/.env.test.erb +0 -6
- data/templates/.gitlab-ci.yml.erb +0 -75
- data/templates/aws_sdk.rb.erb +0 -18
- data/templates/core_diff.rb.erb +0 -59
- data/templates/ecs_stack_concern.rb.erb +0 -20
- data/templates/global_variables.rb.erb +0 -16
- data/templates/helpers.rb.erb +0 -7
- data/templates/new_concern.rb.erb +0 -10
- data/templates/project_concern.rb.erb +0 -26
- data/templates/subnets.rb.erb +0 -18
- data/templates/vpc_concerns.rb.erb +0 -87
- 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
|
@@ -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
|
-
|
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
|
-
|
51
|
-
# stack_policy_url: "StackPolicyURL",
|
64
|
+
parameters: parent_parameters,
|
52
65
|
tags: [
|
53
66
|
{
|
54
67
|
key: "team",
|
55
|
-
value: "
|
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
|
-
|
68
|
-
# stack_policy_url: "StackPolicyURL",
|
79
|
+
parameters: parent_parameters,
|
69
80
|
tags: [
|
70
81
|
{
|
71
82
|
key: "team",
|
72
|
-
value: "
|
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
|