vominator 0.0.1

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.
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
+