cardtapp-cloudformation-ruby-dsl 0.0.1.pre.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/OWNERS +3 -0
- data/PULL_REQUEST_TEMPLATE.md +30 -0
- data/README.md +128 -0
- data/Rakefile +9 -0
- data/bin/cfntemplate-to-ruby +347 -0
- data/cloudformation-ruby-dsl.gemspec +46 -0
- data/docs/Contributing.md +22 -0
- data/docs/Releasing.md +20 -0
- data/examples/cloudformation-ruby-script.rb +274 -0
- data/examples/maps/domains.txt +4 -0
- data/examples/maps/map.json +9 -0
- data/examples/maps/map.rb +5 -0
- data/examples/maps/map.yaml +5 -0
- data/examples/maps/more_maps/map1.json +8 -0
- data/examples/maps/more_maps/map2.json +8 -0
- data/examples/maps/more_maps/map3.json +8 -0
- data/examples/maps/table.txt +5 -0
- data/examples/maps/vpc.txt +25 -0
- data/examples/simple_template.rb +21 -0
- data/examples/userdata.sh +4 -0
- data/initial_contributions.md +5 -0
- data/lib/cloudformation-ruby-dsl/cfntemplate.rb +731 -0
- data/lib/cloudformation-ruby-dsl/dsl.rb +380 -0
- data/lib/cloudformation-ruby-dsl/spotprice.rb +50 -0
- data/lib/cloudformation-ruby-dsl/table.rb +123 -0
- data/lib/cloudformation-ruby-dsl/version.rb +21 -0
- data/lib/cloudformation-ruby-dsl.rb +2 -0
- data/spec/spec_helper.rb +161 -0
- data/spec/validation_spec.rb +26 -0
- metadata +234 -0
@@ -0,0 +1,274 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2013-2014 Bazaarvoice, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'bundler/setup'
|
18
|
+
require 'cloudformation-ruby-dsl/cfntemplate'
|
19
|
+
require 'cloudformation-ruby-dsl/table'
|
20
|
+
|
21
|
+
# Note: this is only intended to demonstrate the cloudformation-ruby-dsl. It compiles
|
22
|
+
# and validates correctly, but won't produce a viable CloudFormation stack.
|
23
|
+
|
24
|
+
template do
|
25
|
+
|
26
|
+
# Metadata may be embedded into the stack, as per http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html
|
27
|
+
# The below definition produces CloudFormation interface metadata.
|
28
|
+
metadata 'AWS::CloudFormation::Interface', {
|
29
|
+
:ParameterGroups => [
|
30
|
+
{
|
31
|
+
:Label => { :default => 'Instance options' },
|
32
|
+
:Parameters => [ 'InstanceType', 'ImageId', 'KeyPairName' ]
|
33
|
+
},
|
34
|
+
{
|
35
|
+
:Label => { :default => 'Other options' },
|
36
|
+
:Parameters => [ 'Label', 'EmailAddress' ]
|
37
|
+
}
|
38
|
+
],
|
39
|
+
:ParameterLabels => {
|
40
|
+
:EmailAddress => {
|
41
|
+
:default => "We value your privacy!"
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
parameter 'Label',
|
47
|
+
:Description => 'The label to apply to the servers.',
|
48
|
+
:Type => 'String',
|
49
|
+
:MinLength => '2',
|
50
|
+
:MaxLength => '25',
|
51
|
+
:AllowedPattern => '[_a-zA-Z0-9]*',
|
52
|
+
:ConstraintDescription => 'Maximum length of the Label parameter may not exceed 25 characters and may only contain letters, numbers and underscores.',
|
53
|
+
# The :Immutable attribute is a Ruby CFN extension. It affects the behavior of the '<template> update ...'
|
54
|
+
# operation in that a stack update may not change the values of parameters marked w/:Immutable => true.
|
55
|
+
:Immutable => true
|
56
|
+
|
57
|
+
parameter 'InstanceType',
|
58
|
+
:Description => 'EC2 instance type',
|
59
|
+
:Type => 'String',
|
60
|
+
:Default => 'm2.xlarge',
|
61
|
+
:AllowedValues => %w(t1.micro m1.small m1.medium m1.large m1.xlarge m2.xlarge m2.2xlarge m2.4xlarge c1.medium c1.xlarge),
|
62
|
+
:ConstraintDescription => 'Must be a valid EC2 instance type.'
|
63
|
+
|
64
|
+
parameter 'ImageId',
|
65
|
+
:Description => 'EC2 Image ID',
|
66
|
+
:Type => 'String',
|
67
|
+
:Default => 'ami-255bbc4c',
|
68
|
+
:AllowedPattern => 'ami-[a-f0-9]{8}',
|
69
|
+
:ConstraintDescription => 'Must be ami-XXXXXXXX (where X is a hexadecimal digit)'
|
70
|
+
|
71
|
+
parameter 'KeyPairName',
|
72
|
+
:Description => 'Name of KeyPair to use.',
|
73
|
+
:Type => 'String',
|
74
|
+
:MinLength => '1',
|
75
|
+
:MaxLength => '40',
|
76
|
+
:Default => parameters['Label']
|
77
|
+
|
78
|
+
parameter 'EmailAddress',
|
79
|
+
:Type => 'String',
|
80
|
+
:Description => 'Email address at which to send notification events.'
|
81
|
+
|
82
|
+
parameter 'BucketName',
|
83
|
+
:Type => 'String',
|
84
|
+
:Description => 'Name of the bucket to upload to.'
|
85
|
+
|
86
|
+
mapping 'InlineExampleMap',
|
87
|
+
:team1 => {
|
88
|
+
:name => 'test1',
|
89
|
+
:email => 'test1@example.com',
|
90
|
+
},
|
91
|
+
:team2 => {
|
92
|
+
:name => 'test2',
|
93
|
+
:email => 'test2@example.com',
|
94
|
+
}
|
95
|
+
|
96
|
+
# Generates mappings from external files with various formats.
|
97
|
+
mapping 'JsonExampleMap', 'maps/map.json'
|
98
|
+
|
99
|
+
mapping 'RubyExampleMap', 'maps/map.rb'
|
100
|
+
|
101
|
+
mapping 'YamlExampleMap', 'maps/map.yaml'
|
102
|
+
|
103
|
+
# Loads JSON mappings dynamically from example directory.
|
104
|
+
Dir.entries('maps/more_maps').each_with_index do |path, index|
|
105
|
+
next if path == "." or path == ".."
|
106
|
+
mapping "ExampleMap#{index - 1}", "maps/more_maps/#{path}"
|
107
|
+
end
|
108
|
+
|
109
|
+
# Selects all rows in the table which match the name/value pairs of the predicate object and returns a
|
110
|
+
# set of nested maps, where the key for the map at level n is the key at index n in the specified keys,
|
111
|
+
# except for the last key in the specified keys which is used to determine the value of the leaf-level map.
|
112
|
+
text = Table.load 'maps/table.txt'
|
113
|
+
mapping 'TableExampleMap',
|
114
|
+
text.get_map({ :column0 => 'foo' }, :column1, :column2, :column3)
|
115
|
+
|
116
|
+
# Shows how to create a table useful for looking up subnets that correspond to a particular env/region for eg. vpc placement.
|
117
|
+
vpc = Table.load 'maps/vpc.txt'
|
118
|
+
mapping 'TableExampleMultimap',
|
119
|
+
vpc.get_multimap({ :visibility => 'private', :zone => ['a', 'c'] }, :env, :region, :subnet)
|
120
|
+
|
121
|
+
# Shows how to use a table for iterative processing.
|
122
|
+
domains = Table.load 'maps/domains.txt'
|
123
|
+
domains.get_multihash(:purpose, {:product => 'demo', :alias => 'true'}, :prefix, :target, :alias_hosted_zone_id).each_pair do |key, value|
|
124
|
+
resource key+'Route53RecordSet', :Type => 'AWS::Route53::RecordSet', :Properties => {
|
125
|
+
:Comment => '',
|
126
|
+
:HostedZoneName => 'bazaarvoice.com',
|
127
|
+
:Name => value[:prefix]+'.bazaarvoice.com',
|
128
|
+
:Type => 'A',
|
129
|
+
:AliasTarget => {
|
130
|
+
:DNSName => value[:target],
|
131
|
+
:HostedZoneId => value[:alias_hosted_zone_id]
|
132
|
+
}
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
# The tag type is a DSL extension; it is not a property of actual CloudFormation templates.
|
138
|
+
# These tags are excised from the template and used to generate a series of --tag arguments
|
139
|
+
# which are passed to CloudFormation when a stack is created.
|
140
|
+
# They do not ultimately appear in the expanded CloudFormation template.
|
141
|
+
# The diff subcommand will compare tags with the running stack and identify any changes,
|
142
|
+
# but a stack update will do the diff and throw an error on any immutable tags update attempt.
|
143
|
+
# The tags are propagated to all resources created by the stack, including the stack itself.
|
144
|
+
# If a resource has its own tag with the same name as CF's it's not overwritten.
|
145
|
+
#
|
146
|
+
# Amazon has set the following restrictions on CloudFormation tags:
|
147
|
+
# => limit 10
|
148
|
+
# CloudFormation tags declaration examples:
|
149
|
+
|
150
|
+
tag 'My:New:Tag',
|
151
|
+
:Value => 'ImmutableTagValue',
|
152
|
+
:Immutable => true
|
153
|
+
|
154
|
+
tag :MyOtherTag,
|
155
|
+
:Value => 'My Value With Spaces'
|
156
|
+
|
157
|
+
tag(:"tag:name", :Value => 'tag_value', :Immutable => true)
|
158
|
+
|
159
|
+
# Following format is deprecated and not advised. Please declare CloudFormation tags as described above.
|
160
|
+
tag :TagName => 'tag_value' # It's immutable.
|
161
|
+
|
162
|
+
resource 'SecurityGroup', :Type => 'AWS::EC2::SecurityGroup', :Properties => {
|
163
|
+
:GroupDescription => 'Lets any vpc traffic in.',
|
164
|
+
:SecurityGroupIngress => {:IpProtocol => '-1', :FromPort => '0', :ToPort => '65535', :CidrIp => "10.0.0.0/8"}
|
165
|
+
}
|
166
|
+
|
167
|
+
resource "ASG", :Type => 'AWS::AutoScaling::AutoScalingGroup', :Properties => {
|
168
|
+
:AvailabilityZones => 'us-east-1',
|
169
|
+
:HealthCheckType => 'EC2',
|
170
|
+
:LaunchConfigurationName => ref('LaunchConfig'),
|
171
|
+
:MinSize => 1,
|
172
|
+
:MaxSize => 5,
|
173
|
+
:NotificationConfiguration => {
|
174
|
+
:TopicARN => ref('EmailSNSTopic'),
|
175
|
+
:NotificationTypes => %w(autoscaling:EC2_INSTANCE_LAUNCH autoscaling:EC2_INSTANCE_LAUNCH_ERROR autoscaling:EC2_INSTANCE_TERMINATE autoscaling:EC2_INSTANCE_TERMINATE_ERROR),
|
176
|
+
},
|
177
|
+
:Tags => [
|
178
|
+
{
|
179
|
+
:Key => 'Name',
|
180
|
+
# Grabs a value in an external map file.
|
181
|
+
:Value => find_in_map('TableExampleMap', 'corge', 'grault'),
|
182
|
+
:PropagateAtLaunch => 'true',
|
183
|
+
},
|
184
|
+
{
|
185
|
+
:Key => 'Label',
|
186
|
+
:Value => parameters['Label'],
|
187
|
+
:PropagateAtLaunch => 'true',
|
188
|
+
}
|
189
|
+
],
|
190
|
+
}
|
191
|
+
|
192
|
+
resource 'EmailSNSTopic', :Type => 'AWS::SNS::Topic', :Properties => {
|
193
|
+
:Subscription => [
|
194
|
+
{
|
195
|
+
:Endpoint => ref('EmailAddress'),
|
196
|
+
:Protocol => 'email',
|
197
|
+
},
|
198
|
+
],
|
199
|
+
}
|
200
|
+
|
201
|
+
resource 'WaitConditionHandle', :Type => 'AWS::CloudFormation::WaitConditionHandle', :Properties => {}
|
202
|
+
|
203
|
+
resource 'WaitCondition', :Type => 'AWS::CloudFormation::WaitCondition', :DependsOn => 'ASG', :Properties => {
|
204
|
+
:Handle => ref('WaitConditionHandle'),
|
205
|
+
:Timeout => 1200,
|
206
|
+
:Count => "1"
|
207
|
+
}
|
208
|
+
|
209
|
+
resource 'LaunchConfig', :Type => 'AWS::AutoScaling::LaunchConfiguration', :Properties => {
|
210
|
+
:ImageId => parameters['ImageId'],
|
211
|
+
:KeyName => ref('KeyPairName'),
|
212
|
+
:IamInstanceProfile => ref('InstanceProfile'),
|
213
|
+
:InstanceType => ref('InstanceType'),
|
214
|
+
:InstanceMonitoring => 'false',
|
215
|
+
:SecurityGroups => [ref('SecurityGroup')],
|
216
|
+
:BlockDeviceMappings => [
|
217
|
+
{:DeviceName => '/dev/sdb', :VirtualName => 'ephemeral0'},
|
218
|
+
{:DeviceName => '/dev/sdc', :VirtualName => 'ephemeral1'},
|
219
|
+
{:DeviceName => '/dev/sdd', :VirtualName => 'ephemeral2'},
|
220
|
+
{:DeviceName => '/dev/sde', :VirtualName => 'ephemeral3'},
|
221
|
+
],
|
222
|
+
# Loads an external userdata script with an interpolated argument.
|
223
|
+
:UserData => base64(interpolate(file('userdata.sh'), time: Time.now)),
|
224
|
+
}
|
225
|
+
|
226
|
+
resource 'InstanceProfile', :Type => 'AWS::IAM::InstanceProfile', :Properties => {
|
227
|
+
# use cfn intrinsic conditional to choose the 2nd value because the expression evaluates to false
|
228
|
+
:Path => fn_if(equal(3, 0), '/unselected/', '/'),
|
229
|
+
:Roles => [ ref('InstanceRole') ],
|
230
|
+
}
|
231
|
+
|
232
|
+
resource 'InstanceRole', :Type => 'AWS::IAM::Role', :Properties => {
|
233
|
+
:AssumeRolePolicyDocument => {
|
234
|
+
:Statement => [
|
235
|
+
{
|
236
|
+
:Effect => 'Allow',
|
237
|
+
:Principal => { :Service => [ 'ec2.amazonaws.com' ] },
|
238
|
+
:Action => [ 'sts:AssumeRole' ],
|
239
|
+
},
|
240
|
+
],
|
241
|
+
},
|
242
|
+
:Path => '/',
|
243
|
+
}
|
244
|
+
|
245
|
+
# Use sub to set bucket names for an S3 Policy.
|
246
|
+
resource 'ManagedPolicy', :Type => "AWS::IAM::ManagedPolicy", :Properties => {
|
247
|
+
:Description => 'Access policy for S3 Buckets',
|
248
|
+
:PolicyDocument => {
|
249
|
+
:Version => "2012-10-17",
|
250
|
+
:Statement => [
|
251
|
+
{
|
252
|
+
:Action => ["s3:ListBucket"],
|
253
|
+
:Effect => "Allow",
|
254
|
+
:Resource => [
|
255
|
+
sub("arn:aws:s3:::${BucketName}"),
|
256
|
+
sub("arn:aws:s3:::${BaseName}${Hash}", {:BaseName => 'Bucket', :Hash => '3bsd73w'}),
|
257
|
+
]
|
258
|
+
}
|
259
|
+
]
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|
263
|
+
# add conditions that can be used elsewhere in the template
|
264
|
+
condition 'myCondition', fn_and(equal("one", "two"), not_equal("three", "four"))
|
265
|
+
|
266
|
+
output 'EmailSNSTopicARN',
|
267
|
+
:Value => ref('EmailSNSTopic'),
|
268
|
+
:Description => 'ARN of SNS Topic used to send emails on events.'
|
269
|
+
|
270
|
+
output 'MappingLookup',
|
271
|
+
:Value => find_in_map('TableExampleMap', 'corge', 'grault'),
|
272
|
+
:Description => 'An example map lookup.'
|
273
|
+
|
274
|
+
end.exec!
|
@@ -0,0 +1,4 @@
|
|
1
|
+
purpose product prefix target alias alias_hosted_zone_id has_origin_domain origin_target
|
2
|
+
CDN demo code dabc123.cloudfront.net true Z2FDTNDATAQYW2 false
|
3
|
+
Assets demo assets ddef456.cloudfront.net true Z2FDTNDATAQYW2 false
|
4
|
+
API demo api prod-api false false prod-api-origin.whatever.net.
|
@@ -0,0 +1,25 @@
|
|
1
|
+
env region zone visibility subnet
|
2
|
+
qa us-east-1 a public subnet-aaaa
|
3
|
+
qa us-east-1 a private subnet-bbbb
|
4
|
+
qa us-east-1 b public subnet-cccc
|
5
|
+
qa us-east-1 b private subnet-dddd
|
6
|
+
qa us-east-1 c public subnet-eeee
|
7
|
+
qa us-east-1 c private subnet-ffff
|
8
|
+
qa eu-west-1 a public subnet-gggg
|
9
|
+
qa eu-west-1 a private subnet-hhhh
|
10
|
+
qa eu-west-1 b public subnet-iiii
|
11
|
+
qa eu-west-1 b private subnet-jjjj
|
12
|
+
qa eu-west-1 c public subnet-kkkk
|
13
|
+
qa eu-west-1 c private subnet-llll
|
14
|
+
prod us-east-1 a public subnet-mmmm
|
15
|
+
prod us-east-1 a private subnet-nnnn
|
16
|
+
prod us-east-1 b public subnet-oooo
|
17
|
+
prod us-east-1 b private subnet-pppp
|
18
|
+
prod us-east-1 c public subnet-qqqq
|
19
|
+
prod us-east-1 c private subnet-rrrr
|
20
|
+
prod eu-west-1 a public subnet-ssss
|
21
|
+
prod eu-west-1 a private subnet-tttt
|
22
|
+
prod eu-west-1 b public subnet-uuuu
|
23
|
+
prod eu-west-1 b private subnet-vvvv
|
24
|
+
prod eu-west-1 c public subnet-wwww
|
25
|
+
prod eu-west-1 c private subnet-xxxx
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'cloudformation-ruby-dsl/cfntemplate'
|
4
|
+
|
5
|
+
tmpl = template do
|
6
|
+
@stack_name = 'hello-bucket-example'
|
7
|
+
|
8
|
+
parameter 'Label',
|
9
|
+
:Description => 'The label to apply to the bucket.',
|
10
|
+
:Type => 'String',
|
11
|
+
:Default => 'cfnrdsl',
|
12
|
+
:UsePreviousValue => true
|
13
|
+
|
14
|
+
resource "HelloBucket",
|
15
|
+
:Type => 'AWS::S3::Bucket',
|
16
|
+
:Properties => {
|
17
|
+
:BucketName => ref('Label')
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
tmpl.exec!
|
@@ -0,0 +1,5 @@
|
|
1
|
+
Several people were instrumental in building this project during its incubation stage. Because the process of open sourcing involved squashing the repository to remove confidential information, some of their contributions have been lost from the history that Github displays.
|
2
|
+
|
3
|
+
- [Shawn Smith](https://github.com/shawnsmith): initial implementation
|
4
|
+
- [Nathaniel Eliot](https://github.com/temujin9): converted from raw library to gem, refactored table code, cleaned and prepared code for open sourcing
|
5
|
+
- [Jona Fenocchi](http://github.com/jonaf): added support for CloudFormation tags
|