cfndsl 0.1.16 → 0.1.17
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 +13 -5
- data/.gitignore +3 -0
- data/.travis.yml +19 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.md +221 -0
- data/Rakefile +55 -0
- data/cfndsl.gemspec +21 -0
- data/lib/cfndsl/version.rb +3 -0
- data/sample/autoscale.rb +243 -0
- data/sample/autoscale.template +176 -0
- data/sample/autoscale2.rb +231 -0
- data/sample/circular.rb +33 -0
- data/sample/t1.rb +16 -0
- data/sample/t1.yaml +2 -0
- data/sample/vpc-example.rb +51 -0
- data/spec/cfndsl_spec.rb +151 -0
- data/spec/fixtures/heattest.rb +23 -0
- data/spec/fixtures/test.rb +79 -0
- data/spec/spec_helper.rb +1 -0
- metadata +45 -22
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzU3YmUxZDJhYTg1YWNjNmE5MWE5OWY3NDBiNzM5NjNhMTMxYmM4Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OGM0MGQ3ZjY4YzllMDFiMGM2N2ZjNWNhZDhkN2U2OTVjY2QwYjhjMQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZmUwNTYyZmY0NjM4YWJmYzdhZmY2ZmVjNThmOTZhZTc0OGZjZDRmZmZjMzE5
|
10
|
+
N2UyNzg2NmM0MTJjZGUzMDNmMTc4YTk0ZmYxMjViMWY5MGQ0ZTI4ZDIzZmU2
|
11
|
+
MGNiZWU5Y2QxNWJlZmEyMTY3ZjMzNGQxYzBlYWFmOWNjNDlmMjE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTczZDA1NTAzNDUyNDIwNGYxY2M4YjcxMjI3OWE1Mzc5YzQwNTM2MGQ3MjNi
|
14
|
+
ZWZiMWM0Y2ZhMWRkMGI4NGU2MDE5NDUyNDU0YjljYTQyNWNhY2JlNjU5Y2Qy
|
15
|
+
MDM1OWRkMTBlMTUxZDFiNzQ2NjU1NDQxNDYxMWNmNWQ2YmM4MjE=
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.0
|
4
|
+
sudo: false
|
5
|
+
|
6
|
+
deploy:
|
7
|
+
provider: rubygems
|
8
|
+
api_key:
|
9
|
+
secure: AH+4R+m6U0kmALFYAYEQl2oF/dj1yOnu/M1ZOtWttk+Zjwnw6kFc0hFLbvxdy6uivq1XmMXVa9eXzDPsRjW4ow2g+UuDLxvkvfT45SMmJ5sYQpoiD7AIJnEvMu6TXnmfiRNgM5bHnV3oilNJ8KKCbCFCd+imGRLTkBuhLkhkejA=
|
10
|
+
gem: cfndsl
|
11
|
+
on:
|
12
|
+
tags: true
|
13
|
+
repo: stevenjack/cfndsl
|
14
|
+
notifications:
|
15
|
+
email:
|
16
|
+
recipients:
|
17
|
+
- stevenmajack@gmail.com
|
18
|
+
on_failure: change
|
19
|
+
on_success: never
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Steven Jack
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
cfndsl
|
2
|
+
======
|
3
|
+
|
4
|
+
[](https://travis-ci.org/stevenjack/cfndsl)
|
5
|
+
[](http://badge.fury.io/rb/cfndsl)
|
6
|
+
[](https://gitter.im/stevenjack/cfndsl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
7
|
+
|
8
|
+
[AWS Cloudformation](http://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/GettingStarted.html) templates are an incredibly powerful way to build
|
9
|
+
sets of resources in Amazon's AWS environment. Unfortunately, because
|
10
|
+
they are specified in JSON, they are also difficult to write and
|
11
|
+
maintain:
|
12
|
+
|
13
|
+
* JSON does not allow comments
|
14
|
+
|
15
|
+
* All structures are JSON, so it is sometimes easy for a person
|
16
|
+
reading a template to get lost.
|
17
|
+
|
18
|
+
* References and internal functions have a particularly unpleasant syntax.
|
19
|
+
|
20
|
+
|
21
|
+
The cnfdsl gem provides a simple DSL that allows you to write equivalent
|
22
|
+
templates in a more friendly language and generate the correct json
|
23
|
+
templates by running ruby.
|
24
|
+
|
25
|
+
## Getting Started
|
26
|
+
|
27
|
+
sudo gem install cfndsl
|
28
|
+
|
29
|
+
Now write a template in the dsl
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
|
33
|
+
CloudFormation {
|
34
|
+
Description "Test"
|
35
|
+
|
36
|
+
Parameter("One") {
|
37
|
+
String
|
38
|
+
Default "Test"
|
39
|
+
MaxLength 15
|
40
|
+
}
|
41
|
+
|
42
|
+
Output(:One,FnBase64( Ref("One")))
|
43
|
+
|
44
|
+
EC2_Instance(:MyInstance) {
|
45
|
+
ImageId "ami-12345678"
|
46
|
+
}
|
47
|
+
|
48
|
+
}
|
49
|
+
```
|
50
|
+
|
51
|
+
Then run cfndsl on the file
|
52
|
+
|
53
|
+
```
|
54
|
+
chris@raspberrypi:~/git/cfndsl$ cfndsl test.rb | json_pp
|
55
|
+
{
|
56
|
+
"Parameters" : {
|
57
|
+
"One" : {
|
58
|
+
"Type" : "String",
|
59
|
+
"Default" : "Test",
|
60
|
+
"MaxLength" : 15
|
61
|
+
}
|
62
|
+
},
|
63
|
+
"Resources" : {
|
64
|
+
"MyInstance" : {
|
65
|
+
"Type" : "AWS::EC2::Instance",
|
66
|
+
"Properties" : {
|
67
|
+
"ImageId" : "ami-12345678"
|
68
|
+
}
|
69
|
+
}
|
70
|
+
},
|
71
|
+
"AWSTemplateFormatVersion" : "2010-09-09",
|
72
|
+
"Outputs" : {
|
73
|
+
"One" : {
|
74
|
+
"Value" : {
|
75
|
+
"Fn::Base64" : {
|
76
|
+
"Ref" : "One"
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
},
|
81
|
+
"Description" : "Test"
|
82
|
+
}
|
83
|
+
```
|
84
|
+
|
85
|
+
*Aside: that is correct - a significant amount of the development for
|
86
|
+
this gem was done on a [Raspberry Pi](http://www.raspberrypi.org).*
|
87
|
+
|
88
|
+
## Samples
|
89
|
+
|
90
|
+
There is a more detailed example in the samples directory. The file
|
91
|
+
"autoscale.template" is one of the standard Amazon sample templates.
|
92
|
+
"autoscale.rb" generates an equivalent template file.
|
93
|
+
|
94
|
+
## Command Line Options
|
95
|
+
|
96
|
+
The cfndsl command line program now accepts some command line options.
|
97
|
+
|
98
|
+
```
|
99
|
+
Usage: cfndsl [options] FILE
|
100
|
+
-o, --output FILE Write output to file
|
101
|
+
-y, --yaml FILE Import yaml file as local variables
|
102
|
+
-r, --ruby FILE Evaluate ruby file before template
|
103
|
+
-j, --json FILE Import json file as local variables
|
104
|
+
-D, --define "VARIABLE=VALUE" Directly set local VARIABLE as VALUE
|
105
|
+
-v, --verbose Turn on verbose ouptut
|
106
|
+
-h, --help Display this screen
|
107
|
+
```
|
108
|
+
|
109
|
+
By default, cfndsl will attempt to evaluate FILE as cfndsl template and print
|
110
|
+
the resulting cloudformation json template to stdout. With the -o option, you
|
111
|
+
can instead have it write the resulting json template to a given file. The -v
|
112
|
+
option prints out additional information (to stderr) about what is happening
|
113
|
+
in the model generation process.
|
114
|
+
|
115
|
+
The -y, -j, -r and -D options can be used to control some things about the
|
116
|
+
environment that the template code gets evaluate in. For instance, the -D
|
117
|
+
option allows you to set a variable at the command line that can then be
|
118
|
+
referred to within the template itself.
|
119
|
+
|
120
|
+
This is best illustrated with a example. Consider the following cfndsl
|
121
|
+
template
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# cfndsl template t1.rb
|
125
|
+
CloudFormation {
|
126
|
+
|
127
|
+
DESCRIPTION ||= "default description"
|
128
|
+
MACHINES ||= 3
|
129
|
+
|
130
|
+
Description DESCRIPTION
|
131
|
+
|
132
|
+
(1..MACHINES).each do |i|
|
133
|
+
name = "machine#{i}"
|
134
|
+
EC2_Instance(name) {
|
135
|
+
ImageId "ami-12345678"
|
136
|
+
Type "t1.micro"
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
}
|
141
|
+
```
|
142
|
+
|
143
|
+
Note the two variables "DESCRIPTION" and "MACHINES". The template
|
144
|
+
sets these to some reasonable default values, and if you run cfndsl
|
145
|
+
on it without changing them in any way you get the following cloudformation
|
146
|
+
template:
|
147
|
+
|
148
|
+
```json
|
149
|
+
{
|
150
|
+
"Resources": {
|
151
|
+
"machine1": {
|
152
|
+
"Type": "AWS::EC2::Instance",
|
153
|
+
"Properties": {
|
154
|
+
"ImageId": "ami-12345678"
|
155
|
+
}
|
156
|
+
}
|
157
|
+
},
|
158
|
+
"Description": "default description",
|
159
|
+
"AWSTemplateFormatVersion": "2010-09-09"
|
160
|
+
}
|
161
|
+
```
|
162
|
+
|
163
|
+
However if you run the command
|
164
|
+
|
165
|
+
```bash
|
166
|
+
$ cfndsl t1.rb -D 'DESCRIPTION="3 machine cluster"' -D 'MACHINES=3'
|
167
|
+
```
|
168
|
+
|
169
|
+
you get the following generated template.
|
170
|
+
|
171
|
+
```json
|
172
|
+
{
|
173
|
+
"Resources": {
|
174
|
+
"machine3": {
|
175
|
+
"Type": "AWS::EC2::Instance",
|
176
|
+
"Properties": {
|
177
|
+
"ImageId": "ami-12345678"
|
178
|
+
}
|
179
|
+
},
|
180
|
+
"machine2": {
|
181
|
+
"Type": "AWS::EC2::Instance",
|
182
|
+
"Properties": {
|
183
|
+
"ImageId": "ami-12345678"
|
184
|
+
}
|
185
|
+
},
|
186
|
+
"machine1": {
|
187
|
+
"Type": "AWS::EC2::Instance",
|
188
|
+
"Properties": {
|
189
|
+
"ImageId": "ami-12345678"
|
190
|
+
}
|
191
|
+
}
|
192
|
+
},
|
193
|
+
"Description": "3 machine cluster",
|
194
|
+
"AWSTemplateFormatVersion": "2010-09-09"
|
195
|
+
}
|
196
|
+
```
|
197
|
+
|
198
|
+
The -y and -j options allow you to group several variable definitions
|
199
|
+
into a single file (formated as either yaml or ruby respectively). If
|
200
|
+
you had a file called 't1.yaml' that contained the following,
|
201
|
+
|
202
|
+
```yaml
|
203
|
+
# t1.yaml
|
204
|
+
DESCRIPTION: 5 machine cluster
|
205
|
+
MACHINES: 5
|
206
|
+
```
|
207
|
+
|
208
|
+
the command
|
209
|
+
|
210
|
+
```bash
|
211
|
+
$ cfndsl t1.rb -y t1.yaml
|
212
|
+
```
|
213
|
+
|
214
|
+
would generate a template with 5 instances declared.
|
215
|
+
|
216
|
+
Finally, the -r option gives you the opportunity to execute some
|
217
|
+
arbitrary ruby code in the evaluation context before the cloudformation
|
218
|
+
template is evaluated.
|
219
|
+
|
220
|
+
|
221
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "cfndsl/version"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new
|
6
|
+
|
7
|
+
task default: [:spec]
|
8
|
+
|
9
|
+
task :bump, :type do |t, args|
|
10
|
+
type = args[:type].downcase
|
11
|
+
version_path = "lib/cfndsl/version.rb"
|
12
|
+
|
13
|
+
fail unless %w(major minor patch).include? type
|
14
|
+
|
15
|
+
if `git rev-parse --abbrev-ref HEAD` != "master"
|
16
|
+
fail "Looks like you're trying to create a release in a branch, you can only create one in 'master'"
|
17
|
+
end
|
18
|
+
|
19
|
+
version_segments = CfnDsl::VERSION.split(".").map(&:to_i)
|
20
|
+
|
21
|
+
case type
|
22
|
+
when "major"
|
23
|
+
version_segments[0]+= 1
|
24
|
+
version_segments[1] = 0
|
25
|
+
version_segments[2] = 0
|
26
|
+
when "minor"
|
27
|
+
version_segments[1]+= 1
|
28
|
+
version_segments[2] = 0
|
29
|
+
when "patch"
|
30
|
+
version_segments[2]+= 1
|
31
|
+
end
|
32
|
+
|
33
|
+
version = version_segments.join(".")
|
34
|
+
|
35
|
+
puts "Bumping gem from version #{CfnDsl::VERSION} to #{version} as a '#{type.capitalize}' release"
|
36
|
+
|
37
|
+
contents = File.read version_path
|
38
|
+
updated_contents = contents.gsub(/([0-9\.]+)/, version)
|
39
|
+
File.write(version_path, updated_contents)
|
40
|
+
|
41
|
+
puts "Commiting version update"
|
42
|
+
`git add #{version_path}`
|
43
|
+
`git commit --message='#{type.capitalize} release #{version}'`
|
44
|
+
|
45
|
+
puts "Tagging release"
|
46
|
+
`git tag -a v#{version} -m 'Version #{version}'`
|
47
|
+
|
48
|
+
puts "Pushing branch"
|
49
|
+
`git push origin master`
|
50
|
+
|
51
|
+
puts "Pushing tag"
|
52
|
+
`git push origin v#{version}`
|
53
|
+
|
54
|
+
puts "All done, travis should pick up and release the gem now!"
|
55
|
+
end
|
data/cfndsl.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cfndsl/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "cfndsl"
|
8
|
+
s.version = CfnDsl::VERSION
|
9
|
+
s.summary = "AWS Cloudformation DSL"
|
10
|
+
s.description = "DSL for creating AWS Cloudformation templates"
|
11
|
+
s.authors = ["Steven Jack", "Chris Howe"]
|
12
|
+
s.email = ["stevenmajack@gmail.com", "chris@howeville.com"]
|
13
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
14
|
+
s.executables << "cfndsl"
|
15
|
+
s.homepage = "https://github.com/stevenjack/cfndsl"
|
16
|
+
s.license = "MIT"
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency "bundler"
|
21
|
+
end
|
data/sample/autoscale.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
|
2
|
+
# We start things off by calling the CloudFormation function.
|
3
|
+
CloudFormation {
|
4
|
+
# Declare the template format version
|
5
|
+
AWSTemplateFormatVersion "2010-09-09"
|
6
|
+
|
7
|
+
# As the DSL is really ruby, we get all of the different
|
8
|
+
# ways to quote strings that come with ruby.
|
9
|
+
Description %Q/
|
10
|
+
Create a multi-az, load balanced, Auto Scaled sample web site. The
|
11
|
+
Auto Scaling trigger is based on the CPU utilization of the web
|
12
|
+
servers. The AMI is chosen based on the region in which the stack is
|
13
|
+
run. This example creates a web service running across all
|
14
|
+
availability zones in a region. The instances are load balanced with a
|
15
|
+
simple health check. The web site is available on port 80, however,
|
16
|
+
the instances can be configured to listen on any port (8888 by
|
17
|
+
default).
|
18
|
+
|
19
|
+
**WARNING** This template creates one or more Amazon EC2
|
20
|
+
instances. You will be billed for the AWS resources used if you create
|
21
|
+
a stack from this template.
|
22
|
+
/
|
23
|
+
|
24
|
+
# We can declare Parameters anywhere in the CloudFormation
|
25
|
+
# block - these will get rolled up into the Parameters container
|
26
|
+
# in the output.
|
27
|
+
Parameter("InstanceType") {
|
28
|
+
Description "Type of EC2 instance to launch"
|
29
|
+
Type "String"
|
30
|
+
Default "m1.small"
|
31
|
+
}
|
32
|
+
|
33
|
+
Parameter( "WebServerPort") {
|
34
|
+
Description "The TCP port for the Web Server"
|
35
|
+
Type "String"
|
36
|
+
Default "8888"
|
37
|
+
}
|
38
|
+
Parameter("KeyName") {
|
39
|
+
Description "The EC2 Key Pair to allow SSH access to the instances"
|
40
|
+
Type "String"
|
41
|
+
}
|
42
|
+
|
43
|
+
# The same kind of thing for parameters works for mappings,
|
44
|
+
# except that there is not a special object declared for mappings,
|
45
|
+
# so we just have to build a hash of hashes which will be turned
|
46
|
+
# directly into json.
|
47
|
+
|
48
|
+
Mapping("AWSRegionArch2AMI", {
|
49
|
+
"us-east-1" => { "32" => "ami-6411e20d", "64" => "ami-7a11e213" },
|
50
|
+
"us-west-1" => { "32" => "ami-c9c7978c", "64" => "ami-cfc7978a" },
|
51
|
+
"eu-west-1" => { "32" => "ami-37c2f643", "64" => "ami-31c2f645" },
|
52
|
+
"ap-southeast-1" => { "32" => "ami-66f28c34", "64" => "ami-60f28c32" },
|
53
|
+
"ap-northeast-1" => { "32" => "ami-9c03a89d", "64" => "ami-a003a8a1" }
|
54
|
+
})
|
55
|
+
|
56
|
+
|
57
|
+
# We can also write arbitrary ruby code
|
58
|
+
|
59
|
+
# Here we build up a ruby hash
|
60
|
+
architectures = {}
|
61
|
+
[ "t1.micro", "m1.large", "m1.xlarge", "m2.xlarge",
|
62
|
+
"m2.2xlarge", "m2.4xlarge", "c1.xlarge", "cc1.4xlarge"
|
63
|
+
].each do |arch|
|
64
|
+
# Iterate through the 64 bit machines to build a mapping for
|
65
|
+
# 64 bit architecture
|
66
|
+
architectures[arch] = { "Arch" => "64" }
|
67
|
+
end
|
68
|
+
|
69
|
+
[ "m1.small", "c1.medium"
|
70
|
+
].each do |arch|
|
71
|
+
# iterate throught he 32 bit machine to build a mapping for
|
72
|
+
# 32 bit architectures
|
73
|
+
architectures[arch] = { "Arch" => "32" }
|
74
|
+
end
|
75
|
+
|
76
|
+
Mapping("AWSInstanceType2Arch", architectures )
|
77
|
+
# This will add a mapping entry equivalent to the following to the
|
78
|
+
# template:
|
79
|
+
#
|
80
|
+
# "AWSInstanceType2Arch" : {
|
81
|
+
# "m2.4xlarge" : { "Arch" : "64" },
|
82
|
+
# "c1.xlarge" : { "Arch" : "64" },
|
83
|
+
# "c1.medium" : { "Arch" : "32" },
|
84
|
+
# "m1.xlarge" : { "Arch" : "64" },
|
85
|
+
# "m1.large" : { "Arch" : "64" },
|
86
|
+
# "t1.micro" : { "Arch" : "64" },
|
87
|
+
# "m1.small" : { "Arch" : "32" },
|
88
|
+
# "m2.2xlarge" : { "Arch" : "64" },
|
89
|
+
# "m2.xlarge" : { "Arch" : "64" },
|
90
|
+
# "cc1.4xlarge" : { "Arch" : "64" }
|
91
|
+
# }
|
92
|
+
|
93
|
+
|
94
|
+
# Resources work similar to Parameters
|
95
|
+
Resource("WebServerGroup") {
|
96
|
+
Type "AWS::AutoScaling::AutoScalingGroup"
|
97
|
+
|
98
|
+
# To call aws template defined functions, call them like
|
99
|
+
# functios (leaving out the double colons). For example
|
100
|
+
# the following:
|
101
|
+
Property("AvailabilityZones", FnGetAZs("") )
|
102
|
+
# will generate JSON that includes
|
103
|
+
# { "Fn::GetAZs : "" }
|
104
|
+
# as the value of the AvailabilityZones property for the
|
105
|
+
# "WebServerGroup" reqource.
|
106
|
+
|
107
|
+
# The same works for references
|
108
|
+
Property("LaunchConfigurationName", Ref( "LaunchConfig") )
|
109
|
+
Property("MinSize", "1")
|
110
|
+
Property("MaxSize", "3")
|
111
|
+
|
112
|
+
# If you need to set a property value to a JSON array in
|
113
|
+
# the template, you can just use a ruby array in the DSL.
|
114
|
+
Property("LoadBalancerNames", [ Ref( "ElasticLoadBalancer") ] )
|
115
|
+
|
116
|
+
}
|
117
|
+
|
118
|
+
# You can use either strings or symbols for
|
119
|
+
# Resource/Parameter/Mapping/Output names
|
120
|
+
Resource( :LaunchConfig ) {
|
121
|
+
Type "AWS::AutoScaling::LaunchConfiguration"
|
122
|
+
|
123
|
+
Property("KeyName", Ref("KeyName") )
|
124
|
+
Property( "ImageId",
|
125
|
+
FnFindInMap( "AWSRegionArch2AMI", Ref("AWS::Region"),
|
126
|
+
FnFindInMap( "AWSInstanceType2Arch", Ref("InstanceType"),"Arch")))
|
127
|
+
Property("UserData", FnBase64( Ref("WebServerPort")))
|
128
|
+
Property("SecurityGroups", [ Ref("InstanceSecurityGroup")])
|
129
|
+
Property("InstanceType", Ref("InstanceType") )
|
130
|
+
}
|
131
|
+
|
132
|
+
|
133
|
+
Resource( "WebServerScaleUpPolicy" ) {
|
134
|
+
Type "AWS::AutoScaling::ScalingPolicy"
|
135
|
+
Property("AdjustmentType", "ChangeInCapacity")
|
136
|
+
Property("AutoScalingGroupName", Ref( "WebServerGroup") )
|
137
|
+
Property("Cooldown", "60")
|
138
|
+
Property("ScalingAdjustment", "1")
|
139
|
+
}
|
140
|
+
|
141
|
+
Resource("WebServerScaleDownPolicy") {
|
142
|
+
Type "AWS::AutoScaling::ScalingPolicy"
|
143
|
+
Property("AdjustmentType", "ChangeInCapacity")
|
144
|
+
Property("AutoScalingGroupName", Ref( "WebServerGroup" ))
|
145
|
+
Property("Cooldown", "60")
|
146
|
+
Property("ScalingAdjustment", "-1")
|
147
|
+
}
|
148
|
+
|
149
|
+
|
150
|
+
# You can use ruby language constructs to keep from repeating
|
151
|
+
# yourself
|
152
|
+
|
153
|
+
# declare an aray - we are going to use it to collect some
|
154
|
+
# resources that we create
|
155
|
+
alarms = [];
|
156
|
+
|
157
|
+
# When we declare a resource with "Resource", we are
|
158
|
+
# actually calling a method on CfnDsl::CloudFormationTemplate
|
159
|
+
# that sets up the resource, and then returns it. We can use
|
160
|
+
# the return value for other means.
|
161
|
+
alarms.push Resource("CPUAlarmHigh") {
|
162
|
+
Type "AWS::CloudWatch::Alarm"
|
163
|
+
Property("AlarmDescription", "Scale-up if CPU > 90% for 10 minutes")
|
164
|
+
Property("Threshold", "90")
|
165
|
+
Property("AlarmActions", [ Ref("WebServerScaleUpPolicy" ) ])
|
166
|
+
Property("ComparisonOperator", "GreaterThanThreshold")
|
167
|
+
}
|
168
|
+
|
169
|
+
# Declare a second alarm resource and add it to our list
|
170
|
+
alarms.push Resource("CPUAlarmLow") {
|
171
|
+
Type "AWS::CloudWatch::Alarm"
|
172
|
+
Property("AlarmDescription", "Scale-down if CPU < 70% for 10 minutes")
|
173
|
+
Property("Threshold", "70")
|
174
|
+
Property("AlarmActions", [ Ref("WebServerScaleDownPolicy" ) ])
|
175
|
+
Property("ComparisonOperator", "LessThanThreshold")
|
176
|
+
}
|
177
|
+
|
178
|
+
# Ok, the alarms that we previously declared actually share a bunch
|
179
|
+
# of property declarations. Here we iterate through the alarms and
|
180
|
+
# call declare on each one, passing in a code block. This works the
|
181
|
+
# same as the declarations placed in the code blocks that went along
|
182
|
+
# with the call to Resource that was used to create the resouce above.
|
183
|
+
alarms.each do |alarm|
|
184
|
+
alarm.declare {
|
185
|
+
Property("MetricName", "CPUUtilization")
|
186
|
+
Property("Namespace", "AWS/EC2")
|
187
|
+
Property("Statistic", "Average")
|
188
|
+
Property("Period", "300")
|
189
|
+
Property("EvaluationPeriods", "2")
|
190
|
+
Property("Dimensions", [
|
191
|
+
{
|
192
|
+
"Name" => "AutoScalingGroupName",
|
193
|
+
"Value" => Ref("WebServerGroup" )
|
194
|
+
}
|
195
|
+
])
|
196
|
+
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
Resource( "ElasticLoadBalancer" ) {
|
202
|
+
Type "AWS::ElasticLoadBalancing::LoadBalancer"
|
203
|
+
Property( "AvailabilityZones", FnGetAZs(""))
|
204
|
+
Property( "Listeners" , [ {
|
205
|
+
"LoadBalancerPort" => "80",
|
206
|
+
"InstancePort" => Ref( "WebServerPort" ),
|
207
|
+
"Protocol" => "HTTP"
|
208
|
+
} ] )
|
209
|
+
Property( "HealthCheck" , {
|
210
|
+
# FnFormat replaces %0, %1, etc with passed in parameters
|
211
|
+
# Note that it renders to a call to Fn::Join in the json.
|
212
|
+
"Target" => FnFormat("HTTP:%0/", Ref( "WebServerPort" ) ),
|
213
|
+
"HealthyThreshold" => "3",
|
214
|
+
"UnhealthyThreshold" => "5",
|
215
|
+
"Interval" => "30",
|
216
|
+
"Timeout" => "5"
|
217
|
+
})
|
218
|
+
}
|
219
|
+
|
220
|
+
Resource("InstanceSecurityGroup" ) {
|
221
|
+
Type "AWS::EC2::SecurityGroup"
|
222
|
+
Property("GroupDescription" , "Enable SSH access and HTTP access on the inbound port")
|
223
|
+
Property("SecurityGroupIngress", [ {
|
224
|
+
"IpProtocol" => "tcp",
|
225
|
+
"FromPort" => "22",
|
226
|
+
"ToPort" => "22",
|
227
|
+
"CidrIp" => "0.0.0.0/0"
|
228
|
+
},
|
229
|
+
{
|
230
|
+
"IpProtocol" => "tcp",
|
231
|
+
"FromPort" => Ref( "WebServerPort" ),
|
232
|
+
"ToPort" => Ref( "WebServerPort" ),
|
233
|
+
"SourceSecurityGroupOwnerId" => FnGetAtt("ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"),
|
234
|
+
"SourceSecurityGroupName" => FnGetAtt("ElasticLoadBalancer", "SourceSecurityGroup.GroupName")
|
235
|
+
} ])
|
236
|
+
}
|
237
|
+
|
238
|
+
Output( "URL" ) {
|
239
|
+
Description "The URL of the website"
|
240
|
+
Value FnJoin( "", [ "http://", FnGetAtt( "ElasticLoadBalancer", "DNSName" ) ] )
|
241
|
+
}
|
242
|
+
|
243
|
+
}
|