kumogata 0.4.17 → 0.4.18

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5f0d6857d6a041d6536eea743a194b8afdca4af
4
- data.tar.gz: 6df7c7b58ea4c37a5c000ff702e3d4055ed74555
3
+ metadata.gz: 2c52814103c4dfd004239aeb9cd54b86323b8d1a
4
+ data.tar.gz: bc647f27a50468920365f9b6ddb18975668ebe37
5
5
  SHA512:
6
- metadata.gz: 0c117ae2daefe0e134745cd7d6e1a4f4ede6e3fb557c25407d5756f4383837596d5b67c96c57b88597579f94eadf31f58a105639771d3b6de5385d100beb0b2b
7
- data.tar.gz: b3dff3cfdf600b6e0e76d4e153da977138acf0e0c05faf17e856e675e071a57d03efb121897e4bc3dff0dcbff9ce6eec2bded483e521fb1272cc87df7a4b4079
6
+ metadata.gz: 12b445111efc2f36caf2ca79f1ec7aa0e6dcb37921188882124fe0674400a7552b0832fdb663505a24eafb3d91cccdd774a34836550f69390438e17b11057a8e
7
+ data.tar.gz: c547e1adc47c4e4e719fe3f10997b031ac41576c92ca46322b09badd890d836e0da6b9887083050be32e13fc8f9cbf1d6ef84d795ea51e7d7d79ce6214fb1625
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script:
5
+ - bundle install
6
+ - bundle exec rake
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
1
  # Kumogata
2
2
 
3
- ![Stack is not required!](http://serif.hatelabo.jp/images/cache/a69b56ca6985b6ee7e605a102445953f355f54f5/62192c286da4377e608f3f5a5200f1bec272a92e.gif)
3
+ Kumogata is a tool for [AWS CloudFormation](https://aws.amazon.com/cloudformation/).
4
4
 
5
+ This is a `format converter` + `useful tool`.
6
+ It supports the following format:
5
7
 
6
- Kumogata is a tool for [AWS CloudFormation](https://aws.amazon.com/cloudformation/).
8
+ * JSON
9
+ * Ruby
10
+ * YAML
11
+ * JavaScript
7
12
 
8
13
  [![Gem Version](https://badge.fury.io/rb/kumogata.png?201406152020)](http://badge.fury.io/rb/kumogata)
9
- [![Build Status](https://drone.io/github.com/winebarrel/kumogata/status.png?201406152020)](https://drone.io/github.com/winebarrel/kumogata/latest)
14
+ [![Build Status](https://travis-ci.org/winebarrel/kumogata.svg?branch=master)](https://travis-ci.org/winebarrel/kumogata)
10
15
 
11
16
  It can define a template in Ruby DSL, such as:
12
17
 
@@ -132,8 +137,6 @@ If you want to pass parameters, please use `-p` option:
132
137
  **The stack will be delete if you do not specify the stack name explicitly.**
133
138
  (And only the resources will remain)
134
139
 
135
- I think the stack that manage resources is not required in many case...
136
-
137
140
  ### Convert JSON to Ruby
138
141
 
139
142
  JSON template can be converted to Ruby template.
@@ -367,6 +370,67 @@ Status: 0
367
370
  (Save to `/foo/bar/command_result.json`)
368
371
  ```
369
372
 
373
+ ## JavaScript template
374
+
375
+ You can also use the YAML template instead of JSON and Ruby.
376
+
377
+ ```javascript
378
+ function fetch_ami() {
379
+ return "ami-XXXXXXXX";
380
+ }
381
+
382
+ /* For JS Object is evaluated last, it must be enclosed in parentheses */
383
+ ({
384
+ Resources: { /* comment */
385
+ myEC2Instance: {
386
+ Type: "AWS::EC2::Instance",
387
+ Properties: {
388
+ ImageId: fetch_ami(),
389
+ InstanceType: "t1.micro"
390
+ }
391
+ }
392
+ },
393
+ Outputs: {
394
+ AZ: { /* comment */
395
+ Value: {
396
+ "Fn::GetAtt": [
397
+ "myEC2Instance",
398
+ "AvailabilityZone"
399
+ ]
400
+ }
401
+ }
402
+ }
403
+ })
404
+
405
+ /*
406
+ {
407
+ "Resources": {
408
+ "myEC2Instance": {
409
+ "Type": "AWS::EC2::Instance",
410
+ "Properties": {
411
+ "ImageId": "ami-XXXXXXXX",
412
+ "InstanceType": "t1.micro"
413
+ }
414
+ }
415
+ },
416
+ "Outputs": {
417
+ "AZ": {
418
+ "Value": {
419
+ "Fn::GetAtt": [
420
+ "myEC2Instance",
421
+ "AvailabilityZone"
422
+ ]
423
+ }
424
+ }
425
+ }
426
+ }
427
+ */
428
+ ```
429
+
430
+ ### Convert JSON template to JavaScript
431
+
432
+ $ kumogata convert Drupal_Single_Instance.template --output-format=js
433
+
370
434
  ## YAML template
371
435
 
372
436
  You can also use the YAML template instead of JSON and Ruby.
data/bin/kumogata CHANGED
@@ -3,6 +3,7 @@ $: << File.expand_path("#{File.dirname __FILE__}/../lib")
3
3
  require 'rubygems'
4
4
  require 'kumogata'
5
5
  require 'kumogata/argument_parser'
6
+ require 'kumogata/config_parser'
6
7
 
7
8
  options = nil
8
9
 
@@ -21,6 +22,7 @@ begin
21
22
  Diffy::Diff.default_format = options.color? ? :color : :text
22
23
 
23
24
  aws_opts = {}
25
+ config_parser = Kumogata::ConfigParser.new
24
26
 
25
27
  # Set config file path
26
28
  if options.config_path?
@@ -28,23 +30,29 @@ begin
28
30
  raise "No such config file: #{options.config_path}"
29
31
  end
30
32
 
31
- AWSConfig.config_file = options.config_path
33
+ config_parser.path = options.config_path
32
34
  end
33
35
 
34
36
  # Load .aws/config
35
37
  # See http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html
36
- if File.exist?(AWSConfig.config_file)
37
- unless AWSConfig.has_profile?(options.config_profile)
38
- raise "No profile: #{AWSConfig.config_file} => #{options.config_profile}"
38
+ if config_parser.path.exist?
39
+ config_parser.parse!
40
+
41
+ if options.config_profile and not config_parser[options.config_profile]
42
+ raise "No profile: #{config_parser.path} => #{options.config_profile}"
39
43
  end
40
44
 
41
- {
42
- 'aws_access_key_id' => :access_key_id,
43
- 'aws_secret_access_key' => :secret_access_key,
44
- 'region' => :region,
45
- }.each do |config_key, option_key|
46
- option_value = AWSConfig[options.config_profile][config_key]
47
- aws_opts[option_key] = option_value if option_value
45
+ profile_name = options.config_profile || :default
46
+
47
+ if (profile = config_parser[profile_name])
48
+ {
49
+ 'aws_access_key_id' => :access_key_id,
50
+ 'aws_secret_access_key' => :secret_access_key,
51
+ 'region' => :region,
52
+ }.each do |config_key, option_key|
53
+ option_value = profile[config_key]
54
+ aws_opts[option_key] = option_value if option_value
55
+ end
48
56
  end
49
57
  end
50
58
 
data/kumogata.gemspec CHANGED
@@ -19,16 +19,16 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.add_dependency 'aws-sdk'
22
- spec.add_dependency 'aws_config', '>= 0.0.2'
23
22
  spec.add_dependency 'coderay'
24
23
  spec.add_dependency 'diffy'
25
- spec.add_dependency 'dslh', '>= 0.2.6'
24
+ spec.add_dependency 'dslh', '>= 0.2.7'
26
25
  spec.add_dependency 'hashie'
27
26
  spec.add_dependency 'highline'
28
27
  spec.add_dependency 'json'
29
28
  spec.add_dependency 'net-ssh'
30
29
  spec.add_dependency 'retryable'
31
30
  spec.add_dependency 'term-ansicolor'
31
+ spec.add_dependency 'therubyracer'
32
32
  spec.add_dependency 'uuidtools'
33
33
  spec.add_development_dependency 'bundler'
34
34
  spec.add_development_dependency 'rake'
data/lib/kumogata.rb CHANGED
@@ -2,7 +2,6 @@ module Kumogata; end
2
2
  require 'kumogata/version'
3
3
 
4
4
  require 'aws-sdk'
5
- require 'aws_config'
6
5
  require 'base64'
7
6
  require 'coderay'
8
7
  require 'diffy'
@@ -15,6 +14,7 @@ require 'net/ssh'
15
14
  require 'open-uri'
16
15
  require 'open3'
17
16
  require 'optparse'
17
+ require 'pathname'
18
18
  require 'rbconfig'
19
19
  require 'retryable'
20
20
  require 'set'
@@ -23,6 +23,7 @@ require 'strscan'
23
23
  require 'term/ansicolor'
24
24
  require 'thread'
25
25
  require 'uuidtools'
26
+ require 'v8'
26
27
  require 'yaml'
27
28
 
28
29
  require 'kumogata/client'
@@ -35,3 +36,4 @@ require 'kumogata/outputs_filter'
35
36
  require 'kumogata/post_processing'
36
37
  require 'kumogata/string_stream'
37
38
  require 'kumogata/utils'
39
+ require 'kumogata/v8_object_ext'
@@ -8,7 +8,6 @@ class Kumogata::ArgumentParser
8
8
  :command_result_log => File.join(Dir.pwd, 'command_result.json'),
9
9
  :color => $stdout.tty?,
10
10
  :debug => false,
11
- :config_profile => :default,
12
11
  }
13
12
 
14
13
  COMMANDS = {
@@ -81,7 +80,7 @@ class Kumogata::ArgumentParser
81
80
  update_usage(opt)
82
81
 
83
82
  begin
84
- supported_formats = [:ruby, :json, :yaml]
83
+ supported_formats = [:ruby, :json, :yaml, :js]
85
84
  opt.on('-k', '--access-key ACCESS_KEY') {|v| options[:access_key_id] = v }
86
85
  opt.on('-s', '--secret-key SECRET_KEY') {|v| options[:secret_access_key] = v }
87
86
  opt.on('-r', '--region REGION') {|v| options[:region] = v }
@@ -40,6 +40,7 @@ class Kumogata::Client
40
40
  when :ruby then :json
41
41
  when :json then :ruby
42
42
  when :yaml then :json
43
+ when :js then :json
43
44
  end
44
45
  end
45
46
 
@@ -50,6 +51,8 @@ class Kumogata::Client
50
51
  JSON.pretty_generate(template).colorize_as(:json)
51
52
  when :yaml
52
53
  YAML.dump(template).colorize_as(:yaml)
54
+ when :js
55
+ '(' + JSON.pretty_generate(template).colorize_as(:json) + ')'
53
56
  end
54
57
  end
55
58
 
@@ -163,6 +166,14 @@ class Kumogata::Client
163
166
  when :yaml
164
167
  parsed = YAML.load(f.read)
165
168
  Kumogata::Utils.stringify(parsed)
169
+ when :js
170
+ obj = V8::Context.new.eval(f.read)
171
+
172
+ unless obj.instance_of?(V8::Object)
173
+ raise "Invalid JavaScript template. Please return Object: #{path_or_url}"
174
+ end
175
+
176
+ Kumogata::Utils.stringify(obj.to_hash)
166
177
  else
167
178
  raise "Unknown format: #{format}"
168
179
  end
@@ -179,10 +190,12 @@ class Kumogata::Client
179
190
  case File.extname(path_or_url)
180
191
  when '.rb'
181
192
  :ruby
182
- when '.json', '.js'
193
+ when '.json'
183
194
  :json
184
195
  when '.yml', '.yaml'
185
196
  :yaml
197
+ when '.js'
198
+ :js
186
199
  else
187
200
  :json
188
201
  end
@@ -0,0 +1,33 @@
1
+ class Kumogata::ConfigParser
2
+ attr_reader :path
3
+
4
+ def initialize(path = '~/.aws/config')
5
+ self.path = path
6
+ @profiles = {}
7
+ end
8
+
9
+ def path=(v)
10
+ @path = Pathname.new(v).expand_path
11
+ end
12
+
13
+ def [](profile_name)
14
+ @profiles[profile_name.to_s]
15
+ end
16
+
17
+ def parse!
18
+ profile_name = nil
19
+
20
+ @path.each_line do |line|
21
+ line.strip!
22
+ next if line.empty?
23
+
24
+ if line =~ /\A\[(.+)\]\z/
25
+ profile_name = $1
26
+ elsif profile_name
27
+ key, value = line.split('=', 2).map {|i| i.strip }
28
+ @profiles[profile_name] ||= {}
29
+ @profiles[profile_name][key] = value
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ class V8::Object
2
+ def to_hash
3
+ to_hash0(self)
4
+ end
5
+
6
+ def to_hash0(obj)
7
+ case obj
8
+ when V8::Array
9
+ obj.map {|v| to_hash0(v) }
10
+ when V8::Object
11
+ h = {}
12
+ obj.each do |k, v|
13
+ h[to_hash0(k)] = to_hash0(v)
14
+ end
15
+ h
16
+ else
17
+ obj
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module Kumogata
2
- VERSION = '0.4.17'
2
+ VERSION = '0.4.18'
3
3
  end
@@ -0,0 +1,38 @@
1
+ describe Kumogata::ConfigParser do
2
+ subject { Kumogata::ConfigParser.new }
3
+
4
+ it 'parse aws/config' do
5
+ content = <<-EOS
6
+ [default]
7
+ aws_access_key_id = AKIAIOSFODNN7EXAMPLE
8
+ aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
9
+ aws_security_token = texample123324
10
+
11
+ [profile2]
12
+ aws_access_key_id = xAKIAIOSFODNN7EXAMPLE
13
+ aws_secret_access_key = xwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
14
+ aws_security_token = xtexample123324
15
+
16
+ [invalid]
17
+ EOS
18
+
19
+ tempfile(content) do |config|
20
+ subject.path = config.path
21
+ subject.parse!
22
+
23
+ expect(subject[:default]).to eq(
24
+ 'aws_access_key_id' => 'AKIAIOSFODNN7EXAMPLE',
25
+ 'aws_secret_access_key' => 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
26
+ 'aws_security_token' => 'texample123324',
27
+ )
28
+
29
+ expect(subject['profile2']).to eq(
30
+ 'aws_access_key_id' => 'xAKIAIOSFODNN7EXAMPLE',
31
+ 'aws_secret_access_key' => 'xwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
32
+ 'aws_security_token' => 'xtexample123324',
33
+ )
34
+
35
+ expect(subject['invalid']).to be_nil
36
+ end
37
+ end
38
+ end
@@ -126,6 +126,105 @@ end
126
126
  EOS
127
127
  end
128
128
 
129
+ it 'convert Ruby template to JavaScript template' do
130
+ template = <<-EOS
131
+ Resources do
132
+ myEC2Instance do
133
+ Type "AWS::EC2::Instance"
134
+ Properties do
135
+ ImageId "ami-XXXXXXXX"
136
+ InstanceType "t1.micro"
137
+ end
138
+ end
139
+ end
140
+
141
+ Outputs do
142
+ AZ do
143
+ Value do
144
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
145
+ end
146
+ end
147
+ end
148
+ EOS
149
+
150
+ js_template = run_client(:convert, :template => template, :options => {:output_format => :js})
151
+
152
+ expect(js_template).to eq <<-EOS.strip
153
+ ({
154
+ "Resources": {
155
+ "myEC2Instance": {
156
+ "Type": "AWS::EC2::Instance",
157
+ "Properties": {
158
+ "ImageId": "ami-XXXXXXXX",
159
+ "InstanceType": "t1.micro"
160
+ }
161
+ }
162
+ },
163
+ "Outputs": {
164
+ "AZ": {
165
+ "Value": {
166
+ "Fn::GetAtt": [
167
+ "myEC2Instance",
168
+ "AvailabilityZone"
169
+ ]
170
+ }
171
+ }
172
+ }
173
+ })
174
+ EOS
175
+ end
176
+
177
+ it 'convert JavaScript template to Ruby template' do
178
+ template = <<-EOS
179
+ function fetch_ami() {
180
+ return "ami-XXXXXXXX";
181
+ }
182
+
183
+ ({
184
+ Resources: { /* comment */
185
+ myEC2Instance: {
186
+ Type: "AWS::EC2::Instance",
187
+ Properties: {
188
+ ImageId: fetch_ami(),
189
+ InstanceType: "t1.micro"
190
+ }
191
+ }
192
+ },
193
+ Outputs: {
194
+ AZ: { /* comment */
195
+ Value: {
196
+ "Fn::GetAtt": [
197
+ "myEC2Instance",
198
+ "AvailabilityZone"
199
+ ]
200
+ }
201
+ }
202
+ }
203
+ })
204
+ EOS
205
+
206
+ ruby_template = run_client(:convert, :template => template, :template_ext => '.js', :options => {:output_format => :ruby})
207
+
208
+ expect(ruby_template).to eq((<<-EOS).chomp)
209
+ Resources do
210
+ myEC2Instance do
211
+ Type "AWS::EC2::Instance"
212
+ Properties do
213
+ ImageId "ami-XXXXXXXX"
214
+ InstanceType "t1.micro"
215
+ end
216
+ end
217
+ end
218
+ Outputs do
219
+ AZ do
220
+ Value do
221
+ Fn__GetAtt "myEC2Instance", "AvailabilityZone"
222
+ end
223
+ end
224
+ end
225
+ EOS
226
+ end
227
+
129
228
  it 'convert YAML template to JSON template' do
130
229
  template = <<-EOS
131
230
  ---
@@ -863,4 +962,1298 @@ end
863
962
 
864
963
  expect(ruby_template).to eq(json_template)
865
964
  end
965
+
966
+ let(:vpc_knowhow_2014_04_template) do
967
+ path = File.expand_path('../vpc-knowhow-2014-04.template', __FILE__)
968
+ open(path) {|f| f.read }
969
+ end
970
+
971
+ it 'convert JSON template to Ruby template (include yum key)' do
972
+ ruby_template = run_client(:convert, :template => vpc_knowhow_2014_04_template, :template_ext => '.template')
973
+
974
+ expect(ruby_template).to eq <<-'EOS'.strip
975
+ AWSTemplateFormatVersion "2010-09-09"
976
+ Description "VPC knowhow template"
977
+ Parameters do
978
+ KeyName do
979
+ Description "Name of an existing EC2 KeyPair to enable SSH access to the instances"
980
+ Type "String"
981
+ MinLength 1
982
+ MaxLength 64
983
+ AllowedPattern "[-_ a-zA-Z0-9]*"
984
+ ConstraintDescription "can contain only alphanumeric characters, spaces, dashes and underscores."
985
+ end
986
+ SSHFrom do
987
+ Description "Lockdown SSH access to the bastion host (default can be accessed from anywhere)"
988
+ Type "String"
989
+ MinLength 9
990
+ MaxLength 18
991
+ Default "0.0.0.0/0"
992
+ AllowedPattern "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
993
+ ConstraintDescription "must be a valid CIDR range of the form x.x.x.x/x."
994
+ end
995
+ DBInstanceType do
996
+ Description "EC2 instance type for the Blue environment"
997
+ Default "db.t1.micro"
998
+ Type "String"
999
+ end
1000
+ DBSnapshotName do
1001
+ Default ""
1002
+ Description "The name of a DB snapshot (optional)"
1003
+ Type "String"
1004
+ end
1005
+ DBAllocatedStorage do
1006
+ Default 5
1007
+ Description "DB instance disk size"
1008
+ Type "Number"
1009
+ end
1010
+ DBUsername do
1011
+ Default "admin"
1012
+ Description "The database master account username"
1013
+ Type "String"
1014
+ MinLength 1
1015
+ MaxLength 16
1016
+ AllowedPattern "[a-zA-Z][a-zA-Z0-9]*"
1017
+ ConstraintDescription "must begin with a letter and contain only alphanumeric characters."
1018
+ end
1019
+ DBPassword do
1020
+ Description "Password of RDS master password"
1021
+ Type "String"
1022
+ NoEcho "true"
1023
+ MinLength 4
1024
+ end
1025
+ DBName do
1026
+ Default ""
1027
+ Description "The name of a DB01 database"
1028
+ Type "String"
1029
+ end
1030
+ WebInstanceType do
1031
+ Description "EC2 instance type for the web server"
1032
+ Default "t1.micro"
1033
+ Type "String"
1034
+ end
1035
+ WebFleetSize do
1036
+ Description "Number of EC2 instances to launch for the web server"
1037
+ Default 2
1038
+ Type "Number"
1039
+ MaxValue 100
1040
+ MinValue 1
1041
+ end
1042
+ HostedZone do
1043
+ Description "The DNS name of an existing Amazon Route 53 hosted zone"
1044
+ Type "String"
1045
+ end
1046
+ end
1047
+ Conditions do
1048
+ UseDBSnapshot do
1049
+ Fn__Not [
1050
+ _{
1051
+ Fn__Equals [
1052
+ _{
1053
+ Ref "DBSnapshotName"
1054
+ },
1055
+ ""
1056
+ ]
1057
+ }
1058
+ ]
1059
+ end
1060
+ end
1061
+ Mappings do
1062
+ AWSAmazonLinuxAMI(
1063
+ {"us-east-1"=>
1064
+ {"name"=>"Virginia",
1065
+ "201303"=>"ami-3275ee5b",
1066
+ "201309"=>"ami-35792c5c",
1067
+ "201403"=>"ami-2f726546"},
1068
+ "us-west-2"=>
1069
+ {"name"=>"Oregon",
1070
+ "201303"=>"ami-ecbe2adc",
1071
+ "201309"=>"ami-d03ea1e0",
1072
+ "201403"=>"ami-b8f69f88"},
1073
+ "us-west-1"=>
1074
+ {"name"=>"California",
1075
+ "201303"=>"ami-66d1fc23",
1076
+ "201309"=>"ami-687b4f2d",
1077
+ "201403"=>"ami-84f1cfc1"},
1078
+ "eu-west-1"=>
1079
+ {"name"=>"Ireland",
1080
+ "201303"=>"ami-44939930",
1081
+ "201309"=>"ami-149f7863",
1082
+ "201403"=>"ami-a921dfde"},
1083
+ "ap-southeast-1"=>
1084
+ {"name"=>"Singapole",
1085
+ "201303"=>"ami-aa9ed2f8",
1086
+ "201309"=>"ami-14f2b946",
1087
+ "201403"=>"ami-787c2c2a"},
1088
+ "ap-southeast-2"=>
1089
+ {"name"=>"Sydney",
1090
+ "201303"=>"ami-363eaf0c",
1091
+ "201309"=>"ami-a148d59b",
1092
+ "201403"=>"ami-0bc85031"},
1093
+ "ap-northeast-1"=>
1094
+ {"name"=>"Tokyo",
1095
+ "201303"=>"ami-173fbf16",
1096
+ "201309"=>"ami-3561fe34",
1097
+ "201403"=>"ami-a1bec3a0"},
1098
+ "sa-east-1"=>
1099
+ {"name"=>"SaoPaulo",
1100
+ "201303"=>"ami-dd6bb0c0",
1101
+ "201309"=>"ami-9f6ec982",
1102
+ "201403"=>"ami-89de7c94"}})
1103
+ ELBLogger(
1104
+ {"us-east-1"=>{"AccountID"=>"127311923021"},
1105
+ "us-west-2"=>{"AccountID"=>"797873946194"},
1106
+ "us-west-1"=>{"AccountID"=>"027434742980"},
1107
+ "eu-west-1"=>{"AccountID"=>"156460612806"},
1108
+ "ap-southeast-1"=>{"AccountID"=>"114774131450"},
1109
+ "ap-southeast-2"=>{"AccountID"=>"783225319266"},
1110
+ "ap-northeast-1"=>{"AccountID"=>"582318560864"},
1111
+ "sa-east-1"=>{"AccountID"=>"507241528517"},
1112
+ "us-gov-west-1"=>{"AccountID"=>"048591011584"}})
1113
+ StackConfig do
1114
+ VPC do
1115
+ CIDR "10.0.0.0/16"
1116
+ end
1117
+ FrontendSubnet1 do
1118
+ CIDR "10.0.0.0/24"
1119
+ end
1120
+ FrontendSubnet2 do
1121
+ CIDR "10.0.1.0/24"
1122
+ end
1123
+ ApplicationSubnet1 do
1124
+ CIDR "10.0.100.0/24"
1125
+ end
1126
+ ApplicationSubnet2 do
1127
+ CIDR "10.0.101.0/24"
1128
+ end
1129
+ DatastoreSubnet1 do
1130
+ CIDR "10.0.200.0/24"
1131
+ end
1132
+ DatastoreSubnet2 do
1133
+ CIDR "10.0.201.0/24"
1134
+ end
1135
+ BastionServer do
1136
+ InstanceType "t1.micro"
1137
+ end
1138
+ end
1139
+ end
1140
+ Resources do
1141
+ PowerUserRole do
1142
+ Type "AWS::IAM::Role"
1143
+ Properties do
1144
+ AssumeRolePolicyDocument do
1145
+ Statement [
1146
+ _{
1147
+ Effect "Allow"
1148
+ Principal do
1149
+ Service ["ec2.amazonaws.com"]
1150
+ end
1151
+ Action ["sts:AssumeRole"]
1152
+ }
1153
+ ]
1154
+ end
1155
+ Path "/"
1156
+ Policies [
1157
+ _{
1158
+ PolicyName "PowerUserPolicy"
1159
+ PolicyDocument do
1160
+ Statement [
1161
+ _{
1162
+ Sid "PowerUserStmt"
1163
+ Effect "Allow"
1164
+ NotAction "iam:*"
1165
+ Resource "*"
1166
+ }
1167
+ ]
1168
+ end
1169
+ }
1170
+ ]
1171
+ end
1172
+ end
1173
+ PowerUserProfile do
1174
+ Type "AWS::IAM::InstanceProfile"
1175
+ Properties do
1176
+ Path "/"
1177
+ Roles [
1178
+ _{
1179
+ Ref "PowerUserRole"
1180
+ }
1181
+ ]
1182
+ end
1183
+ end
1184
+ LogBucket do
1185
+ Type "AWS::S3::Bucket"
1186
+ DeletionPolicy "Retain"
1187
+ end
1188
+ LogBucketPolicy do
1189
+ Type "AWS::S3::BucketPolicy"
1190
+ Properties do
1191
+ Bucket do
1192
+ Ref "LogBucket"
1193
+ end
1194
+ PolicyDocument do
1195
+ Id "LogBucketPolicy"
1196
+ Statement [
1197
+ _{
1198
+ Sid "WriteAccess"
1199
+ Action ["s3:PutObject"]
1200
+ Effect "Allow"
1201
+ Resource do
1202
+ Fn__Join [
1203
+ "",
1204
+ [
1205
+ "arn:aws:s3:::",
1206
+ _{
1207
+ Ref "LogBucket"
1208
+ },
1209
+ "/AWSLogs/",
1210
+ _{
1211
+ Ref "AWS::AccountId"
1212
+ },
1213
+ "/*"
1214
+ ]
1215
+ ]
1216
+ end
1217
+ Principal do
1218
+ AWS do
1219
+ Fn__FindInMap [
1220
+ "ELBLogger",
1221
+ _{
1222
+ Ref "AWS::Region"
1223
+ },
1224
+ "AccountID"
1225
+ ]
1226
+ end
1227
+ end
1228
+ }
1229
+ ]
1230
+ end
1231
+ end
1232
+ end
1233
+ VPC do
1234
+ Type "AWS::EC2::VPC"
1235
+ Properties do
1236
+ CidrBlock do
1237
+ Fn__FindInMap "StackConfig", "VPC", "CIDR"
1238
+ end
1239
+ EnableDnsSupport "true"
1240
+ EnableDnsHostnames "true"
1241
+ InstanceTenancy "default"
1242
+ Tags [
1243
+ _{
1244
+ Key "Application"
1245
+ Value do
1246
+ Ref "AWS::StackId"
1247
+ end
1248
+ },
1249
+ _{
1250
+ Key "Network"
1251
+ Value "Public"
1252
+ }
1253
+ ]
1254
+ end
1255
+ end
1256
+ InternetGateway do
1257
+ Type "AWS::EC2::InternetGateway"
1258
+ Properties do
1259
+ Tags [
1260
+ _{
1261
+ Key "Application"
1262
+ Value do
1263
+ Ref "AWS::StackId"
1264
+ end
1265
+ },
1266
+ _{
1267
+ Key "Network"
1268
+ Value "Public"
1269
+ }
1270
+ ]
1271
+ end
1272
+ end
1273
+ AttachGateway do
1274
+ Type "AWS::EC2::VPCGatewayAttachment"
1275
+ Properties do
1276
+ VpcId do
1277
+ Ref "VPC"
1278
+ end
1279
+ InternetGatewayId do
1280
+ Ref "InternetGateway"
1281
+ end
1282
+ end
1283
+ end
1284
+ PublicRouteTable do
1285
+ Type "AWS::EC2::RouteTable"
1286
+ DependsOn "AttachGateway"
1287
+ Properties do
1288
+ VpcId do
1289
+ Ref "VPC"
1290
+ end
1291
+ Tags [
1292
+ _{
1293
+ Key "Application"
1294
+ Value do
1295
+ Ref "AWS::StackId"
1296
+ end
1297
+ },
1298
+ _{
1299
+ Key "Network"
1300
+ Value "Public"
1301
+ }
1302
+ ]
1303
+ end
1304
+ end
1305
+ PrivateRouteTable do
1306
+ Type "AWS::EC2::RouteTable"
1307
+ DependsOn "AttachGateway"
1308
+ Properties do
1309
+ VpcId do
1310
+ Ref "VPC"
1311
+ end
1312
+ Tags [
1313
+ _{
1314
+ Key "Application"
1315
+ Value do
1316
+ Ref "AWS::StackId"
1317
+ end
1318
+ },
1319
+ _{
1320
+ Key "Network"
1321
+ Value "Private"
1322
+ }
1323
+ ]
1324
+ end
1325
+ end
1326
+ PublicRoute do
1327
+ Type "AWS::EC2::Route"
1328
+ DependsOn "AttachGateway"
1329
+ Properties do
1330
+ RouteTableId do
1331
+ Ref "PublicRouteTable"
1332
+ end
1333
+ DestinationCidrBlock "0.0.0.0/0"
1334
+ GatewayId do
1335
+ Ref "InternetGateway"
1336
+ end
1337
+ end
1338
+ end
1339
+ FrontendSubnet1 do
1340
+ Type "AWS::EC2::Subnet"
1341
+ DependsOn "AttachGateway"
1342
+ Properties do
1343
+ VpcId do
1344
+ Ref "VPC"
1345
+ end
1346
+ AvailabilityZone do
1347
+ Fn__Select [
1348
+ "0",
1349
+ _{
1350
+ Fn__GetAZs do
1351
+ Ref "AWS::Region"
1352
+ end
1353
+ }
1354
+ ]
1355
+ end
1356
+ CidrBlock do
1357
+ Fn__FindInMap "StackConfig", "FrontendSubnet1", "CIDR"
1358
+ end
1359
+ Tags [
1360
+ _{
1361
+ Key "Application"
1362
+ Value do
1363
+ Ref "AWS::StackId"
1364
+ end
1365
+ },
1366
+ _{
1367
+ Key "Network"
1368
+ Value "Public"
1369
+ }
1370
+ ]
1371
+ end
1372
+ end
1373
+ FrontendSubnet2 do
1374
+ Type "AWS::EC2::Subnet"
1375
+ DependsOn "AttachGateway"
1376
+ Properties do
1377
+ VpcId do
1378
+ Ref "VPC"
1379
+ end
1380
+ AvailabilityZone do
1381
+ Fn__Select [
1382
+ "1",
1383
+ _{
1384
+ Fn__GetAZs do
1385
+ Ref "AWS::Region"
1386
+ end
1387
+ }
1388
+ ]
1389
+ end
1390
+ CidrBlock do
1391
+ Fn__FindInMap "StackConfig", "FrontendSubnet2", "CIDR"
1392
+ end
1393
+ Tags [
1394
+ _{
1395
+ Key "Application"
1396
+ Value do
1397
+ Ref "AWS::StackId"
1398
+ end
1399
+ },
1400
+ _{
1401
+ Key "Network"
1402
+ Value "Public"
1403
+ }
1404
+ ]
1405
+ end
1406
+ end
1407
+ ApplicationSubnet1 do
1408
+ Type "AWS::EC2::Subnet"
1409
+ DependsOn "AttachGateway"
1410
+ Properties do
1411
+ VpcId do
1412
+ Ref "VPC"
1413
+ end
1414
+ CidrBlock do
1415
+ Fn__FindInMap "StackConfig", "ApplicationSubnet1", "CIDR"
1416
+ end
1417
+ AvailabilityZone do
1418
+ Fn__Select [
1419
+ "0",
1420
+ _{
1421
+ Fn__GetAZs do
1422
+ Ref "AWS::Region"
1423
+ end
1424
+ }
1425
+ ]
1426
+ end
1427
+ Tags [
1428
+ _{
1429
+ Key "Application"
1430
+ Value do
1431
+ Ref "AWS::StackId"
1432
+ end
1433
+ },
1434
+ _{
1435
+ Key "Network"
1436
+ Value "Public"
1437
+ }
1438
+ ]
1439
+ end
1440
+ end
1441
+ ApplicationSubnet2 do
1442
+ Type "AWS::EC2::Subnet"
1443
+ DependsOn "AttachGateway"
1444
+ Properties do
1445
+ VpcId do
1446
+ Ref "VPC"
1447
+ end
1448
+ CidrBlock do
1449
+ Fn__FindInMap "StackConfig", "ApplicationSubnet2", "CIDR"
1450
+ end
1451
+ AvailabilityZone do
1452
+ Fn__Select [
1453
+ "1",
1454
+ _{
1455
+ Fn__GetAZs do
1456
+ Ref "AWS::Region"
1457
+ end
1458
+ }
1459
+ ]
1460
+ end
1461
+ Tags [
1462
+ _{
1463
+ Key "Application"
1464
+ Value do
1465
+ Ref "AWS::StackId"
1466
+ end
1467
+ },
1468
+ _{
1469
+ Key "Network"
1470
+ Value "Public"
1471
+ }
1472
+ ]
1473
+ end
1474
+ end
1475
+ DatastoreSubnet1 do
1476
+ Type "AWS::EC2::Subnet"
1477
+ DependsOn "AttachGateway"
1478
+ Properties do
1479
+ VpcId do
1480
+ Ref "VPC"
1481
+ end
1482
+ CidrBlock do
1483
+ Fn__FindInMap "StackConfig", "DatastoreSubnet1", "CIDR"
1484
+ end
1485
+ AvailabilityZone do
1486
+ Fn__Select [
1487
+ "0",
1488
+ _{
1489
+ Fn__GetAZs do
1490
+ Ref "AWS::Region"
1491
+ end
1492
+ }
1493
+ ]
1494
+ end
1495
+ Tags [
1496
+ _{
1497
+ Key "Application"
1498
+ Value do
1499
+ Ref "AWS::StackId"
1500
+ end
1501
+ },
1502
+ _{
1503
+ Key "Network"
1504
+ Value "Private"
1505
+ }
1506
+ ]
1507
+ end
1508
+ end
1509
+ DatastoreSubnet2 do
1510
+ Type "AWS::EC2::Subnet"
1511
+ DependsOn "AttachGateway"
1512
+ Properties do
1513
+ VpcId do
1514
+ Ref "VPC"
1515
+ end
1516
+ CidrBlock do
1517
+ Fn__FindInMap "StackConfig", "DatastoreSubnet2", "CIDR"
1518
+ end
1519
+ AvailabilityZone do
1520
+ Fn__Select [
1521
+ "1",
1522
+ _{
1523
+ Fn__GetAZs do
1524
+ Ref "AWS::Region"
1525
+ end
1526
+ }
1527
+ ]
1528
+ end
1529
+ Tags [
1530
+ _{
1531
+ Key "Application"
1532
+ Value do
1533
+ Ref "AWS::StackId"
1534
+ end
1535
+ },
1536
+ _{
1537
+ Key "Network"
1538
+ Value "Private"
1539
+ }
1540
+ ]
1541
+ end
1542
+ end
1543
+ FrontendSubnet1RouteTableAssociation do
1544
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1545
+ Properties do
1546
+ SubnetId do
1547
+ Ref "FrontendSubnet1"
1548
+ end
1549
+ RouteTableId do
1550
+ Ref "PublicRouteTable"
1551
+ end
1552
+ end
1553
+ end
1554
+ FrontendSubnet2RouteTableAssociation do
1555
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1556
+ Properties do
1557
+ SubnetId do
1558
+ Ref "FrontendSubnet2"
1559
+ end
1560
+ RouteTableId do
1561
+ Ref "PublicRouteTable"
1562
+ end
1563
+ end
1564
+ end
1565
+ ApplicationSubnet1RouteTableAssociation do
1566
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1567
+ Properties do
1568
+ SubnetId do
1569
+ Ref "ApplicationSubnet1"
1570
+ end
1571
+ RouteTableId do
1572
+ Ref "PublicRouteTable"
1573
+ end
1574
+ end
1575
+ end
1576
+ ApplicationSubnet2RouteTableAssociation do
1577
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1578
+ Properties do
1579
+ SubnetId do
1580
+ Ref "ApplicationSubnet2"
1581
+ end
1582
+ RouteTableId do
1583
+ Ref "PublicRouteTable"
1584
+ end
1585
+ end
1586
+ end
1587
+ DatastoreSubnet1RouteTableAssociation do
1588
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1589
+ Properties do
1590
+ SubnetId do
1591
+ Ref "DatastoreSubnet1"
1592
+ end
1593
+ RouteTableId do
1594
+ Ref "PrivateRouteTable"
1595
+ end
1596
+ end
1597
+ end
1598
+ DatastoreSubnet2RouteTableAssociation do
1599
+ Type "AWS::EC2::SubnetRouteTableAssociation"
1600
+ Properties do
1601
+ SubnetId do
1602
+ Ref "DatastoreSubnet2"
1603
+ end
1604
+ RouteTableId do
1605
+ Ref "PrivateRouteTable"
1606
+ end
1607
+ end
1608
+ end
1609
+ VPCDefaultSecurityGroup do
1610
+ Type "AWS::EC2::SecurityGroup"
1611
+ Properties do
1612
+ VpcId do
1613
+ Ref "VPC"
1614
+ end
1615
+ GroupDescription "Allow all communications in VPC"
1616
+ SecurityGroupIngress [
1617
+ _{
1618
+ IpProtocol "tcp"
1619
+ FromPort 0
1620
+ ToPort 65535
1621
+ CidrIp do
1622
+ Fn__FindInMap "StackConfig", "VPC", "CIDR"
1623
+ end
1624
+ },
1625
+ _{
1626
+ IpProtocol "udp"
1627
+ FromPort 0
1628
+ ToPort 65535
1629
+ CidrIp do
1630
+ Fn__FindInMap "StackConfig", "VPC", "CIDR"
1631
+ end
1632
+ },
1633
+ _{
1634
+ IpProtocol "icmp"
1635
+ FromPort "-1"
1636
+ ToPort "-1"
1637
+ CidrIp do
1638
+ Fn__FindInMap "StackConfig", "VPC", "CIDR"
1639
+ end
1640
+ }
1641
+ ]
1642
+ end
1643
+ end
1644
+ SSHSecurityGroup do
1645
+ Type "AWS::EC2::SecurityGroup"
1646
+ Properties do
1647
+ VpcId do
1648
+ Ref "VPC"
1649
+ end
1650
+ GroupDescription "Enable SSH access via port 22"
1651
+ SecurityGroupIngress [
1652
+ _{
1653
+ IpProtocol "tcp"
1654
+ FromPort 22
1655
+ ToPort 22
1656
+ CidrIp do
1657
+ Ref "SSHFrom"
1658
+ end
1659
+ }
1660
+ ]
1661
+ end
1662
+ end
1663
+ PublicWebSecurityGroup do
1664
+ Type "AWS::EC2::SecurityGroup"
1665
+ Properties do
1666
+ VpcId do
1667
+ Ref "VPC"
1668
+ end
1669
+ GroupDescription "Public Security Group with HTTP access on port 443 from the internet"
1670
+ SecurityGroupIngress [
1671
+ _{
1672
+ IpProtocol "tcp"
1673
+ FromPort 80
1674
+ ToPort 80
1675
+ CidrIp "0.0.0.0/0"
1676
+ },
1677
+ _{
1678
+ IpProtocol "tcp"
1679
+ FromPort 443
1680
+ ToPort 443
1681
+ CidrIp "0.0.0.0/0"
1682
+ }
1683
+ ]
1684
+ end
1685
+ end
1686
+ ApplicationSecurityGroup do
1687
+ Type "AWS::EC2::SecurityGroup"
1688
+ Properties do
1689
+ VpcId do
1690
+ Ref "VPC"
1691
+ end
1692
+ GroupDescription "Marker security group for Application server."
1693
+ end
1694
+ end
1695
+ MySQLSecurityGroup do
1696
+ Type "AWS::EC2::SecurityGroup"
1697
+ Properties do
1698
+ VpcId do
1699
+ Ref "VPC"
1700
+ end
1701
+ GroupDescription "Marker security group for MySQL server."
1702
+ end
1703
+ end
1704
+ BastionWaitHandle do
1705
+ Type "AWS::CloudFormation::WaitConditionHandle"
1706
+ end
1707
+ BastionWaitCondition do
1708
+ Type "AWS::CloudFormation::WaitCondition"
1709
+ DependsOn "BastionInstance"
1710
+ Properties do
1711
+ Handle do
1712
+ Ref "BastionWaitHandle"
1713
+ end
1714
+ Timeout 900
1715
+ end
1716
+ end
1717
+ BastionInstance do
1718
+ Type "AWS::EC2::Instance"
1719
+ Properties do
1720
+ InstanceType do
1721
+ Fn__FindInMap "StackConfig", "BastionServer", "InstanceType"
1722
+ end
1723
+ KeyName do
1724
+ Ref "KeyName"
1725
+ end
1726
+ SubnetId do
1727
+ Ref "FrontendSubnet1"
1728
+ end
1729
+ ImageId do
1730
+ Fn__FindInMap [
1731
+ "AWSAmazonLinuxAMI",
1732
+ _{
1733
+ Ref "AWS::Region"
1734
+ },
1735
+ "201403"
1736
+ ]
1737
+ end
1738
+ IamInstanceProfile do
1739
+ Ref "PowerUserProfile"
1740
+ end
1741
+ SecurityGroupIds [
1742
+ _{
1743
+ Ref "SSHSecurityGroup"
1744
+ },
1745
+ _{
1746
+ Ref "VPCDefaultSecurityGroup"
1747
+ }
1748
+ ]
1749
+ Tags [
1750
+ _{
1751
+ Key "Name"
1752
+ Value "Bastion"
1753
+ }
1754
+ ]
1755
+ UserData do
1756
+ Fn__Base64 do
1757
+ Fn__Join [
1758
+ "",
1759
+ [
1760
+ "#! /bin/bash -v\n",
1761
+ "yum update -y\n",
1762
+ "# Helper function\n",
1763
+ "function error_exit\n",
1764
+ "{\n",
1765
+ " /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '",
1766
+ _{
1767
+ Ref "BastionWaitHandle"
1768
+ },
1769
+ "'\n",
1770
+ " exit 1\n",
1771
+ "}\n",
1772
+ "# Install packages\n",
1773
+ "/opt/aws/bin/cfn-init -s ",
1774
+ _{
1775
+ Ref "AWS::StackId"
1776
+ },
1777
+ " -r BastionInstance ",
1778
+ " --region ",
1779
+ _{
1780
+ Ref "AWS::Region"
1781
+ },
1782
+ " || error_exit 'Failed to run cfn-init'\n",
1783
+ "# All is well so signal success\n",
1784
+ "/opt/aws/bin/cfn-signal -e $? -r \"BastionInstance setup complete\" '",
1785
+ _{
1786
+ Ref "BastionWaitHandle"
1787
+ },
1788
+ "'\n"
1789
+ ]
1790
+ ]
1791
+ end
1792
+ end
1793
+ end
1794
+ Metadata do
1795
+ AWS__CloudFormation__Init do
1796
+ config do
1797
+ packages do
1798
+ yum(
1799
+ {"mysql55"=>[], "jq"=>[], "python-magic"=>[]})
1800
+ end
1801
+ end
1802
+ end
1803
+ end
1804
+ end
1805
+ BastionInstanceEIP do
1806
+ Type "AWS::EC2::EIP"
1807
+ DependsOn "AttachGateway"
1808
+ Properties do
1809
+ Domain "vpc"
1810
+ InstanceId do
1811
+ Ref "BastionInstance"
1812
+ end
1813
+ end
1814
+ end
1815
+ BastionDNSRecord do
1816
+ Type "AWS::Route53::RecordSet"
1817
+ Properties do
1818
+ HostedZoneName do
1819
+ Fn__Join [
1820
+ "",
1821
+ [
1822
+ _{
1823
+ Ref "HostedZone"
1824
+ },
1825
+ "."
1826
+ ]
1827
+ ]
1828
+ end
1829
+ Comment "A record for the Bastion instance."
1830
+ Name do
1831
+ Fn__Join [
1832
+ "",
1833
+ [
1834
+ "bastion.",
1835
+ _{
1836
+ Ref "HostedZone"
1837
+ },
1838
+ "."
1839
+ ]
1840
+ ]
1841
+ end
1842
+ Type "A"
1843
+ TTL 300
1844
+ ResourceRecords [
1845
+ _{
1846
+ Ref "BastionInstanceEIP"
1847
+ }
1848
+ ]
1849
+ end
1850
+ end
1851
+ BastionLocalDNSRecord do
1852
+ Type "AWS::Route53::RecordSet"
1853
+ Properties do
1854
+ HostedZoneName do
1855
+ Fn__Join [
1856
+ "",
1857
+ [
1858
+ _{
1859
+ Ref "HostedZone"
1860
+ },
1861
+ "."
1862
+ ]
1863
+ ]
1864
+ end
1865
+ Comment "A record for the private IP address of Bastion instance."
1866
+ Name do
1867
+ Fn__Join [
1868
+ "",
1869
+ [
1870
+ "bastion.local.",
1871
+ _{
1872
+ Ref "HostedZone"
1873
+ },
1874
+ "."
1875
+ ]
1876
+ ]
1877
+ end
1878
+ Type "A"
1879
+ TTL 300
1880
+ ResourceRecords [
1881
+ _{
1882
+ Fn__GetAtt "BastionInstance", "PrivateIp"
1883
+ }
1884
+ ]
1885
+ end
1886
+ end
1887
+ DBParamGroup do
1888
+ Type "AWS::RDS::DBParameterGroup"
1889
+ Properties do
1890
+ Description "Default parameter group for Portnoy"
1891
+ Family "MySQL5.6"
1892
+ Parameters(
1893
+ {"character_set_database"=>"utf8mb4",
1894
+ "character_set_client"=>"utf8mb4",
1895
+ "character_set_connection"=>"utf8mb4",
1896
+ "character_set_results"=>"utf8mb4",
1897
+ "character_set_server"=>"utf8mb4",
1898
+ "skip-character-set-client-handshake"=>"TRUE"})
1899
+ end
1900
+ end
1901
+ DBSubnetGroup do
1902
+ Type "AWS::RDS::DBSubnetGroup"
1903
+ Properties do
1904
+ DBSubnetGroupDescription "Database subnets for RDS"
1905
+ SubnetIds [
1906
+ _{
1907
+ Ref "DatastoreSubnet1"
1908
+ },
1909
+ _{
1910
+ Ref "DatastoreSubnet2"
1911
+ }
1912
+ ]
1913
+ end
1914
+ end
1915
+ DBInstance do
1916
+ Type "AWS::RDS::DBInstance"
1917
+ DeletionPolicy "Snapshot"
1918
+ Properties do
1919
+ DBInstanceClass do
1920
+ Ref "DBInstanceType"
1921
+ end
1922
+ AllocatedStorage do
1923
+ Ref "DBAllocatedStorage"
1924
+ end
1925
+ Engine "MySQL"
1926
+ MultiAZ "true"
1927
+ EngineVersion "5.6.13"
1928
+ MasterUsername do
1929
+ Ref "DBUsername"
1930
+ end
1931
+ MasterUserPassword do
1932
+ Ref "DBPassword"
1933
+ end
1934
+ BackupRetentionPeriod 35
1935
+ DBParameterGroupName do
1936
+ Ref "DBParamGroup"
1937
+ end
1938
+ DBSubnetGroupName do
1939
+ Ref "DBSubnetGroup"
1940
+ end
1941
+ DBSnapshotIdentifier do
1942
+ Fn__If [
1943
+ "UseDBSnapshot",
1944
+ _{
1945
+ Ref "DBSnapshotName"
1946
+ },
1947
+ _{
1948
+ Ref "AWS::NoValue"
1949
+ }
1950
+ ]
1951
+ end
1952
+ PreferredBackupWindow "19:00-19:30"
1953
+ PreferredMaintenanceWindow "sat:20:00-sat:20:30"
1954
+ VPCSecurityGroups [
1955
+ _{
1956
+ Ref "VPCDefaultSecurityGroup"
1957
+ },
1958
+ _{
1959
+ Ref "MySQLSecurityGroup"
1960
+ }
1961
+ ]
1962
+ end
1963
+ end
1964
+ DatabaseDNSRecord do
1965
+ Type "AWS::Route53::RecordSet"
1966
+ Properties do
1967
+ HostedZoneName do
1968
+ Fn__Join [
1969
+ "",
1970
+ [
1971
+ _{
1972
+ Ref "HostedZone"
1973
+ },
1974
+ "."
1975
+ ]
1976
+ ]
1977
+ end
1978
+ Comment "CNAME for the database."
1979
+ Name do
1980
+ Fn__Join [
1981
+ "",
1982
+ [
1983
+ "db.local.",
1984
+ _{
1985
+ Ref "HostedZone"
1986
+ },
1987
+ "."
1988
+ ]
1989
+ ]
1990
+ end
1991
+ Type "CNAME"
1992
+ TTL 300
1993
+ ResourceRecords [
1994
+ _{
1995
+ Fn__GetAtt "DBInstance", "Endpoint.Address"
1996
+ }
1997
+ ]
1998
+ end
1999
+ end
2000
+ ApplicationFleet do
2001
+ Type "AWS::AutoScaling::AutoScalingGroup"
2002
+ UpdatePolicy do
2003
+ AutoScalingRollingUpdate do
2004
+ MaxBatchSize 1
2005
+ MinInstancesInService 1
2006
+ PauseTime "PT2M30S"
2007
+ end
2008
+ end
2009
+ Properties do
2010
+ AvailabilityZones [
2011
+ _{
2012
+ Fn__GetAtt "ApplicationSubnet1", "AvailabilityZone"
2013
+ },
2014
+ _{
2015
+ Fn__GetAtt "ApplicationSubnet2", "AvailabilityZone"
2016
+ }
2017
+ ]
2018
+ VPCZoneIdentifier [
2019
+ _{
2020
+ Ref "ApplicationSubnet1"
2021
+ },
2022
+ _{
2023
+ Ref "ApplicationSubnet2"
2024
+ }
2025
+ ]
2026
+ LaunchConfigurationName do
2027
+ Ref "ApplicationServerLaunchConfig"
2028
+ end
2029
+ MinSize do
2030
+ Ref "WebFleetSize"
2031
+ end
2032
+ MaxSize do
2033
+ Ref "WebFleetSize"
2034
+ end
2035
+ DesiredCapacity do
2036
+ Ref "WebFleetSize"
2037
+ end
2038
+ LoadBalancerNames [
2039
+ _{
2040
+ Ref "ElasticLoadBalancer"
2041
+ }
2042
+ ]
2043
+ Tags [
2044
+ _{
2045
+ Key "Name"
2046
+ Value "Application"
2047
+ PropagateAtLaunch "true"
2048
+ }
2049
+ ]
2050
+ end
2051
+ end
2052
+ ApplicationServerLaunchConfig do
2053
+ Type "AWS::AutoScaling::LaunchConfiguration"
2054
+ Properties do
2055
+ InstanceType do
2056
+ Ref "WebInstanceType"
2057
+ end
2058
+ KeyName do
2059
+ Ref "KeyName"
2060
+ end
2061
+ ImageId do
2062
+ Fn__FindInMap [
2063
+ "AWSAmazonLinuxAMI",
2064
+ _{
2065
+ Ref "AWS::Region"
2066
+ },
2067
+ "201403"
2068
+ ]
2069
+ end
2070
+ SecurityGroups [
2071
+ _{
2072
+ Ref "VPCDefaultSecurityGroup"
2073
+ },
2074
+ _{
2075
+ Ref "ApplicationSecurityGroup"
2076
+ }
2077
+ ]
2078
+ AssociatePublicIpAddress "true"
2079
+ IamInstanceProfile do
2080
+ Ref "PowerUserProfile"
2081
+ end
2082
+ InstanceMonitoring "false"
2083
+ UserData do
2084
+ Fn__Base64 do
2085
+ Fn__Join [
2086
+ "",
2087
+ [
2088
+ "#! /bin/bash -v\n",
2089
+ "yum update -y\n",
2090
+ "# Install packages\n",
2091
+ "/opt/aws/bin/cfn-init -s ",
2092
+ _{
2093
+ Ref "AWS::StackId"
2094
+ },
2095
+ " -r ApplicationServerLaunchConfig ",
2096
+ " --region ",
2097
+ _{
2098
+ Ref "AWS::Region"
2099
+ },
2100
+ " || error_exit 'Failed to run cfn-init'\n"
2101
+ ]
2102
+ ]
2103
+ end
2104
+ end
2105
+ end
2106
+ Metadata do
2107
+ AWS__CloudFormation__Init do
2108
+ config do
2109
+ packages do
2110
+ yum(
2111
+ {"httpd"=>[], "mysql55"=>[]})
2112
+ end
2113
+ files do
2114
+ _path("/var/www/html/index.html") do
2115
+ content "<html><head><title>Hello</title></head><body>Hello, world!</body></html>"
2116
+ mode "000644"
2117
+ owner "apache"
2118
+ group "apache"
2119
+ end
2120
+ end
2121
+ services do
2122
+ sysvinit do
2123
+ httpd do
2124
+ enabled "true"
2125
+ ensureRunning "true"
2126
+ end
2127
+ end
2128
+ end
2129
+ end
2130
+ end
2131
+ end
2132
+ end
2133
+ ElasticLoadBalancer do
2134
+ Type "AWS::ElasticLoadBalancing::LoadBalancer"
2135
+ DependsOn "AttachGateway"
2136
+ Properties do
2137
+ Subnets [
2138
+ _{
2139
+ Ref "FrontendSubnet1"
2140
+ },
2141
+ _{
2142
+ Ref "FrontendSubnet2"
2143
+ }
2144
+ ]
2145
+ Listeners [
2146
+ _{
2147
+ LoadBalancerPort 80
2148
+ InstancePort 80
2149
+ Protocol "HTTP"
2150
+ }
2151
+ ]
2152
+ HealthCheck do
2153
+ Target "HTTP:80/index.html"
2154
+ HealthyThreshold 2
2155
+ UnhealthyThreshold 2
2156
+ Interval 6
2157
+ Timeout 5
2158
+ end
2159
+ SecurityGroups [
2160
+ _{
2161
+ Ref "PublicWebSecurityGroup"
2162
+ }
2163
+ ]
2164
+ end
2165
+ end
2166
+ LoadBalancerDNSRecord do
2167
+ Type "AWS::Route53::RecordSetGroup"
2168
+ Properties do
2169
+ HostedZoneName do
2170
+ Fn__Join [
2171
+ "",
2172
+ [
2173
+ _{
2174
+ Ref "HostedZone"
2175
+ },
2176
+ "."
2177
+ ]
2178
+ ]
2179
+ end
2180
+ Comment "Zone apex alias targeted to LoadBalancer."
2181
+ RecordSets [
2182
+ _{
2183
+ Name do
2184
+ Fn__Join [
2185
+ "",
2186
+ [
2187
+ _{
2188
+ Ref "HostedZone"
2189
+ },
2190
+ "."
2191
+ ]
2192
+ ]
2193
+ end
2194
+ Type "A"
2195
+ AliasTarget do
2196
+ HostedZoneId do
2197
+ Fn__GetAtt "ElasticLoadBalancer", "CanonicalHostedZoneNameID"
2198
+ end
2199
+ DNSName do
2200
+ Fn__GetAtt "ElasticLoadBalancer", "CanonicalHostedZoneName"
2201
+ end
2202
+ end
2203
+ }
2204
+ ]
2205
+ end
2206
+ end
2207
+ end
2208
+ Outputs do
2209
+ JdbcConnectionString do
2210
+ Value do
2211
+ Fn__Join [
2212
+ "",
2213
+ [
2214
+ "jdbc:mysql://",
2215
+ _{
2216
+ Ref "DatabaseDNSRecord"
2217
+ },
2218
+ ":",
2219
+ _{
2220
+ Fn__GetAtt "DBInstance", "Endpoint.Port"
2221
+ },
2222
+ "/",
2223
+ _{
2224
+ Ref "DBName"
2225
+ }
2226
+ ]
2227
+ ]
2228
+ end
2229
+ Description "-"
2230
+ end
2231
+ SSHToBackendServer do
2232
+ Value do
2233
+ Fn__Join [
2234
+ "",
2235
+ [
2236
+ "ssh -i /path/to/",
2237
+ _{
2238
+ Ref "KeyName"
2239
+ },
2240
+ ".pem",
2241
+ " -oProxyCommand='ssh -i /path/to/",
2242
+ _{
2243
+ Ref "KeyName"
2244
+ },
2245
+ ".pem -W %h:%p ec2-user@",
2246
+ _{
2247
+ Ref "BastionDNSRecord"
2248
+ },
2249
+ "'",
2250
+ " ec2-user@<private-ip>"
2251
+ ]
2252
+ ]
2253
+ end
2254
+ Description "SSH command to connect to the backend servers"
2255
+ end
2256
+ end
2257
+ EOS
2258
+ end
866
2259
  end