vominator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +5 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +116 -0
  7. data/Rakefile +12 -0
  8. data/bin/vominate +12 -0
  9. data/circle.yml +5 -0
  10. data/lib/ec2.rb +12 -0
  11. data/lib/ec2/instances.rb +362 -0
  12. data/lib/ec2/security_groups.rb +314 -0
  13. data/lib/ec2/ssm.rb +81 -0
  14. data/lib/vominator/aws.rb +15 -0
  15. data/lib/vominator/constants.rb +53 -0
  16. data/lib/vominator/ec2.rb +308 -0
  17. data/lib/vominator/instances.rb +34 -0
  18. data/lib/vominator/route53.rb +125 -0
  19. data/lib/vominator/security_groups.rb +26 -0
  20. data/lib/vominator/ssm.rb +57 -0
  21. data/lib/vominator/version.rb +3 -0
  22. data/lib/vominator/vominator.rb +74 -0
  23. data/lib/vominator/vpc.rb +82 -0
  24. data/lib/vpc.rb +8 -0
  25. data/lib/vpc/create.rb +188 -0
  26. data/spec/lib/instances_spec.rb +2 -0
  27. data/spec/lib/vominator/aws_spec.rb +6 -0
  28. data/spec/lib/vominator/ec2_spec.rb +783 -0
  29. data/spec/lib/vominator/instances_spec.rb +96 -0
  30. data/spec/lib/vominator/route53_spec.rb +64 -0
  31. data/spec/lib/vominator/ssm_spec.rb +95 -0
  32. data/spec/lib/vominator/vominator_spec.rb +209 -0
  33. data/spec/spec_helper.rb +103 -0
  34. data/spec/support/matchers/exit_with_code.rb +24 -0
  35. data/test/puke/cloud-configs/.gitkeep +0 -0
  36. data/test/puke/cloud-configs/cloud-config-example.erb +63 -0
  37. data/test/puke/config.yaml +16 -0
  38. data/test/puke/products/sample-api/instances.yaml +37 -0
  39. data/test/puke/products/sample-api/security_groups.yaml +19 -0
  40. data/test/vominator.yaml +7 -0
  41. data/vominator.gemspec +34 -0
  42. metadata +259 -0
@@ -0,0 +1,314 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'colored'
4
+ require 'terminal-table'
5
+ require_relative '../vominator/constants'
6
+ require_relative '../vominator/aws'
7
+ require_relative '../vominator/security_groups'
8
+ require_relative '../vominator/ec2'
9
+
10
+ options = {}
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = 'Usage: vominate ec2 security_groups [options]'.yellow
14
+
15
+ opts.on('-p PRODUCT', '--product PRODUCT', String, 'REQUIRED: The product which you want to manage security groups for') do |value|
16
+ options[:product] = value
17
+ end
18
+
19
+ opts.on('-e ENVIRONMENT', '--environment ENVIRONMENT', String, 'REQUIRED: The environment which you want to manage security groups for') do |value|
20
+ options[:environment] = value
21
+ end
22
+
23
+ opts.on('--security-groups GROUPS', Array, 'OPTIONAL: Comma Delimited list of security groups') do |value|
24
+ options[:groups] = value
25
+ end
26
+
27
+ opts.on('--delete', 'Enable Deletions. This should be used with care') do |value|
28
+ options[:delete] = value
29
+ end
30
+ opts.on('-t', '--test', 'OPTIONAL: Test run. Show what would be changed without making any actual changes') do
31
+ options[:test] = true
32
+ end
33
+
34
+ opts.on('-l', '--list', 'OPTIONAL: List out products and environments') do
35
+ options[:list] = true
36
+ end
37
+
38
+ opts.on('--verbose', 'OPTIONAL: Show all security group rules in tables') do
39
+ options[:verbose] = true
40
+ end
41
+
42
+ opts.on('-d', '--debug', 'OPTIONAL: debug output') do
43
+ options[:debug] = true
44
+ end
45
+
46
+ opts.on_tail(:NONE, '-h', '--help', 'OPTIONAL: Display this screen') do
47
+ puts opts
48
+ exit
49
+ end
50
+
51
+ begin
52
+ opts.parse!
53
+ throw Exception unless ((options.include? :environment) && (options.include? :product)) || options[:list]
54
+ rescue
55
+ puts opts
56
+ exit
57
+ end
58
+ end
59
+
60
+ TEST = options[:test]
61
+ def test?(message)
62
+ LOGGER.test(message) if TEST
63
+ TEST
64
+ end
65
+
66
+
67
+ if options[:list]
68
+ data = {}
69
+ PUKE_CONFIG.keys.each do |environment|
70
+ LOGGER.info "--#{environment}"
71
+ products = PUKE_CONFIG[environment]['products'] || Array.new
72
+ products.each do |product|
73
+ LOGGER.info " --#{product}"
74
+ end
75
+ end
76
+ exit(1)
77
+ end
78
+
79
+ puke_config = Vominator.get_puke_variables(options[:environment])
80
+
81
+ #TODO: Validate Environment and Product
82
+ LOGGER.info("Working on #{options[:product]} in #{options[:environment]}.")
83
+
84
+
85
+ unless test?('Vominator is running in test mode. It will NOT make any changes.')
86
+ LOGGER.warning('WARNING: Vominator will make changes to your environment. Please run test mode first if you are unsure.')
87
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
88
+ exit(1)
89
+ end
90
+ end
91
+
92
+ if options[:groups]
93
+ puke_security_groups = Vominator::SecurityGroups.get_security_groups(options[:environment], options[:product], options[:groups])
94
+ # If the user specified a filter, barf in the event a specified security group doesnt exist in puke
95
+ invalid_security_groups = options[:groups].reject{|g| puke_security_group_names.include? g}
96
+ if invalid_security_groups.count > 0
97
+ LOGGER.fatal("Unable to find the following security groups in your puke: #{invalid_security_groups.join(',')}")
98
+ end
99
+ else
100
+ puke_security_groups = Vominator::SecurityGroups.get_security_groups(options[:environment], options[:product])
101
+ end
102
+
103
+ unless puke_security_groups
104
+ LOGGER.fatal('Unable to load security groups . Make sure the product is correctly defined for the environment you have selected and that a security_groups.yaml file exists with at least one group defined.')
105
+ end
106
+
107
+ ec2_client = Aws::EC2::Client.new(region: puke_config['region_name'])
108
+
109
+
110
+ puke_security_group_names = puke_security_groups.map{|g| g.keys[0]}
111
+ vpc_security_groups = Vominator::EC2.get_security_groups(ec2_client, puke_config['vpc_id'])
112
+ vpc_security_group_names = vpc_security_groups.map{|g| g.group_name }
113
+
114
+ new_security_group_names = puke_security_group_names.reject{|g| vpc_security_group_names.include? g}
115
+ if new_security_group_names.count > 0
116
+ unless test?("Would create the following new security groups: #{new_security_group_names.join(',')}")
117
+ new_security_group_names.each do |security_group_name|
118
+ #TODO: Automagically nuke the default outbound ACL for each security group
119
+ description = puke_security_groups.select{ |g| g.keys[0] == security_group_name}.first['description']
120
+ LOGGER.success("Successfully created #{security_group_name}") if Vominator::EC2.create_security_group(ec2_client, security_group_name, puke_config['vpc_id'], description)
121
+ end
122
+ # Sleep for just a second to allow amazon to converge
123
+ sleep(1)
124
+ end
125
+ end
126
+
127
+ untracked_security_group_names = vpc_security_group_names.reject{|g| puke_security_group_names.include? g}
128
+ if untracked_security_group_names.count > 0
129
+ LOGGER.warning("The following security groups exist in the AWS account but are not defined in your puke: #{untracked_security_group_names.join(',')}")
130
+ end
131
+
132
+ # Refresh our list of existing security groups now that we have created ones we need.
133
+ vpc_security_groups = Vominator::EC2.get_security_groups(ec2_client, puke_config['vpc_id'])
134
+ vpc_security_groups_id_lookup = Hash[vpc_security_groups.map{|g| [g.group_name, g.group_id]}]
135
+ vpc_security_groups_name_lookup = Hash[vpc_security_groups.map{|g| [g.group_id, g.group_name]}]
136
+
137
+ puke_security_groups.each do |puke_security_group|
138
+ puke_security_group_name = puke_security_group.keys[0]
139
+ vpc_security_group = vpc_security_groups.select{|g| g.group_name == puke_security_group_name}.first
140
+
141
+ # Create empty arrays if we havent specified either ingress or egress rules.
142
+ puke_security_group['ingress'] = [] unless puke_security_group['ingress'] && puke_security_group['ingress'].count > 0
143
+ puke_security_group['egress'] = [] unless puke_security_group['egress'] && puke_security_group['egress'].count > 0
144
+
145
+ if vpc_security_group
146
+ # Update environment tag if needed
147
+ environment_tag = vpc_security_group.tags.select{|t| t.key == 'Environment'}.first
148
+
149
+ environment_tag = nil if environment_tag.value != options[:environment] if environment_tag
150
+
151
+ unless environment_tag
152
+ unless test?("Would set environment tag to #{options[:environment]} for #{puke_security_group_name}")
153
+ Vominator::EC2.tag_resource(ec2_client, vpc_security_group.group_id, [{key: 'Environment', value: options[:environment]}])
154
+ LOGGER.info("Updated tags for #{puke_security_group_name}")
155
+ end
156
+ end
157
+
158
+ # Normalize the existing ingress rules for the security group.
159
+ vpc_ingress_rules = Array.new
160
+ vpc_security_group.ip_permissions.each do |rule|
161
+ #TODO: Normalize -1 to all for ip_protocol
162
+ #TODO: if -1 for ip_protocol set :from_port and to_ports
163
+
164
+ rule.ip_ranges.each do |ip_range|
165
+ vpc_ingress_rules.push({ :ip_protocol => rule.ip_protocol, :from_port => rule.from_port, :to_port => rule.to_port, :cidr_ip => ip_range.cidr_ip, :source_security_group_id => nil })
166
+ end
167
+
168
+ rule.user_id_group_pairs.each do |group|
169
+ vpc_ingress_rules.push({ :ip_protocol => rule.ip_protocol, :from_port => rule.from_port, :to_port => rule.to_port, :cidr_ip => nil, :source_security_group_id => group.group_id, :source_security_group_name => vpc_security_groups_name_lookup[group.group_id] })
170
+ end
171
+ end
172
+
173
+ # Normalize the rules that we defined in puke for the security group.
174
+ puke_ingress_rules = Array.new
175
+ cidr_block_regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/
176
+
177
+ puke_security_group['ingress'].each do |rule|
178
+ #TODO: Normalize all to -1 for ip_protocol
179
+ if rule['ports'].to_s.include?('..')
180
+ from_port = rule['ports'].split('..')[0]
181
+ to_port = rule['ports'].split('..')[1]
182
+ else
183
+ from_port = rule['ports']
184
+ to_port = rule['ports']
185
+ end
186
+
187
+ if rule['source'] =~ cidr_block_regex
188
+ puke_ingress_rules.push({ :ip_protocol => rule['protocol'], :from_port => from_port.to_i, :to_port => to_port.to_i, :cidr_ip => rule['source'], :source_security_group_id => nil})
189
+ else rule['source']
190
+ if vpc_security_groups_id_lookup[puke_security_group_name]
191
+ group_id = vpc_security_groups_id_lookup[puke_security_group_name]
192
+ puke_ingress_rules.push({ :ip_protocol => rule['protocol'], :from_port => from_port.to_i, :to_port => to_port.to_i, :cidr_ip => nil, :source_security_group_id => group_id, :source_security_group_name => vpc_security_groups_name_lookup[group_id] })
193
+ else
194
+ LOGGER.fatal("Do not recognize #{rule['source']} as a valid cidr block and was unable to resolve this to a valid security group for #{rule} in #{puke_security_group_name}")
195
+ end
196
+ end
197
+ end
198
+
199
+ # Determie what new rules we need to add
200
+ ingress_to_create = puke_ingress_rules - vpc_ingress_rules
201
+ # Determine what rules we should delete
202
+ ingress_to_delete = vpc_ingress_rules - puke_ingress_rules
203
+
204
+ # Normalize the existing egress rules for the security group
205
+ vpc_egress_rules = Array.new
206
+ vpc_security_group.ip_permissions_egress.each do |rule|
207
+ #TODO: Normalize -1 to all for ip_protocol
208
+ #TODO: if -1 for ip_protocol set :from_port and to_ports
209
+ rule.ip_ranges.each do |ip_range|
210
+ vpc_egress_rules.push({ :ip_protocol => rule.ip_protocol, :from_port => rule.from_port, :to_port => rule.to_port, :cidr_ip => ip_range.cidr_ip, :source_security_group_id => nil })
211
+ end
212
+
213
+ rule.user_id_group_pairs.each do |group|
214
+ vpc_egress_rules.push({ :ip_protocol => rule.ip_protocol, :from_port => rule.from_port, :to_port => rule.to_port, :cidr_ip => nil, :source_security_group_id => group.group_id, :source_security_group_name => vpc_security_groups_name_lookup[group.group_id] })
215
+ end
216
+ end
217
+
218
+ # Normalize the rules that we defined in puke for the security group.
219
+ puke_egress_rules = Array.new
220
+ cidr_block_regex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/
221
+
222
+ puke_security_group['egress'].each do |rule|
223
+ #TODO: Normalize all to -1 for ip_protocol
224
+ if rule['ports'].to_s.include?('..')
225
+ from_port = rule['ports'].split('..')[0]
226
+ to_port = rule['ports'].split('..')[1]
227
+ else
228
+ from_port = rule['ports']
229
+ to_port = rule['ports']
230
+ end
231
+
232
+ if rule['source'] =~ cidr_block_regex
233
+ puke_egress_rules.push({ :ip_protocol => rule['protocol'], :from_port => from_port.to_i, :to_port => to_port.to_i, :cidr_ip => rule['source'], :source_security_group_id => nil})
234
+ else rule['source']
235
+ if vpc_security_groups_id_lookup[puke_security_group_name]
236
+ group_id = vpc_security_groups_id_lookup[puke_security_group_name]
237
+ puke_egress_rules.push({ :ip_protocol => rule['protocol'], :from_port => from_port.to_i, :to_port => to_port.to_i, :cidr_ip => nil, :source_security_group_id => group_id, :source_security_group_name => vpc_security_groups_name_lookup[group_id] })
238
+ else
239
+ LOGGER.fatal("Do not recognize #{rule['source']} as a valid cidr block and was unable to resolve this to a valid security group for #{rule} in #{puke_security_group_name}")
240
+ end
241
+ end
242
+ end
243
+
244
+ # Determine what new rules we need to add
245
+ egress_to_create = puke_egress_rules - vpc_egress_rules
246
+ # Determine what rules we should delete
247
+ egress_to_delete = vpc_egress_rules - puke_egress_rules
248
+
249
+ table = Terminal::Table.new :title => puke_security_group_name.cyan, :headings => ['Type', 'Source', 'from port', 'to_port', 'Protocol', 'Action'], :style => {:width => 125} do |t|
250
+ ingress_to_create.each do |rule|
251
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
252
+ t.add_row ['Inbound'.green, source.green, rule[:from_port].to_s.green, rule[:to_port].to_s.green, rule[:ip_protocol].green, 'Create'.green]
253
+ end
254
+
255
+ ingress_to_delete.each do |rule|
256
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
257
+ t.add_row ['Inbound'.red, source.red, rule[:from_port].to_s.red, rule[:to_port].to_s.red, rule[:ip_protocol].red, 'Delete'.red]
258
+ end
259
+
260
+ if options[:verbose]
261
+ ((vpc_ingress_rules - ingress_to_create) - ingress_to_delete).each do |rule|
262
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
263
+ t.add_row ['Inbound', source, rule[:from_port].to_s, rule[:to_port].to_s, rule[:ip_protocol], nil]
264
+ end
265
+ end
266
+
267
+ egress_to_create.each do |rule|
268
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
269
+ t.add_row ['Outbound'.green, source.green, rule[:from_port].to_s.green, rule[:to_port].to_s.green, rule[:ip_protocol].green, 'Create'.green]
270
+ end
271
+
272
+ egress_to_delete.each do |rule|
273
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
274
+ t.add_row ['Outbound'.red, source.red, rule[:from_port].to_s.red, rule[:to_port].to_s.red, rule[:ip_protocol].red, 'Delete'.red]
275
+ end
276
+
277
+ if options[:verbose]
278
+ ((vpc_egress_rules - egress_to_create) - egress_to_delete).each do |rule|
279
+ source = rule[:cidr_ip] || rule[:source_security_group_name]
280
+ t.add_row ['Outbound', source, rule[:from_port].to_s, rule[:to_port].to_s, rule[:ip_protocol], nil]
281
+ end
282
+ end
283
+ end
284
+
285
+
286
+ LOGGER.info(table)
287
+
288
+ #TODO: Maybe find a way to cleanup the display output better on success
289
+ unless options[:test]
290
+ ingress_to_create.each do |rule|
291
+ Vominator::EC2.create_security_group_rule(ec2_client,'ingress',vpc_security_group.group_id,rule)
292
+ LOGGER.success("Added inbound rule to #{vpc_security_group.group_name}: #{rule}")
293
+ end
294
+
295
+
296
+ egress_to_create.each do |rule|
297
+ Vominator::EC2.create_security_group_rule(ec2_client,'egress',vpc_security_group.group_id,rule)
298
+ LOGGER.success("Added outbound rule to #{vpc_security_group.group_name}: #{rule}")
299
+ end
300
+
301
+ if options[:delete]
302
+ ingress_to_delete.each do |rule|
303
+ Vominator::EC2.delete_security_group_rule(ec2_client,'ingress',vpc_security_group.group_id,rule)
304
+ LOGGER.success("Removed inbound rule to #{vpc_security_group.group_name}: #{rule}")
305
+ end
306
+
307
+ egress_to_delete.each do |rule|
308
+ Vominator::EC2.delete_security_group_rule(ec2_client,'egress',vpc_security_group.group_id,rule)
309
+ LOGGER.success("Removed outbound rule to #{vpc_security_group.group_name}: #{rule}")
310
+ end
311
+ end
312
+ end
313
+ end
314
+ end
data/lib/ec2/ssm.rb ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+ require 'colored'
4
+ require_relative '../vominator/constants'
5
+ require_relative '../vominator/aws'
6
+ require_relative '../vominator/ssm'
7
+
8
+ options = {}
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = 'Usage: vominate ssm [options]'.yellow
12
+
13
+ opts.on('-eENVIRONMENT', '--environment ENVIRONMENT', 'REQUIRED: The environment which you want to manage ssm for') do |value|
14
+ options[:environment] = value
15
+ end
16
+
17
+ opts.on('-t', '--test', 'OPTIONAL: Test run. Show what would be changed without making any actual changes') do
18
+ options[:test] = true
19
+ end
20
+
21
+ opts.on('-l', '--list', 'OPTIONAL: List out documents for a specific environment') do
22
+ options[:list] = true
23
+ end
24
+
25
+ opts.on('-d', '--debug', 'OPTIONAL: debug output') do
26
+ options[:debug] = true
27
+ end
28
+
29
+ opts.on_tail(:NONE, '-h', '--help', 'OPTIONAL: Display this screen') do
30
+ puts opts
31
+ exit
32
+ end
33
+
34
+ begin
35
+ opts.parse!
36
+ throw Exception unless (options.include? :environment) || options[:list]
37
+ rescue
38
+ puts opts
39
+ exit
40
+ end
41
+ end
42
+
43
+ TEST = options[:test]
44
+ def test?(message)
45
+ LOGGER.test(message) if TEST
46
+ TEST
47
+ end
48
+
49
+ puke_config = Vominator.get_puke_variables(options[:environment])
50
+
51
+ #TODO: Validate Environment
52
+ LOGGER.info("Working on #{options[:environment]}.")
53
+
54
+ unless test?('Vominator is running in test mode. It will NOT make any changes.')
55
+ LOGGER.warning('WARNING: Vominator will make changes to your environment. Please run test mode first if you are unsure.')
56
+ unless Vominator.yesno?(prompt: 'Do you wish to proceed?', default: false)
57
+ exit(1)
58
+ end
59
+ end
60
+
61
+ ssm = Aws::SSM::Client.new(region: puke_config['region_name'])
62
+
63
+ aws_documents = Vominator::SSM.get_documents(ssm)
64
+
65
+ puke_config['ssm_documents'].each do |document_name|
66
+ document_data = File.read("#{VOMINATOR_CONFIG['configuration_path']}/ssm-documents/#{document_name}")
67
+
68
+ if aws_documents.include? document_name
69
+ #should we delete it?
70
+
71
+ #should we update it?
72
+ else
73
+ unless test?("Would create SSM document for #{document_name}")
74
+ if Vominator::SSM.put_document(ssm,document_name,document_data)
75
+ LOGGER.success("Succesfully created SSM document #{document_name}")
76
+ else
77
+ LOGGER.fatal("Failed to create SSM document #{document_name}")
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ require 'aws-sdk'
2
+ require_relative 'vominator'
3
+ require_relative 'constants'
4
+
5
+ Aws.config[:credentials] = Aws::Credentials.new(VOMINATOR_CONFIG['access_key_id'], VOMINATOR_CONFIG['secret_access_key'])
6
+
7
+ module Vominator
8
+ class AWS
9
+ def self.get_availability_zones(ec2_client)
10
+ resp = ec2_client.describe_availability_zones
11
+ zones = resp.availability_zones.map{|z| z['zone_name']}.sort_by{|z| z}
12
+ return zones
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,53 @@
1
+ require_relative 'vominator'
2
+
3
+ LOGGER = Vominator::Logger
4
+
5
+ VOMINATOR_CONFIG ||= Vominator.get_config
6
+ PUKE_CONFIG ||= Vominator.get_puke_config(VOMINATOR_CONFIG['configuration_path'])
7
+
8
+ EC2_INSTANCE_METADATA = {
9
+ :'t1.micro' => {:ephemeral_devices => 0, :virtualization_type => 'paravirtual'},
10
+ :'t2.micro' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
11
+ :'t2.small' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
12
+ :'t2.medium' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
13
+ :'m1.small' => {:ephemeral_devices => 1, :virtualization_type => 'paravirtual'},
14
+ :'m1.medium' => {:ephemeral_devices => 1, :virtualization_type => 'paravirtual'},
15
+ :'m1.large' => {:ephemeral_devices => 2, :virtualization_type => 'paravirtual'},
16
+ :'m1.xlarge' => {:ephemeral_devices => 4, :virtualization_type => 'paravirtual'},
17
+ :'m3.medium' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
18
+ :'m3.large' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
19
+ :'m3.xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
20
+ :'m3.2xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
21
+ :'c1.medium' => {:ephemeral_devices => 1, :virtualization_type => 'paravirtual'},
22
+ :'c1.xlarge' => {:ephemeral_devices => 4, :virtualization_type => 'paravirtual'},
23
+ :'m2.xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'paravirtual'},
24
+ :'m2.2xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'paravirtual'},
25
+ :'m2.4xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'paravirtual'},
26
+ :'hi1.4xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
27
+ :'hs1.8xlarge' => {:ephemeral_devices => 24, :virtualization_type => 'hvm'},
28
+ :'cr1.8xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
29
+ :'cc1.4xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
30
+ :'cc2.8xlarge' => {:ephemeral_devices => 4, :virtualization_type => 'hvm'},
31
+ :'cg1.4xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
32
+ :'c3.large' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
33
+ :'c3.xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
34
+ :'c3.2xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
35
+ :'c3.4xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
36
+ :'c3.8xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
37
+ :'c4.large' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
38
+ :'c4.xlarge' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
39
+ :'c4.2xlarge' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
40
+ :'c4.4xlarge' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
41
+ :'c4.8xlarge' => {:ephemeral_devices => 0, :virtualization_type => 'hvm'},
42
+ :'g2.2xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
43
+ :'i2.xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
44
+ :'i2.2xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
45
+ :'i2.4xlarge' => {:ephemeral_devices => 4, :virtualization_type => 'hvm'},
46
+ :'i2.8xlarge' => {:ephemeral_devices => 8, :virtualization_type => 'hvm'},
47
+ :'r3.large' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
48
+ :'r3.xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
49
+ :'r3.2xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
50
+ :'r3.4xlarge' => {:ephemeral_devices => 1, :virtualization_type => 'hvm'},
51
+ :'r3.8xlarge' => {:ephemeral_devices => 2, :virtualization_type => 'hvm'},
52
+ }
53
+