cfndsl 0.1.16 → 0.1.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/stevenjack/cfndsl.png?branch=master)](https://travis-ci.org/stevenjack/cfndsl)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/cfndsl.png)](http://badge.fury.io/rb/cfndsl)
|
6
|
+
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](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
|
+
}
|