kumogata 0.4.17 → 0.4.18

Sign up to get free protection for your applications and to get access to all the features.
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'kumogata'
2
2
  require 'kumogata/argument_parser'
3
+ require 'kumogata/config_parser'
3
4
  require 'tempfile'
4
5
  require 'time'
5
6
  require 'timecop'
@@ -23,9 +24,9 @@ class Kumogata::Crypt
23
24
  end
24
25
  end
25
26
 
26
- def tempfile(content, template_ext)
27
+ def tempfile(content, template_ext = nil)
27
28
  basename = "#{File.basename __FILE__}.#{$$}"
28
- basename = [basename, template_ext]
29
+ basename = [basename, template_ext] if template_ext
29
30
 
30
31
  Tempfile.open(basename) do |f|
31
32
  f << content
@@ -0,0 +1,703 @@
1
+ {
2
+ "AWSTemplateFormatVersion": "2010-09-09",
3
+ "Description": "VPC knowhow template",
4
+ "Parameters": {
5
+ "KeyName": {
6
+ "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instances",
7
+ "Type": "String",
8
+ "MinLength": "1",
9
+ "MaxLength": "64",
10
+ "AllowedPattern": "[-_ a-zA-Z0-9]*",
11
+ "ConstraintDescription": "can contain only alphanumeric characters, spaces, dashes and underscores."
12
+ },
13
+ "SSHFrom" : {
14
+ "Description" : "Lockdown SSH access to the bastion host (default can be accessed from anywhere)",
15
+ "Type" : "String",
16
+ "MinLength": "9",
17
+ "MaxLength": "18",
18
+ "Default" : "0.0.0.0/0",
19
+ "AllowedPattern" : "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
20
+ "ConstraintDescription" : "must be a valid CIDR range of the form x.x.x.x/x."
21
+ },
22
+ "DBInstanceType" : {
23
+ "Description" : "EC2 instance type for the Blue environment",
24
+ "Default" : "db.t1.micro",
25
+ "Type" : "String"
26
+ },
27
+ "DBSnapshotName" : {
28
+ "Default" : "",
29
+ "Description" : "The name of a DB snapshot (optional)",
30
+ "Type" : "String"
31
+ },
32
+ "DBAllocatedStorage" : {
33
+ "Default" : "5",
34
+ "Description" : "DB instance disk size",
35
+ "Type" : "Number"
36
+ },
37
+ "DBUsername": {
38
+ "Default": "admin",
39
+ "Description" : "The database master account username",
40
+ "Type": "String",
41
+ "MinLength": "1",
42
+ "MaxLength": "16",
43
+ "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*",
44
+ "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters."
45
+ },
46
+ "DBPassword" : {
47
+ "Description" : "Password of RDS master password",
48
+ "Type" : "String",
49
+ "NoEcho": "true",
50
+ "MinLength": "4"
51
+ },
52
+ "DBName" : {
53
+ "Default" : "",
54
+ "Description" : "The name of a DB01 database",
55
+ "Type" : "String"
56
+ },
57
+ "WebInstanceType" : {
58
+ "Description" : "EC2 instance type for the web server",
59
+ "Default" : "t1.micro",
60
+ "Type" : "String"
61
+ },
62
+ "WebFleetSize" : {
63
+ "Description" : "Number of EC2 instances to launch for the web server",
64
+ "Default" : "2",
65
+ "Type" : "Number",
66
+ "MaxValue" : "100",
67
+ "MinValue" : "1"
68
+ },
69
+ "HostedZone" : {
70
+ "Description" : "The DNS name of an existing Amazon Route 53 hosted zone",
71
+ "Type" : "String"
72
+ }
73
+ },
74
+ "Conditions" : {
75
+ "UseDBSnapshot" : { "Fn::Not" : [ { "Fn::Equals" : [ { "Ref" : "DBSnapshotName" }, "" ] } ] }
76
+ },
77
+ "Mappings": {
78
+ "AWSAmazonLinuxAMI": {
79
+ "us-east-1": { "name":"Virginia", "201303": "ami-3275ee5b", "201309": "ami-35792c5c", "201403": "ami-2f726546" },
80
+ "us-west-2": { "name":"Oregon", "201303": "ami-ecbe2adc", "201309": "ami-d03ea1e0", "201403": "ami-b8f69f88" },
81
+ "us-west-1": { "name":"California", "201303": "ami-66d1fc23", "201309": "ami-687b4f2d", "201403": "ami-84f1cfc1" },
82
+ "eu-west-1": { "name":"Ireland", "201303": "ami-44939930", "201309": "ami-149f7863", "201403": "ami-a921dfde" },
83
+ "ap-southeast-1": { "name":"Singapole", "201303": "ami-aa9ed2f8", "201309": "ami-14f2b946", "201403": "ami-787c2c2a" },
84
+ "ap-southeast-2": { "name":"Sydney", "201303": "ami-363eaf0c", "201309": "ami-a148d59b", "201403": "ami-0bc85031" },
85
+ "ap-northeast-1": { "name":"Tokyo", "201303": "ami-173fbf16", "201309": "ami-3561fe34", "201403": "ami-a1bec3a0" },
86
+ "sa-east-1": { "name":"SaoPaulo", "201303": "ami-dd6bb0c0", "201309": "ami-9f6ec982", "201403": "ami-89de7c94" }
87
+ },
88
+ "ELBLogger": {
89
+ "us-east-1": { "AccountID": "127311923021" },
90
+ "us-west-2": { "AccountID": "797873946194" },
91
+ "us-west-1": { "AccountID": "027434742980" },
92
+ "eu-west-1": { "AccountID": "156460612806" },
93
+ "ap-southeast-1": { "AccountID": "114774131450" },
94
+ "ap-southeast-2": { "AccountID": "783225319266" },
95
+ "ap-northeast-1": { "AccountID": "582318560864" },
96
+ "sa-east-1": { "AccountID": "507241528517" },
97
+ "us-gov-west-1": { "AccountID": "048591011584" }
98
+ },
99
+ "StackConfig" : {
100
+ "VPC" : { "CIDR" : "10.0.0.0/16" },
101
+ "FrontendSubnet1" : { "CIDR" : "10.0.0.0/24" },
102
+ "FrontendSubnet2" : { "CIDR" : "10.0.1.0/24" },
103
+ "ApplicationSubnet1": { "CIDR" : "10.0.100.0/24" },
104
+ "ApplicationSubnet2": { "CIDR" : "10.0.101.0/24" },
105
+ "DatastoreSubnet1" : { "CIDR" : "10.0.200.0/24" },
106
+ "DatastoreSubnet2" : { "CIDR" : "10.0.201.0/24" },
107
+ "BastionServer" : { "InstanceType" : "t1.micro" }
108
+ }
109
+ },
110
+ "Resources": {
111
+ "PowerUserRole" : {
112
+ "Type" : "AWS::IAM::Role",
113
+ "Properties" : {
114
+ "AssumeRolePolicyDocument" : {
115
+ "Statement": [ {
116
+ "Effect": "Allow",
117
+ "Principal": {
118
+ "Service": [ "ec2.amazonaws.com" ]
119
+ },
120
+ "Action": [ "sts:AssumeRole" ]
121
+ } ]
122
+ },
123
+ "Path" : "/",
124
+ "Policies" :[ {
125
+ "PolicyName" : "PowerUserPolicy",
126
+ "PolicyDocument" : {
127
+ "Statement": [ {
128
+ "Sid": "PowerUserStmt",
129
+ "Effect": "Allow",
130
+ "NotAction": "iam:*",
131
+ "Resource": "*"
132
+ } ]
133
+ }
134
+ }]
135
+ }
136
+ },
137
+ "PowerUserProfile" : {
138
+ "Type" : "AWS::IAM::InstanceProfile",
139
+ "Properties" : {
140
+ "Path": "/",
141
+ "Roles" : [ { "Ref" : "PowerUserRole" } ]
142
+ }
143
+ },
144
+
145
+ "LogBucket" : {
146
+ "Type" : "AWS::S3::Bucket",
147
+ "DeletionPolicy" : "Retain"
148
+ },
149
+ "LogBucketPolicy" : {
150
+ "Type" : "AWS::S3::BucketPolicy",
151
+ "Properties" : {
152
+ "Bucket" : { "Ref" : "LogBucket" },
153
+ "PolicyDocument" : {
154
+ "Id" : "LogBucketPolicy",
155
+ "Statement" : [{
156
+ "Sid" : "WriteAccess",
157
+ "Action" : [ "s3:PutObject" ],
158
+ "Effect" : "Allow",
159
+ "Resource" : { "Fn::Join" : [ "", [
160
+ "arn:aws:s3:::", { "Ref" : "LogBucket" } , "/AWSLogs/", { "Ref" : "AWS::AccountId" }, "/*"
161
+ ]]},
162
+ "Principal" : {
163
+ "AWS" : { "Fn::FindInMap" : [ "ELBLogger", { "Ref": "AWS::Region" }, "AccountID" ]}
164
+ }
165
+ }]
166
+ }
167
+ }
168
+ },
169
+
170
+ "VPC" : {
171
+ "Type" : "AWS::EC2::VPC",
172
+ "Properties" : {
173
+ "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]},
174
+ "EnableDnsSupport" : "true",
175
+ "EnableDnsHostnames" : "true",
176
+ "InstanceTenancy" : "default",
177
+ "Tags" : [
178
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } },
179
+ {"Key" : "Network", "Value" : "Public" }
180
+ ]
181
+ }
182
+ },
183
+ "InternetGateway" : {
184
+ "Type" : "AWS::EC2::InternetGateway",
185
+ "Properties" : {
186
+ "Tags" : [
187
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } },
188
+ {"Key" : "Network", "Value" : "Public" }
189
+ ]
190
+ }
191
+ },
192
+ "AttachGateway" : {
193
+ "Type" : "AWS::EC2::VPCGatewayAttachment",
194
+ "Properties" : {
195
+ "VpcId" : {"Ref" : "VPC"},
196
+ "InternetGatewayId" : {"Ref" : "InternetGateway"}
197
+ }
198
+ },
199
+
200
+ "PublicRouteTable" : {
201
+ "Type" : "AWS::EC2::RouteTable",
202
+ "DependsOn" : "AttachGateway",
203
+ "Properties" : {
204
+ "VpcId" : { "Ref" : "VPC" },
205
+ "Tags" : [
206
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
207
+ {"Key" : "Network", "Value" : "Public" }
208
+ ]
209
+ }
210
+ },
211
+ "PrivateRouteTable" : {
212
+ "Type" : "AWS::EC2::RouteTable",
213
+ "DependsOn" : "AttachGateway",
214
+ "Properties" : {
215
+ "VpcId" : { "Ref" : "VPC" },
216
+ "Tags" : [
217
+ { "Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } },
218
+ { "Key" : "Network", "Value" : "Private" }
219
+ ]
220
+ }
221
+ },
222
+ "PublicRoute" : {
223
+ "Type" : "AWS::EC2::Route",
224
+ "DependsOn" : "AttachGateway",
225
+ "Properties" : {
226
+ "RouteTableId" : { "Ref" : "PublicRouteTable" },
227
+ "DestinationCidrBlock" : "0.0.0.0/0",
228
+ "GatewayId" : { "Ref" : "InternetGateway" }
229
+ }
230
+ },
231
+
232
+ "FrontendSubnet1": {
233
+ "Type": "AWS::EC2::Subnet",
234
+ "DependsOn" : "AttachGateway",
235
+ "Properties" : {
236
+ "VpcId": { "Ref": "VPC" },
237
+ "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
238
+ "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "FrontendSubnet1", "CIDR" ]},
239
+ "Tags" : [
240
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
241
+ {"Key" : "Network", "Value" : "Public" }
242
+ ]
243
+ }
244
+ },
245
+ "FrontendSubnet2": {
246
+ "Type": "AWS::EC2::Subnet",
247
+ "DependsOn" : "AttachGateway",
248
+ "Properties": {
249
+ "VpcId": { "Ref": "VPC" },
250
+ "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
251
+ "CidrBlock": { "Fn::FindInMap" : [ "StackConfig", "FrontendSubnet2", "CIDR" ]},
252
+ "Tags" : [
253
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
254
+ {"Key" : "Network", "Value" : "Public" }
255
+ ]
256
+ }
257
+ },
258
+ "ApplicationSubnet1" : {
259
+ "Type" : "AWS::EC2::Subnet",
260
+ "DependsOn" : "AttachGateway",
261
+ "Properties" : {
262
+ "VpcId" : {"Ref" : "VPC"},
263
+ "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "ApplicationSubnet1", "CIDR" ]},
264
+ "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
265
+ "Tags" : [
266
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
267
+ {"Key" : "Network", "Value" : "Public" }
268
+ ]
269
+ }
270
+ },
271
+ "ApplicationSubnet2" : {
272
+ "Type" : "AWS::EC2::Subnet",
273
+ "DependsOn" : "AttachGateway",
274
+ "Properties" : {
275
+ "VpcId" : {"Ref" : "VPC"},
276
+ "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "ApplicationSubnet2", "CIDR" ]},
277
+ "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
278
+ "Tags" : [
279
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId"} },
280
+ {"Key" : "Network", "Value" : "Public" }
281
+ ]
282
+ }
283
+ },
284
+ "DatastoreSubnet1" : {
285
+ "Type" : "AWS::EC2::Subnet",
286
+ "DependsOn" : "AttachGateway",
287
+ "Properties" : {
288
+ "VpcId" : { "Ref" : "VPC" },
289
+ "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "DatastoreSubnet1", "CIDR" ]},
290
+ "AvailabilityZone" : { "Fn::Select" : [ "0", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
291
+ "Tags" : [
292
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } },
293
+ {"Key" : "Network", "Value" : "Private" }
294
+ ]
295
+ }
296
+ },
297
+ "DatastoreSubnet2" : {
298
+ "Type" : "AWS::EC2::Subnet",
299
+ "DependsOn" : "AttachGateway",
300
+ "Properties" : {
301
+ "VpcId" : { "Ref" : "VPC" },
302
+ "CidrBlock" : { "Fn::FindInMap" : [ "StackConfig", "DatastoreSubnet2", "CIDR" ]},
303
+ "AvailabilityZone" : { "Fn::Select" : [ "1", { "Fn::GetAZs" : { "Ref" : "AWS::Region" }}]},
304
+ "Tags" : [
305
+ {"Key" : "Application", "Value" : { "Ref" : "AWS::StackId" } },
306
+ {"Key" : "Network", "Value" : "Private" }
307
+ ]
308
+ }
309
+ },
310
+
311
+ "FrontendSubnet1RouteTableAssociation" : {
312
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
313
+ "Properties" : {
314
+ "SubnetId" : { "Ref" : "FrontendSubnet1" },
315
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
316
+ }
317
+ },
318
+ "FrontendSubnet2RouteTableAssociation" : {
319
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
320
+ "Properties" : {
321
+ "SubnetId" : { "Ref" : "FrontendSubnet2" },
322
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
323
+ }
324
+ },
325
+ "ApplicationSubnet1RouteTableAssociation" : {
326
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
327
+ "Properties" : {
328
+ "SubnetId" : { "Ref" : "ApplicationSubnet1" },
329
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
330
+ }
331
+ },
332
+ "ApplicationSubnet2RouteTableAssociation" : {
333
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
334
+ "Properties" : {
335
+ "SubnetId" : { "Ref" : "ApplicationSubnet2" },
336
+ "RouteTableId" : { "Ref" : "PublicRouteTable" }
337
+ }
338
+ },
339
+ "DatastoreSubnet1RouteTableAssociation" : {
340
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
341
+ "Properties" : {
342
+ "SubnetId" : { "Ref" : "DatastoreSubnet1" },
343
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" }
344
+ }
345
+ },
346
+ "DatastoreSubnet2RouteTableAssociation" : {
347
+ "Type" : "AWS::EC2::SubnetRouteTableAssociation",
348
+ "Properties" : {
349
+ "SubnetId" : { "Ref" : "DatastoreSubnet2" },
350
+ "RouteTableId" : { "Ref" : "PrivateRouteTable" }
351
+ }
352
+ },
353
+
354
+
355
+ "VPCDefaultSecurityGroup" : {
356
+ "Type" : "AWS::EC2::SecurityGroup",
357
+ "Properties" : {
358
+ "VpcId" : { "Ref" : "VPC" },
359
+ "GroupDescription" : "Allow all communications in VPC",
360
+ "SecurityGroupIngress" : [
361
+ { "IpProtocol" : "tcp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} },
362
+ { "IpProtocol" : "udp", "FromPort" : "0", "ToPort" : "65535", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} },
363
+ { "IpProtocol" : "icmp", "FromPort" : "-1", "ToPort" : "-1", "CidrIp" : { "Fn::FindInMap" : [ "StackConfig", "VPC", "CIDR" ]} }
364
+ ]
365
+ }
366
+ },
367
+ "SSHSecurityGroup" : {
368
+ "Type" : "AWS::EC2::SecurityGroup",
369
+ "Properties" : {
370
+ "VpcId" : { "Ref" : "VPC" },
371
+ "GroupDescription" : "Enable SSH access via port 22",
372
+ "SecurityGroupIngress" : [
373
+ { "IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHFrom" }}
374
+ ]
375
+ }
376
+ },
377
+ "PublicWebSecurityGroup" : {
378
+ "Type" : "AWS::EC2::SecurityGroup",
379
+ "Properties" : {
380
+ "VpcId" : { "Ref" : "VPC" },
381
+ "GroupDescription" : "Public Security Group with HTTP access on port 443 from the internet",
382
+ "SecurityGroupIngress" : [
383
+ { "IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0" },
384
+ { "IpProtocol" : "tcp", "FromPort" : "443", "ToPort" : "443", "CidrIp" : "0.0.0.0/0" }
385
+ ]
386
+ }
387
+ },
388
+
389
+ "ApplicationSecurityGroup" : {
390
+ "Type" : "AWS::EC2::SecurityGroup",
391
+ "Properties" : {
392
+ "VpcId" : {"Ref" : "VPC"},
393
+ "GroupDescription" : "Marker security group for Application server."
394
+ }
395
+ },
396
+
397
+ "MySQLSecurityGroup" : {
398
+ "Type" : "AWS::EC2::SecurityGroup",
399
+ "Properties" : {
400
+ "VpcId" : {"Ref" : "VPC"},
401
+ "GroupDescription" : "Marker security group for MySQL server."
402
+ }
403
+ },
404
+
405
+ "BastionWaitHandle" : {
406
+ "Type" : "AWS::CloudFormation::WaitConditionHandle"
407
+ },
408
+ "BastionWaitCondition" : {
409
+ "Type" : "AWS::CloudFormation::WaitCondition",
410
+ "DependsOn" : "BastionInstance",
411
+ "Properties" : {
412
+ "Handle" : { "Ref" : "BastionWaitHandle" },
413
+ "Timeout" : "900"
414
+ }
415
+ },
416
+ "BastionInstance": {
417
+ "Type": "AWS::EC2::Instance",
418
+ "Properties": {
419
+ "InstanceType": { "Fn::FindInMap" : [ "StackConfig", "BastionServer", "InstanceType" ]},
420
+ "KeyName": { "Ref": "KeyName" },
421
+ "SubnetId": { "Ref" : "FrontendSubnet1" },
422
+ "ImageId": { "Fn::FindInMap": [ "AWSAmazonLinuxAMI", { "Ref": "AWS::Region" }, "201403" ]},
423
+ "IamInstanceProfile": { "Ref" : "PowerUserProfile" },
424
+ "SecurityGroupIds" : [
425
+ { "Ref" : "SSHSecurityGroup" },
426
+ { "Ref" : "VPCDefaultSecurityGroup" }
427
+ ],
428
+ "Tags": [
429
+ { "Key": "Name", "Value": "Bastion" }
430
+ ],
431
+ "UserData" : { "Fn::Base64" : { "Fn::Join" : [ "", [
432
+ "#! /bin/bash -v\n",
433
+ "yum update -y\n",
434
+
435
+ "# Helper function\n",
436
+ "function error_exit\n",
437
+ "{\n",
438
+ " /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "BastionWaitHandle" }, "'\n",
439
+ " exit 1\n",
440
+ "}\n",
441
+
442
+ "# Install packages\n",
443
+ "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r BastionInstance ",
444
+ " --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",
445
+
446
+ "# All is well so signal success\n",
447
+ "/opt/aws/bin/cfn-signal -e $? -r \"BastionInstance setup complete\" '", { "Ref" : "BastionWaitHandle" }, "'\n"
448
+ ]]}}
449
+ },
450
+ "Metadata" : {
451
+ "AWS::CloudFormation::Init" : {
452
+ "config" : {
453
+ "packages" : {
454
+ "yum" : {
455
+ "mysql55" : [],
456
+ "jq" : [],
457
+ "python-magic" : []
458
+ }
459
+ }
460
+ }
461
+ }
462
+ }
463
+ },
464
+ "BastionInstanceEIP": {
465
+ "Type": "AWS::EC2::EIP",
466
+ "DependsOn" : "AttachGateway",
467
+ "Properties": {
468
+ "Domain": "vpc",
469
+ "InstanceId": { "Ref": "BastionInstance" }
470
+ }
471
+ },
472
+ "BastionDNSRecord" : {
473
+ "Type" : "AWS::Route53::RecordSet",
474
+ "Properties" : {
475
+ "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]},
476
+ "Comment" : "A record for the Bastion instance.",
477
+ "Name" : { "Fn::Join" : [ "", ["bastion.", {"Ref" : "HostedZone"}, "." ]]},
478
+ "Type" : "A",
479
+ "TTL" : "300",
480
+ "ResourceRecords" : [
481
+ {"Ref" :"BastionInstanceEIP"}
482
+ ]
483
+ }
484
+ },
485
+ "BastionLocalDNSRecord" : {
486
+ "Type" : "AWS::Route53::RecordSet",
487
+ "Properties" : {
488
+ "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]},
489
+ "Comment" : "A record for the private IP address of Bastion instance.",
490
+ "Name" : { "Fn::Join" : [ "", ["bastion.local.", {"Ref" : "HostedZone"}, "." ]]},
491
+ "Type" : "A",
492
+ "TTL" : "300",
493
+ "ResourceRecords" : [
494
+ { "Fn::GetAtt" : [ "BastionInstance", "PrivateIp" ] }
495
+ ]
496
+ }
497
+ },
498
+
499
+ "DBParamGroup" : {
500
+ "Type": "AWS::RDS::DBParameterGroup",
501
+ "Properties" : {
502
+ "Description" : "Default parameter group for Portnoy",
503
+ "Family" : "MySQL5.6",
504
+ "Parameters" : {
505
+ "character_set_database" : "utf8mb4",
506
+ "character_set_client" : "utf8mb4",
507
+ "character_set_connection" : "utf8mb4",
508
+ "character_set_results" : "utf8mb4",
509
+ "character_set_server" : "utf8mb4",
510
+ "skip-character-set-client-handshake" : "TRUE"
511
+ }
512
+ }
513
+ },
514
+ "DBSubnetGroup" : {
515
+ "Type" : "AWS::RDS::DBSubnetGroup",
516
+ "Properties" : {
517
+ "DBSubnetGroupDescription" : "Database subnets for RDS",
518
+ "SubnetIds" : [
519
+ { "Ref": "DatastoreSubnet1" },
520
+ { "Ref": "DatastoreSubnet2" }
521
+ ]
522
+ }
523
+ },
524
+ "DBInstance" : {
525
+ "Type" : "AWS::RDS::DBInstance",
526
+ "DeletionPolicy" : "Snapshot",
527
+ "Properties" : {
528
+ "DBInstanceClass" : { "Ref" : "DBInstanceType" },
529
+ "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" },
530
+ "Engine" : "MySQL",
531
+ "MultiAZ" : "true",
532
+ "EngineVersion" : "5.6.13",
533
+ "MasterUsername" : {"Ref":"DBUsername"},
534
+ "MasterUserPassword" : {"Ref":"DBPassword"},
535
+ "BackupRetentionPeriod" : "35",
536
+ "DBParameterGroupName" : {"Ref":"DBParamGroup"},
537
+ "DBSubnetGroupName" : {"Ref":"DBSubnetGroup"},
538
+ "DBSnapshotIdentifier" : { "Fn::If" : [
539
+ "UseDBSnapshot",
540
+ { "Ref" : "DBSnapshotName" },
541
+ { "Ref" : "AWS::NoValue" }
542
+ ]},
543
+ "PreferredBackupWindow": "19:00-19:30",
544
+ "PreferredMaintenanceWindow": "sat:20:00-sat:20:30",
545
+ "VPCSecurityGroups" : [
546
+ { "Ref" : "VPCDefaultSecurityGroup" },
547
+ { "Ref" : "MySQLSecurityGroup" }
548
+ ]
549
+ }
550
+ },
551
+ "DatabaseDNSRecord" : {
552
+ "Type" : "AWS::Route53::RecordSet",
553
+ "Properties" : {
554
+ "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]},
555
+ "Comment" : "CNAME for the database.",
556
+ "Name" : { "Fn::Join" : [ "", [ "db.local.", { "Ref" : "HostedZone" }, "." ]]},
557
+ "Type" : "CNAME",
558
+ "TTL" : "300",
559
+ "ResourceRecords" : [
560
+ { "Fn::GetAtt" : [ "DBInstance", "Endpoint.Address" ] }
561
+ ]
562
+ }
563
+ },
564
+
565
+ "ApplicationFleet" : {
566
+ "Type" : "AWS::AutoScaling::AutoScalingGroup",
567
+ "UpdatePolicy" : {
568
+ "AutoScalingRollingUpdate" : {
569
+ "MaxBatchSize" : "1",
570
+ "MinInstancesInService" : "1",
571
+ "PauseTime" : "PT2M30S"
572
+ }
573
+ },
574
+ "Properties" : {
575
+ "AvailabilityZones" : [
576
+ { "Fn::GetAtt" : [ "ApplicationSubnet1", "AvailabilityZone" ] },
577
+ { "Fn::GetAtt" : [ "ApplicationSubnet2", "AvailabilityZone" ] }
578
+ ],
579
+ "VPCZoneIdentifier" : [
580
+ { "Ref" : "ApplicationSubnet1" },
581
+ { "Ref" : "ApplicationSubnet2" }
582
+ ],
583
+ "LaunchConfigurationName" : { "Ref" : "ApplicationServerLaunchConfig" },
584
+ "MinSize" : { "Ref" : "WebFleetSize" },
585
+ "MaxSize" : { "Ref" : "WebFleetSize" },
586
+ "DesiredCapacity" : { "Ref" : "WebFleetSize" },
587
+ "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ],
588
+ "Tags" : [
589
+ { "Key" : "Name", "Value" : "Application", "PropagateAtLaunch" : "true" }
590
+ ]
591
+ }
592
+ },
593
+ "ApplicationServerLaunchConfig" : {
594
+ "Type" : "AWS::AutoScaling::LaunchConfiguration",
595
+ "Properties" : {
596
+ "InstanceType": { "Ref" : "WebInstanceType" },
597
+ "KeyName": { "Ref" : "KeyName" },
598
+ "ImageId": { "Fn::FindInMap": [ "AWSAmazonLinuxAMI", { "Ref": "AWS::Region" }, "201403" ]},
599
+ "SecurityGroups": [
600
+ { "Ref" : "VPCDefaultSecurityGroup" },
601
+ { "Ref" : "ApplicationSecurityGroup" }
602
+ ],
603
+ "AssociatePublicIpAddress" : "true",
604
+ "IamInstanceProfile": { "Ref" : "PowerUserProfile" },
605
+ "InstanceMonitoring" : "false",
606
+ "UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
607
+ "#! /bin/bash -v\n",
608
+ "yum update -y\n",
609
+
610
+ "# Install packages\n",
611
+ "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r ApplicationServerLaunchConfig ",
612
+ " --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n"
613
+ ]]}}
614
+ },
615
+ "Metadata" : {
616
+ "AWS::CloudFormation::Init" : {
617
+ "config" : {
618
+ "packages" : {
619
+ "yum" : {
620
+ "httpd" : [],
621
+ "mysql55" : []
622
+ }
623
+ },
624
+ "files" : {
625
+ "/var/www/html/index.html" : {
626
+ "content" : "<html><head><title>Hello</title></head><body>Hello, world!</body></html>",
627
+ "mode" : "000644",
628
+ "owner" : "apache",
629
+ "group" : "apache"
630
+ }
631
+ },
632
+ "services" : {
633
+ "sysvinit" : {
634
+ "httpd" : { "enabled" : "true", "ensureRunning" : "true" }
635
+ }
636
+ }
637
+ }
638
+ }
639
+ }
640
+ },
641
+
642
+ "ElasticLoadBalancer" : {
643
+ "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
644
+ "DependsOn" : "AttachGateway",
645
+ "Properties" : {
646
+ "Subnets" : [
647
+ { "Ref" : "FrontendSubnet1" },
648
+ { "Ref" : "FrontendSubnet2" }
649
+ ],
650
+ "Listeners" : [
651
+ { "LoadBalancerPort" : "80", "InstancePort" : "80", "Protocol" : "HTTP" }
652
+ ],
653
+ "HealthCheck" : {
654
+ "Target" : "HTTP:80/index.html",
655
+ "HealthyThreshold" : "2",
656
+ "UnhealthyThreshold" : "2",
657
+ "Interval" : "6",
658
+ "Timeout" : "5"
659
+ },
660
+ "SecurityGroups" : [
661
+ { "Ref" : "PublicWebSecurityGroup" }
662
+ ]
663
+ }
664
+ },
665
+ "LoadBalancerDNSRecord" : {
666
+ "Type" : "AWS::Route53::RecordSetGroup",
667
+ "Properties" : {
668
+ "HostedZoneName" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]},
669
+ "Comment" : "Zone apex alias targeted to LoadBalancer.",
670
+ "RecordSets" : [
671
+ {
672
+ "Name" : { "Fn::Join" : [ "", [{"Ref" : "HostedZone"}, "." ]]},
673
+ "Type" : "A",
674
+ "AliasTarget" : {
675
+ "HostedZoneId" : { "Fn::GetAtt" : ["ElasticLoadBalancer", "CanonicalHostedZoneNameID"] },
676
+ "DNSName" : { "Fn::GetAtt" : ["ElasticLoadBalancer","CanonicalHostedZoneName"] }
677
+ }
678
+ }
679
+ ]
680
+ }
681
+ }
682
+ },
683
+
684
+ "Outputs": {
685
+ "JdbcConnectionString": {
686
+ "Value": { "Fn::Join": [ "", [
687
+ "jdbc:mysql://",
688
+ { "Ref": "DatabaseDNSRecord" }, ":",
689
+ { "Fn::GetAtt": [ "DBInstance", "Endpoint.Port" ] }, "/",
690
+ { "Ref" : "DBName" }
691
+ ]]},
692
+ "Description": "-"
693
+ },
694
+ "SSHToBackendServer": {
695
+ "Value": { "Fn::Join":["", [
696
+ "ssh -i /path/to/", { "Ref": "KeyName" }, ".pem",
697
+ " -oProxyCommand='ssh -i /path/to/", { "Ref": "KeyName" }, ".pem -W %h:%p ec2-user@", {"Ref" : "BastionDNSRecord"}, "'",
698
+ " ec2-user@<private-ip>"
699
+ ]]},
700
+ "Description": "SSH command to connect to the backend servers"
701
+ }
702
+ }
703
+ }