aws_pocketknife 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +1 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +115 -0
  11. data/Rakefile +22 -0
  12. data/aws_pocketknife.gemspec +40 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/cert/ca-bundle.crt +3988 -0
  16. data/exe/pocketknife +5 -0
  17. data/lib/aws_pocketknife.rb +88 -0
  18. data/lib/aws_pocketknife/admin/policies/developer_dev_acc.json +10 -0
  19. data/lib/aws_pocketknife/admin/policies/developer_prd_acc.json +15 -0
  20. data/lib/aws_pocketknife/admin/policies/tc_devops.json.erb +207 -0
  21. data/lib/aws_pocketknife/admin/policies/tester_dev_acc.json +176 -0
  22. data/lib/aws_pocketknife/admin/policies/tester_prd_acc.json +176 -0
  23. data/lib/aws_pocketknife/admin/policies/web_front_end.json.erb +59 -0
  24. data/lib/aws_pocketknife/admin/trust_relationships/ec2.json +13 -0
  25. data/lib/aws_pocketknife/asg.rb +56 -0
  26. data/lib/aws_pocketknife/cli/ami.rb +24 -0
  27. data/lib/aws_pocketknife/cli/asg.rb +40 -0
  28. data/lib/aws_pocketknife/cli/eb.rb +49 -0
  29. data/lib/aws_pocketknife/cli/ec2.rb +61 -0
  30. data/lib/aws_pocketknife/cli/elb.rb +20 -0
  31. data/lib/aws_pocketknife/cli/iam.rb +31 -0
  32. data/lib/aws_pocketknife/cli/main.rb +34 -0
  33. data/lib/aws_pocketknife/cli/rds.rb +13 -0
  34. data/lib/aws_pocketknife/cli/rds_snapshot.rb +44 -0
  35. data/lib/aws_pocketknife/cli/route53.rb +56 -0
  36. data/lib/aws_pocketknife/cloudwatch_logs.rb +25 -0
  37. data/lib/aws_pocketknife/common/logging.rb +31 -0
  38. data/lib/aws_pocketknife/common/utils.rb +63 -0
  39. data/lib/aws_pocketknife/ec2.rb +308 -0
  40. data/lib/aws_pocketknife/elastic_beanstalk.rb +62 -0
  41. data/lib/aws_pocketknife/elb.rb +25 -0
  42. data/lib/aws_pocketknife/iam.rb +135 -0
  43. data/lib/aws_pocketknife/rds.rb +84 -0
  44. data/lib/aws_pocketknife/route53.rb +234 -0
  45. data/lib/aws_pocketknife/tasks/asg.rake +18 -0
  46. data/lib/aws_pocketknife/tasks/cloudwatch.rake +12 -0
  47. data/lib/aws_pocketknife/tasks/ec2.rake +57 -0
  48. data/lib/aws_pocketknife/tasks/elastic_beanstalk.rake +25 -0
  49. data/lib/aws_pocketknife/tasks/elb.rake +13 -0
  50. data/lib/aws_pocketknife/tasks/iam.rake +57 -0
  51. data/lib/aws_pocketknife/tasks/route53.rake +64 -0
  52. data/lib/aws_pocketknife/version.rb +3 -0
  53. metadata +284 -0
@@ -0,0 +1,20 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class Elb < Thor
7
+
8
+ desc "desc ELB_NAME", "describe elastic load balancer"
9
+ def desc(elb_name)
10
+ elb = AwsPocketknife::Elb.describe_elb_by_name(name: elb_name)
11
+ if elb.nil?
12
+ puts "ELB #{elb_name} not found"
13
+ else
14
+ AwsPocketknife::Ec2.nice_print(object: elb.to_h)
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class Iam < Thor
7
+
8
+ desc "list_ssl_certs", "list ssl certs"
9
+ def list_ssl_certs
10
+ certs = AwsPocketknife::Iam.list_ssl_certificates
11
+ AwsPocketknife::Iam.nice_print(object: certs.to_h)
12
+ end
13
+
14
+ desc "create_user USERNAME", "create user"
15
+ def create_user(username)
16
+ AwsPocketknife::Iam.create_iam_user username
17
+ end
18
+
19
+ desc "create_group GROUP_NAME", "create group"
20
+ def create_group(group_name)
21
+ AwsPocketknife::Iam.create_group group_name
22
+ end
23
+
24
+ desc "add_user_to_group USERNAME GROUP_NAME", "add user to group"
25
+ def add_user_to_group(username, group_name)
26
+ AwsPocketknife::Iam.add_user_to_group username, group_name
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class Main < Thor
7
+
8
+ desc "ec2 SUBCOMMAND ...ARGS", "ec2 command lines"
9
+ subcommand "ec2", AwsPocketknife::Cli::Ec2
10
+
11
+ desc "ami SUBCOMMAND ...ARGS", "ami command lines"
12
+ subcommand "ami", AwsPocketknife::Cli::Ami
13
+
14
+ desc "eb SUBCOMMAND ...ARGS", "elastic beanstalk command lines"
15
+ subcommand "eb", AwsPocketknife::Cli::Eb
16
+
17
+ desc "route53 SUBCOMMAND ...ARGS", "route53 command lines"
18
+ subcommand "route53", AwsPocketknife::Cli::Route53
19
+
20
+ desc "iam SUBCOMMAND ...ARGS", "iam command lines"
21
+ subcommand "iam", AwsPocketknife::Cli::Iam
22
+
23
+ desc "rds SUBCOMMAND ...ARGS", "rds command lines"
24
+ subcommand "rds", AwsPocketknife::Cli::Rds
25
+
26
+ desc "asg SUBCOMMAND ...ARGS", "asg command lines"
27
+ subcommand "asg", AwsPocketknife::Cli::Asg
28
+
29
+ desc "elb SUBCOMMAND ...ARGS", "elb command lines"
30
+ subcommand "elb", AwsPocketknife::Cli::Elb
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class Rds < Thor
7
+
8
+ desc "snapshot SUBCOMMAND ...ARGS", "snapshot command lines"
9
+ subcommand "snapshot", AwsPocketknife::Cli::RdsSnapshot
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class RdsSnapshot < Thor
7
+
8
+ desc "list_snapshots DB_NAME", "list snapshots"
9
+ def list(db_name)
10
+ snapshots = AwsPocketknife::Rds.describe_snapshots(db_name: db_name)
11
+ headers = [ 'Name', 'Creation Time', 'Snapshot Type', 'Status','Port', 'Engine', 'Version', 'Storage (Gb)', 'IOPS']
12
+ data = []
13
+ snapshots.each do |h|
14
+ data << [h.db_snapshot_identifier,
15
+ h.snapshot_create_time,
16
+ h.snapshot_type,
17
+ h.status,
18
+ h.port,
19
+ h.engine,
20
+ h.engine_version,
21
+ h.allocated_storage,
22
+ h.iops
23
+ ]
24
+ end
25
+ AwsPocketknife::Rds.pretty_table(headers: headers, data: data)
26
+ end
27
+
28
+ desc "clean DB_NAME DAYS --dry_run", "Remove manual snapshots with creation time lower than DAYS for database_name."
29
+ option :dry_run, :type => :boolean, :default => true, :desc => 'just show images that would be deleted'
30
+ def clean(db_name, days)
31
+ dry_run = options.fetch("dry_run", true)
32
+ AwsPocketknife::Rds.clean_snapshots db_name: db_name,
33
+ days: days,
34
+ dry_run: dry_run
35
+ end
36
+
37
+ desc "create DB_NAME", "Creates a snapshot for database_name."
38
+ def create(db_name)
39
+ AwsPocketknife::Rds.create_snapshot db_name: db_name
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,56 @@
1
+ require "thor"
2
+ require "aws_pocketknife"
3
+
4
+ module AwsPocketknife
5
+ module Cli
6
+ class Route53 < Thor
7
+
8
+ desc "describe_hosted_zone HOSTED_ZONE", "describe hosted zone"
9
+ def describe_hosted_zone(hosted_zone)
10
+ hosted_zone = AwsPocketknife::Route53.describe_hosted_zone(hosted_zone: hosted_zone)
11
+ unless hosted_zone.nil?
12
+ AwsPocketknife::Route53.nice_print(object: hosted_zone.to_h)
13
+ else
14
+ puts "#{hosted_zone} not found"
15
+ end
16
+ end
17
+
18
+ desc "list", "list hosted zones"
19
+ def list
20
+ hosted_zones = AwsPocketknife::Route53.list_hosted_zones
21
+ headers = [ 'Name', 'Zone ID', 'Comment']
22
+ data = []
23
+ hosted_zones.each do |h|
24
+ data << [h.name,
25
+ AwsPocketknife::Route53.get_hosted_zone_id(hosted_zone: h.id),
26
+ h.config.comment]
27
+ end
28
+ AwsPocketknife::Route53.pretty_table(headers: headers, data: data)
29
+ end
30
+
31
+ desc "list_records HOSTED_ZONE", "list records for hosted zone"
32
+ def list_records(hosted_zone)
33
+ records = AwsPocketknife::Route53.list_records_for_zone_name(hosted_zone_name: hosted_zone)
34
+ headers = ["Name", "Type", "DNS Name"]
35
+ data = []
36
+ if records.length > 0
37
+ records.each do |record|
38
+ if record.type == 'CNAME'
39
+ data << [record.name, record.type, record.resource_records[0].value]
40
+ else
41
+ if record.alias_target.nil?
42
+ data << [record.name, record.type, "N/A"]
43
+ else
44
+ data << [record.name, record.type, record.alias_target.dns_name]
45
+ end
46
+ end
47
+ end
48
+ AwsPocketknife::Route53.pretty_table(headers: headers, data: data)
49
+ else
50
+ puts "No records found hosted zone #{hosted_zone}"
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,25 @@
1
+ require 'aws_pocketknife'
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'recursive-open-struct'
5
+
6
+ module AwsPocketknife
7
+ module CloudwatchLogs
8
+
9
+ class << self
10
+ include AwsPocketknife::Common::Utils
11
+
12
+ def create_log_group(log_group_name: "")
13
+
14
+ unless log_group_name.empty?
15
+ cloudwatch_logs_client.create_log_group({
16
+ log_group_name: log_group_name, # required
17
+ })
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'log4r'
2
+
3
+ module AwsPocketknife
4
+ module Common
5
+ module Logging
6
+
7
+ include Log4r
8
+ Logger = Log4r::Logger
9
+ Log4r::Logger.root.level = Log4r::INFO
10
+
11
+ class << self
12
+
13
+ def logger
14
+ @log ||= initialize_log
15
+ end
16
+
17
+ def initialize_log(name: "aws_pocketknife", pattern: "[%l] %d %m")
18
+ log = Logger.new(name)
19
+
20
+ log_format = Log4r::PatternFormatter.new(:pattern => pattern)
21
+ log_output = Log4r::StdoutOutputter.new 'console'
22
+ log_output.formatter = log_format
23
+ log.add(log_output)
24
+
25
+ return log
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ require "pretty_table"
2
+ require "awesome_print"
3
+ require_relative "logging"
4
+
5
+ module AwsPocketknife
6
+ module Common
7
+ module Utils
8
+ #include AwsPocketknife::Common::Logging
9
+
10
+ def ec2_client
11
+ @ec2_client ||= AwsPocketknife.ec2_client
12
+ end
13
+
14
+ def iam_client
15
+ @iam_client ||= AwsPocketknife.iam_client
16
+ end
17
+
18
+ def route53_client
19
+ @route53_client ||= AwsPocketknife.route53_client
20
+ end
21
+
22
+ def rds_client
23
+ @rds_client ||= AwsPocketknife.rds_client
24
+ end
25
+
26
+ def elb_client
27
+ @elb_client ||= AwsPocketknife.elb_client
28
+ end
29
+
30
+ def asg_client
31
+ @asg_client ||= AwsPocketknife.asg_client
32
+ end
33
+
34
+ def cloudwatch_logs_client
35
+ @cloudwatch_logs_client ||= AwsPocketknife.cloudwatch_logs_client
36
+ end
37
+
38
+ def elastic_beanstalk_client
39
+ @elastic_beanstalk_client ||= AwsPocketknife.elastic_beanstalk_client
40
+ end
41
+
42
+ def pretty_table(headers: [], data: [])
43
+ puts PrettyTable.new(data, headers).to_s
44
+ end
45
+
46
+ # https://github.com/michaeldv/awesome_print
47
+ def nice_print(object: nil)
48
+ ap object
49
+ end
50
+
51
+ def get_tag_value(tags: [], tag_key: "")
52
+ unless tags.empty? or tag_key.length == 0
53
+ tag = tags.select { |tag| tag.key == tag_key }
54
+ return tag[0].value if tag.length == 1
55
+ return "" if tag.length == 0
56
+ else
57
+ return ""
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,308 @@
1
+ require 'aws_pocketknife'
2
+ require 'base64'
3
+ require 'openssl'
4
+ require 'retryable'
5
+ require 'recursive-open-struct'
6
+
7
+ module AwsPocketknife
8
+ module Ec2
9
+
10
+ MAX_ATTEMPTS = 15
11
+ DELAY_SECONDS = 10
12
+
13
+ STATE_PENDING = 'pending'
14
+ STATE_AVAILABLE = 'available'
15
+ STATE_DEREGISTERED = 'deregistered'
16
+ STATE_INVALID = 'invalid'
17
+ STATE_FAILED = 'failed'
18
+ STATE_ERROR = 'error'
19
+
20
+ class << self
21
+ include AwsPocketknife::Common::Utils
22
+ #include AwsPocketknife::Common::Logging
23
+
24
+ Logging = Common::Logging.logger
25
+
26
+ def find_ami_by_name(name: '')
27
+ ec2_client.describe_images({dry_run: false,
28
+ filters: [
29
+ {
30
+ name: "tag:Name",
31
+ values: [name]
32
+ }
33
+ ]}).images
34
+ end
35
+
36
+ def find_ami_by_id(id: '')
37
+ ec2_client.describe_images({dry_run: false,
38
+ image_ids: [id]}).images.first
39
+ end
40
+
41
+ def delete_ami_by_id(id: '')
42
+ image = find_ami_by_id(id: id)
43
+ snapshot_ids = snapshot_ids(image)
44
+ ec2_client.deregister_image(image_id: id)
45
+
46
+ Retryable.retryable(:tries => 20, :sleep => lambda { |n| 2**n }, :on => StandardError) do |retries, exception|
47
+ image = find_ami_by_id(id: id)
48
+ message = "retry #{retries} - Deleting image #{id}"
49
+ message << " State: #{image.state}" if image
50
+ Logging.info message
51
+ raise StandardError unless image.nil?
52
+ end
53
+
54
+ delete_snapshots(snapshot_ids: snapshot_ids)
55
+ end
56
+
57
+ def delete_snapshots(snapshot_ids: [])
58
+ snapshot_ids.each do |snapshot_id|
59
+ Logging.info "Deleting Snapshot: #{snapshot_id}"
60
+ ec2_client.delete_snapshot(snapshot_id: snapshot_id)
61
+ end
62
+ end
63
+
64
+ def snapshot_ids(image)
65
+ snapshot_ids = []
66
+ image.block_device_mappings.each do |device_mapping|
67
+ ebs = device_mapping.ebs
68
+ snapshot_ids << ebs.snapshot_id if ebs && !ebs.snapshot_id.to_s.empty?
69
+ end
70
+ snapshot_ids
71
+ end
72
+
73
+ def clean_ami(options)
74
+ Logging.info "options: #{options}"
75
+
76
+ dry_run = options.fetch(:dry_run, true)
77
+ image_ids = find_ami_by_creation_time(options)
78
+ images_to_delete = find_unused_ami(image_ids: image_ids)
79
+
80
+ Logging.info "images (#{image_ids.length}): #{image_ids}"
81
+ Logging.info "images to delete (#{images_to_delete.length}): #{images_to_delete}"
82
+
83
+ unless dry_run
84
+ images_to_delete.each do |image_id|
85
+ Logging.info "deleting image #{image_id}"
86
+ delete_ami_by_id(id: image_id)
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ def find_unused_ami(image_ids: [])
93
+ images_to_delete = []
94
+ image_ids.each do |image_id|
95
+ # check if there is any instance using the image id
96
+ instances = describe_instances_by_image_id(image_id_list: [image_id])
97
+ if instances.empty?
98
+ images_to_delete << image_id
99
+ else
100
+ Logging.info "#{image_id} is used by instance #{instances.map { |instance| instance.instance_id }}"
101
+ end
102
+ Kernel.sleep 2
103
+ end
104
+ return images_to_delete
105
+ end
106
+
107
+ def find_ami_by_creation_time(options)
108
+
109
+ days = options.fetch(:days, '30').to_i * 24 * 3600
110
+ creation_time = Time.now-days
111
+ Logging.info "Cleaning up images older than #{days} days, i.e, with creation_time < #{creation_time})"
112
+
113
+ image_ids = []
114
+ images = find_ami_by_name(name: options.fetch(:ami_name_pattern, ''))
115
+ images.each do |image|
116
+ image_creation_time = Time.parse(image.creation_date)
117
+ msg = "image #{image.image_id} (#{creation_time}) < (image_creation_time: #{image_creation_time})? "
118
+ if creation_time <= image_creation_time
119
+ image_ids << image.image_id
120
+ msg << "YES, marking to be deleted"
121
+ else
122
+ msg << "NO"
123
+ end
124
+ Logging.info msg
125
+ end
126
+ return image_ids
127
+ end
128
+
129
+ def share_ami(image_id: '', user_id: '', options: {})
130
+ begin
131
+ options = {}
132
+ options[:image_id] = image_id
133
+ options[:launch_permission] = create_launch_permission(user_id)
134
+ Logging.info "Sharing Image #{image_id} with #{user_id} with options #{options}"
135
+ response = @ec2_client.modify_image_attribute(options=options)
136
+ return response
137
+ rescue Exception => e
138
+ Logging.error "## Got an error when sharing the image... #{e.cause} -> #{e.message}"
139
+ raise
140
+ end
141
+ end
142
+
143
+
144
+ def create_image(instance_id: "", name: "", description: "Created at #{Time.now}",
145
+ timeout: 1800, publish_to_account: "",
146
+ volume_type: "gp2",
147
+ iops: 3,
148
+ encrypted: false,
149
+ volume_size: 60
150
+ )
151
+
152
+ begin
153
+ Logging.info "creating image"
154
+ instance = find_by_id(instance_id: instance_id)
155
+ instance = ec2.instances[instance_id]
156
+ image = instance.create_image(name, :description => description)
157
+ sleep 2 until image.exists?
158
+ Logging.info "image #{image.id} state: #{image.state}"
159
+ sleep 10 until image.state != :pending
160
+ if image.state == :failed
161
+ raise "Create image failed"
162
+ end
163
+ Logging.info "image created"
164
+ rescue => e
165
+ Logging.error "Creating AMI failed #{e.message}"
166
+ Logging.error e.backtrace.join("\n")
167
+ raise e
168
+ end
169
+ if publish_to_account.length != 0
170
+ Logging.info "add permissions for #{publish_to_account}"
171
+ image.permissions.add(publish_to_account.gsub(/-/, ''))
172
+ end
173
+ image.id.tap do |image_id|
174
+ Logging.info "Image #{@name}[#{image_id}] created"
175
+ return image_id
176
+ end
177
+ end
178
+
179
+ def stop_instance_by_id(instance_ids)
180
+ instance_id_list = get_instance_id_list(instance_ids: instance_ids)
181
+ Logging.info "Stoping instance id: #{instance_id_list}"
182
+ resp = ec2_client.stop_instances({ instance_ids: instance_id_list })
183
+ wait_till_instance_is_stopped(instance_id_list, max_attempts: MAX_ATTEMPTS, delay_seconds: DELAY_SECONDS)
184
+ Logging.info "Stopped ec2 instance #{instance_id_list}"
185
+ end
186
+
187
+ def start_instance_by_id(instance_ids)
188
+ instance_id_list = get_instance_id_list(instance_ids: instance_ids)
189
+ Logging.info "Start instance id: #{instance_id_list}"
190
+ ec2_client.start_instances({ instance_ids: instance_id_list })
191
+ end
192
+
193
+ # http://serverfault.com/questions/560337/search-ec2-instance-by-its-name-from-aws-command-line-tool
194
+ def find_by_name(name: "")
195
+ instances = []
196
+ resp = ec2_client.describe_instances({dry_run: false,
197
+ filters: [
198
+ {
199
+ name: "tag:Name",
200
+ values: [name]
201
+ }
202
+ ]})
203
+ resp.reservations.each do |reservation|
204
+ reservation.instances.each do |instance|
205
+ instances << instance
206
+ end
207
+ end
208
+ instances
209
+ end
210
+
211
+ def describe_instances_by_image_id(image_id_list: [])
212
+ instances = []
213
+ resp = ec2_client.describe_instances({dry_run: false,
214
+ filters: [
215
+ {
216
+ name: "image-id",
217
+ values: image_id_list
218
+ }
219
+ ]})
220
+ resp.reservations.each do |reservation|
221
+ reservation.instances.each do |instance|
222
+ instances << instance
223
+ end
224
+ end
225
+ instances
226
+ end
227
+
228
+ def find_by_id(instance_id: "")
229
+ resp = ec2_client.describe_instances({dry_run: false, instance_ids: [instance_id.to_s]})
230
+ if resp.nil? or resp.reservations.length == 0 or resp.reservations[0].instances.length == 0
231
+ return nil
232
+ else
233
+ return resp.reservations.first.instances.first
234
+ end
235
+ end
236
+
237
+ def get_windows_password(instance_id: "")
238
+
239
+ private_keyfile_dir = ENV["AWS_POCKETKNIFE_KEYFILE_DIR"] || ""
240
+ raise "Environment variable AWS_POCKETKNIFE_KEYFILE_DIR is not defined" if private_keyfile_dir.length == 0
241
+
242
+ instance = find_by_id(instance_id: instance_id)
243
+ key_name = instance.key_name
244
+ private_keyfile = File.join(private_keyfile_dir, "#{key_name}.pem")
245
+ raise "File #{private_keyfile} not found" unless File.exist?(private_keyfile)
246
+
247
+ resp = ec2_client.get_password_data({dry_run: false,
248
+ instance_id: instance_id})
249
+ encrypted_password = resp.password_data
250
+ decrypted_password = decrypt_windows_password(encrypted_password, private_keyfile)
251
+
252
+ RecursiveOpenStruct.new({password: decrypted_password,
253
+ instance_id: instance.instance_id,
254
+ private_ip_address: instance.private_ip_address,
255
+ public_ip_address: instance.public_ip_address}, recurse_over_arrays: true)
256
+ end
257
+
258
+ # def ec2
259
+ # @ec2 ||= Aws::EC2.new(:ec2_endpoint => "ec2.#{AwsPocketknife::AWS_REGION}.amazonaws.com")
260
+ # end
261
+
262
+ private
263
+
264
+ def create_launch_permission(user_id)
265
+ {
266
+ add: [
267
+ {
268
+ user_id: user_id
269
+ },
270
+ ]
271
+ }
272
+ end
273
+
274
+ # Decrypts an encrypted password using a provided RSA
275
+ # private key file (PEM-format).
276
+ def decrypt_windows_password(encrypted_password, private_keyfile)
277
+ encrypted_password_bytes = Base64.decode64(encrypted_password)
278
+ private_keydata = File.open(private_keyfile, "r").read
279
+ private_key = OpenSSL::PKey::RSA.new(private_keydata)
280
+ private_key.private_decrypt(encrypted_password_bytes)
281
+ end
282
+
283
+ def get_instance_id_list(instance_ids: "")
284
+ instance_ids.strip.split(";")
285
+ end
286
+
287
+ def wait_till_instance_is_stopped(instance_ids, max_attempts: 12, delay_seconds: 10)
288
+ total_wait_seconds = max_attempts * delay_seconds;
289
+ Logging.info "Waiting up to #{total_wait_seconds} seconds with #{delay_seconds} seconds delay for ec2 instance #{instance_ids} to be stopped"
290
+ ec2_client.wait_until(:instance_stopped, { instance_ids: instance_ids }) do |w|
291
+ w.max_attempts = max_attempts
292
+ w.delay = delay_seconds
293
+ end
294
+ end
295
+
296
+ def wait_till_instance_is_terminated(instance_ids, max_attempts: 12, delay_seconds: 10)
297
+ total_wait_seconds = max_attempts * delay_seconds;
298
+ Logging.info "Waiting up to #{total_wait_seconds} seconds with #{delay_seconds} seconds delay for ec2 instance #{instance_ids} to be terminated"
299
+ ec2_client.wait_until(:instance_terminated, { instance_ids: instance_ids }) do |w|
300
+ w.max_attempts = max_attempts
301
+ w.delay = delay_seconds
302
+ end
303
+ end
304
+
305
+ end
306
+
307
+ end
308
+ end