cloud-mu 1.9.0.pre.beta → 2.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/Berksfile +16 -54
  3. data/Berksfile.lock +14 -62
  4. data/bin/mu-aws-setup +131 -108
  5. data/bin/mu-configure +311 -74
  6. data/bin/mu-gcp-setup +84 -62
  7. data/bin/mu-load-config.rb +46 -2
  8. data/bin/mu-self-update +11 -9
  9. data/bin/mu-upload-chef-artifacts +4 -4
  10. data/{mu.gemspec → cloud-mu.gemspec} +2 -2
  11. data/cookbooks/awscli/Berksfile +8 -0
  12. data/cookbooks/mu-activedirectory/Berksfile +11 -0
  13. data/cookbooks/mu-firewall/Berksfile +9 -0
  14. data/cookbooks/mu-firewall/metadata.rb +1 -1
  15. data/cookbooks/mu-glusterfs/Berksfile +10 -0
  16. data/cookbooks/mu-jenkins/Berksfile +14 -0
  17. data/cookbooks/mu-master/Berksfile +23 -0
  18. data/cookbooks/mu-master/attributes/default.rb +1 -1
  19. data/cookbooks/mu-master/metadata.rb +2 -2
  20. data/cookbooks/mu-master/recipes/default.rb +1 -1
  21. data/cookbooks/mu-master/recipes/init.rb +7 -3
  22. data/cookbooks/mu-master/recipes/ssl-certs.rb +1 -0
  23. data/cookbooks/mu-mongo/Berksfile +10 -0
  24. data/cookbooks/mu-openvpn/Berksfile +11 -0
  25. data/cookbooks/mu-php54/Berksfile +13 -0
  26. data/cookbooks/mu-splunk/Berksfile +10 -0
  27. data/cookbooks/mu-tools/Berksfile +21 -0
  28. data/cookbooks/mu-tools/files/default/Mu_CA.pem +15 -15
  29. data/cookbooks/mu-utility/Berksfile +9 -0
  30. data/cookbooks/mu-utility/metadata.rb +2 -1
  31. data/cookbooks/nagios/Berksfile +7 -4
  32. data/cookbooks/s3fs/Berksfile +9 -0
  33. data/environments/dev.json +6 -6
  34. data/environments/prod.json +6 -6
  35. data/modules/mu.rb +20 -42
  36. data/modules/mu/cleanup.rb +102 -100
  37. data/modules/mu/cloud.rb +90 -28
  38. data/modules/mu/clouds/aws.rb +449 -218
  39. data/modules/mu/clouds/aws/alarm.rb +29 -17
  40. data/modules/mu/clouds/aws/cache_cluster.rb +78 -64
  41. data/modules/mu/clouds/aws/collection.rb +25 -18
  42. data/modules/mu/clouds/aws/container_cluster.rb +73 -66
  43. data/modules/mu/clouds/aws/database.rb +124 -116
  44. data/modules/mu/clouds/aws/dnszone.rb +27 -20
  45. data/modules/mu/clouds/aws/firewall_rule.rb +30 -22
  46. data/modules/mu/clouds/aws/folder.rb +18 -3
  47. data/modules/mu/clouds/aws/function.rb +77 -23
  48. data/modules/mu/clouds/aws/group.rb +19 -12
  49. data/modules/mu/clouds/aws/habitat.rb +153 -0
  50. data/modules/mu/clouds/aws/loadbalancer.rb +59 -52
  51. data/modules/mu/clouds/aws/log.rb +30 -23
  52. data/modules/mu/clouds/aws/msg_queue.rb +29 -20
  53. data/modules/mu/clouds/aws/notifier.rb +222 -0
  54. data/modules/mu/clouds/aws/role.rb +178 -90
  55. data/modules/mu/clouds/aws/search_domain.rb +40 -24
  56. data/modules/mu/clouds/aws/server.rb +169 -137
  57. data/modules/mu/clouds/aws/server_pool.rb +60 -83
  58. data/modules/mu/clouds/aws/storage_pool.rb +59 -31
  59. data/modules/mu/clouds/aws/user.rb +36 -27
  60. data/modules/mu/clouds/aws/userdata/linux.erb +101 -93
  61. data/modules/mu/clouds/aws/vpc.rb +250 -189
  62. data/modules/mu/clouds/azure.rb +132 -0
  63. data/modules/mu/clouds/cloudformation.rb +65 -1
  64. data/modules/mu/clouds/cloudformation/alarm.rb +8 -0
  65. data/modules/mu/clouds/cloudformation/cache_cluster.rb +7 -0
  66. data/modules/mu/clouds/cloudformation/collection.rb +7 -0
  67. data/modules/mu/clouds/cloudformation/database.rb +7 -0
  68. data/modules/mu/clouds/cloudformation/dnszone.rb +7 -0
  69. data/modules/mu/clouds/cloudformation/firewall_rule.rb +9 -2
  70. data/modules/mu/clouds/cloudformation/loadbalancer.rb +7 -0
  71. data/modules/mu/clouds/cloudformation/log.rb +7 -0
  72. data/modules/mu/clouds/cloudformation/server.rb +7 -0
  73. data/modules/mu/clouds/cloudformation/server_pool.rb +7 -0
  74. data/modules/mu/clouds/cloudformation/vpc.rb +7 -0
  75. data/modules/mu/clouds/google.rb +214 -110
  76. data/modules/mu/clouds/google/container_cluster.rb +42 -24
  77. data/modules/mu/clouds/google/database.rb +15 -6
  78. data/modules/mu/clouds/google/firewall_rule.rb +17 -25
  79. data/modules/mu/clouds/google/group.rb +13 -5
  80. data/modules/mu/clouds/google/habitat.rb +105 -0
  81. data/modules/mu/clouds/google/loadbalancer.rb +28 -20
  82. data/modules/mu/clouds/google/server.rb +93 -354
  83. data/modules/mu/clouds/google/server_pool.rb +18 -10
  84. data/modules/mu/clouds/google/user.rb +22 -14
  85. data/modules/mu/clouds/google/vpc.rb +97 -69
  86. data/modules/mu/config.rb +133 -38
  87. data/modules/mu/config/alarm.rb +25 -0
  88. data/modules/mu/config/cache_cluster.rb +5 -3
  89. data/modules/mu/config/cache_cluster.yml +23 -0
  90. data/modules/mu/config/database.rb +25 -16
  91. data/modules/mu/config/database.yml +3 -3
  92. data/modules/mu/config/function.rb +1 -2
  93. data/modules/mu/config/{project.rb → habitat.rb} +10 -10
  94. data/modules/mu/config/notifier.rb +85 -0
  95. data/modules/mu/config/notifier.yml +9 -0
  96. data/modules/mu/config/role.rb +1 -1
  97. data/modules/mu/config/search_domain.yml +2 -2
  98. data/modules/mu/config/server.rb +13 -1
  99. data/modules/mu/config/server.yml +3 -3
  100. data/modules/mu/config/server_pool.rb +3 -1
  101. data/modules/mu/config/storage_pool.rb +3 -1
  102. data/modules/mu/config/storage_pool.yml +19 -0
  103. data/modules/mu/config/vpc.rb +70 -8
  104. data/modules/mu/groomers/chef.rb +2 -3
  105. data/modules/mu/kittens.rb +500 -122
  106. data/modules/mu/master.rb +5 -5
  107. data/modules/mu/mommacat.rb +151 -91
  108. data/modules/tests/super_complex_bok.yml +12 -0
  109. data/modules/tests/super_simple_bok.yml +12 -0
  110. data/spec/mu/clouds/azure_spec.rb +82 -0
  111. data/spec/spec_helper.rb +105 -0
  112. metadata +26 -5
  113. data/modules/mu/clouds/aws/notification.rb +0 -139
  114. data/modules/mu/config/notification.rb +0 -44
@@ -39,7 +39,7 @@ module MU
39
39
  @config['iam_policies'].each { |policy|
40
40
  policy_name = @mu_name+"-"+policy.keys.first.upcase
41
41
  MU.log "Creating IAM policy #{policy_name}"
42
- resp = MU::Cloud::AWS.iam.create_policy(
42
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
43
43
  policy_name: policy_name,
44
44
  path: "/"+@deploy.deploy_id+"/",
45
45
  policy_document: JSON.generate(policy.values.first),
@@ -50,7 +50,7 @@ module MU
50
50
 
51
51
  if !@config['bare_policies']
52
52
  MU.log "Creating IAM role #{@mu_name}"
53
- resp = MU::Cloud::AWS.iam.create_role(
53
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
54
54
  path: "/"+@deploy.deploy_id+"/",
55
55
  role_name: @mu_name,
56
56
  description: "Generated by Mu",
@@ -62,14 +62,13 @@ module MU
62
62
 
63
63
  # Called automatically by {MU::Deploy#createResources}
64
64
  def groom
65
-
66
65
  if @config['policies']
67
66
  @config['iam_policies'] ||= []
68
67
  @config['iam_policies'].concat(convert_policies_to_iam)
69
68
  end
70
69
 
71
70
  if !@config['bare_policies']
72
- resp = MU::Cloud::AWS.iam.get_role(
71
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role(
73
72
  role_name: @mu_name
74
73
  ).role
75
74
  ext_tags = resp.tags.map { |t| t.to_h }
@@ -78,64 +77,74 @@ module MU
78
77
 
79
78
  if tag_param.size > 0
80
79
  MU.log "Updating tags on IAM role #{@mu_name}", MU::NOTICE, details: tag_param
81
- MU::Cloud::AWS.iam.tag_role(role_name: @mu_name, tags: tag_param)
80
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).tag_role(role_name: @mu_name, tags: tag_param)
82
81
  end
83
82
  end
84
83
 
85
- if @config['iam_policies']
84
+
85
+ if @config['iam_policies'] or @config['import']
86
86
  attached_policies = []
87
- configured_policies = @config['iam_policies'].map { |p|
88
- @mu_name+"-"+p.keys.first.upcase
89
- }
87
+ configured_policies = []
88
+
89
+ if @config['iam_policies']
90
+ configured_policies = @config['iam_policies'].map { |p|
91
+ @mu_name+"-"+p.keys.first.upcase
92
+ }
93
+ end
94
+
90
95
  if @config['import']
96
+ MU.log "Attaching canned #{@config['import'].size > 1 ? "policies" : "policy"} #{@config['import'].join(", ")} to role #{@mu_name}", MU::NOTICE, details: @config['credentials']
91
97
  configured_policies.concat(@config['import'].map { |p| p.gsub(/.*?\/([^:\/]+)$/, '\1') })
92
98
  end
93
99
 
94
100
  if !@config['bare_policies']
95
- attached_policies = MU::Cloud::AWS.iam.list_attached_role_policies(
101
+ attached_policies = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_role_policies(
96
102
  role_name: @mu_name
97
103
  ).attached_policies
98
104
  attached_policies.each { |a|
99
105
  if !configured_policies.include?(a.policy_name)
100
106
  MU.log "Removing IAM policy #{a.policy_name} from role #{@mu_name}", MU::NOTICE
101
- MU::Cloud::AWS::Role.purgePolicy(a.policy_arn)
107
+ MU::Cloud::AWS::Role.purgePolicy(a.policy_arn, @config['credentials'])
102
108
  end
103
109
  }
104
110
  end
105
111
 
106
- @config['iam_policies'].each { |policy|
107
- policy_name = @mu_name+"-"+policy.keys.first.upcase
112
+ if @config['iam_policies']
113
+ @config['iam_policies'].each { |policy|
114
+ policy_name = @mu_name+"-"+policy.keys.first.upcase
108
115
 
109
- arn = "arn:"+(MU::Cloud::AWS.isGovCloud? ? "aws-us-gov" : "aws")+":iam::"+MU.account_number+":policy/#{@deploy.deploy_id}/#{policy_name}"
110
- resp = begin
111
- desc = MU::Cloud::AWS.iam.get_policy(policy_arn: arn)
116
+ arn = "arn:"+(MU::Cloud::AWS.isGovCloud? ? "aws-us-gov" : "aws")+":iam::"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":policy/#{@deploy.deploy_id}/#{policy_name}"
117
+ resp = begin
118
+ desc = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy(policy_arn: arn)
112
119
 
113
- version = MU::Cloud::AWS.iam.get_policy_version(
114
- policy_arn: arn,
115
- version_id: desc.policy.default_version_id
116
- )
117
- if version.policy_version.document != URI.encode(JSON.generate(policy.values.first), /[^a-z0-9\-]/i)
118
- MU.log "Updating IAM policy #{policy_name}", MU::NOTICE, details: policy.values.first
119
- update_policy(arn, policy.values.first)
120
- MU::Cloud::AWS.iam.get_policy(policy_arn: arn)
121
- else
122
- desc
123
- end
120
+ version = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy_version(
121
+ policy_arn: arn,
122
+ version_id: desc.policy.default_version_id
123
+ )
124
+ if version.policy_version.document != URI.encode(JSON.generate(policy.values.first), /[^a-z0-9\-]/i)
125
+ MU.log "Updating IAM policy #{policy_name}", MU::NOTICE, details: policy.values.first
126
+ update_policy(arn, policy.values.first)
127
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy(policy_arn: arn)
128
+ else
129
+ desc
130
+ end
124
131
 
125
- rescue Aws::IAM::Errors::NoSuchEntity => e
126
- MU.log "Creating IAM policy #{policy_name}", details: policy.values.first
127
- MU::Cloud::AWS.iam.create_policy(
128
- policy_name: policy_name,
129
- path: "/"+@deploy.deploy_id+"/",
130
- policy_document: JSON.generate(policy.values.first),
131
- description: "Generated from inline policy document for Mu role #{@mu_name}"
132
- )
133
- end
132
+ rescue Aws::IAM::Errors::NoSuchEntity => e
133
+ MU.log "Creating IAM policy #{policy_name}", details: policy.values.first
134
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
135
+ policy_name: policy_name,
136
+ path: "/"+@deploy.deploy_id+"/",
137
+ policy_document: JSON.generate(policy.values.first),
138
+ description: "Generated from inline policy document for Mu role #{@mu_name}"
139
+ )
140
+ end
134
141
 
135
- }
142
+ }
143
+ end
136
144
  end
137
145
 
138
- if !@config['bare_policies'] and @config['iam_policies']
146
+ if !@config['bare_policies'] and
147
+ (@config['iam_policies'] or @config['import'])
139
148
  bindTo("role", @mu_name)
140
149
  end
141
150
  end
@@ -158,22 +167,22 @@ module MU
158
167
  def cloud_desc
159
168
  desc = {}
160
169
  if @config['bare_policies']
161
- desc["policies"] = MU::Cloud::AWS.iam.list_policies(
170
+ desc["policies"] = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_policies(
162
171
  path_prefix: "/"+MU.deploy_id+"/"
163
172
  ).policies
164
173
  desc["policies"].reject! { |p|
165
174
  !p.policy_name.match(/^#{Regexp.quote(@mu_name)}-/)
166
175
  }
167
176
  else
168
- desc["role"] = MU::Cloud::AWS.iam.get_role(
177
+ desc["role"] = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role(
169
178
  role_name: @mu_name
170
179
  ).role
171
180
  if @config['iam_policies']
172
181
  desc["policies"] = []
173
- MU::Cloud::AWS.iam.list_attached_role_policies(
182
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_role_policies(
174
183
  role_name: @mu_name
175
184
  ).attached_policies.each { |p|
176
- desc["policies"] << MU::Cloud::AWS.iam.get_policy(
185
+ desc["policies"] << MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy(
177
186
  policy_arn: p.policy_arn
178
187
  ).policy
179
188
  }
@@ -201,7 +210,7 @@ module MU
201
210
  my_policies = cloud_desc["policies"]
202
211
  my_policies.each { |p|
203
212
  if p.policy_name == policy
204
- old = MU::Cloud::AWS.iam.get_policy_version(
213
+ old = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy_version(
205
214
  policy_arn: p.arn,
206
215
  version_id: p.default_version_id
207
216
  ).policy_version
@@ -209,9 +218,17 @@ module MU
209
218
  need_update = false
210
219
  doc["Statement"].each { |s|
211
220
  targets.each { |target|
212
- # XXX resolve mu_type targets to ARNs
213
- if !s["Resource"].include?(target)
214
- s["Resource"] << target
221
+ targetstr = if target['type']
222
+ sibling = @deploy.findLitterMate(
223
+ name: target["identifier"],
224
+ type: target["type"]
225
+ )
226
+ sibling.cloudobj.arn
227
+ else
228
+ target['identifier']
229
+ end
230
+ if sibling and !s["Resource"].include?(targetstr)
231
+ s["Resource"] << targetstr
215
232
  need_update = true
216
233
  end
217
234
  }
@@ -226,34 +243,34 @@ module MU
226
243
 
227
244
  # Delete an IAM policy, along with attendant versions and attachments.
228
245
  # @param policy_arn [String]: The ARN of the policy to purge
229
- def self.purgePolicy(policy_arn)
230
- attachments = MU::Cloud::AWS.iam.list_entities_for_policy(
246
+ def self.purgePolicy(policy_arn, credentials)
247
+ attachments = MU::Cloud::AWS.iam(credentials: credentials).list_entities_for_policy(
231
248
  policy_arn: policy_arn
232
249
  )
233
250
  attachments.policy_users.each { |u|
234
- MU::Cloud::AWS.iam.detach_user_policy(
251
+ MU::Cloud::AWS.iam(credentials: credentials).detach_user_policy(
235
252
  user_name: u.user_name,
236
253
  policy_arn: policy_arn
237
254
  )
238
255
  }
239
256
  attachments.policy_groups.each { |g|
240
- MU::Cloud::AWS.iam.detach_group_policy(
257
+ MU::Cloud::AWS.iam(credentials: credentials).detach_group_policy(
241
258
  group_name: g.group_name,
242
259
  policy_arn: policy_arn
243
260
  )
244
261
  }
245
262
  attachments.policy_roles.each { |r|
246
- MU::Cloud::AWS.iam.detach_role_policy(
263
+ MU::Cloud::AWS.iam(credentials: credentials).detach_role_policy(
247
264
  role_name: r.role_name,
248
265
  policy_arn: policy_arn
249
266
  )
250
267
  }
251
- versions = MU::Cloud::AWS.iam.list_policy_versions(
268
+ versions = MU::Cloud::AWS.iam(credentials: credentials).list_policy_versions(
252
269
  policy_arn: policy_arn,
253
270
  ).versions
254
271
  versions.each { |v|
255
272
  next if v.is_default_version
256
- MU::Cloud::AWS.iam.delete_policy_version(
273
+ MU::Cloud::AWS.iam(credentials: credentials).delete_policy_version(
257
274
  policy_arn: policy_arn,
258
275
  version_id: v.version_id
259
276
  )
@@ -262,51 +279,69 @@ module MU
262
279
  # Delete the policy, unless it's one of the global canned ones owned
263
280
  # by AWS
264
281
  if !policy_arn.match(/^arn:aws:iam::aws:/)
265
- MU::Cloud::AWS.iam.delete_policy(
282
+ MU::Cloud::AWS.iam(credentials: credentials).delete_policy(
266
283
  policy_arn: policy_arn
267
284
  )
268
285
  end
269
286
  end
270
287
 
288
+ # Does this resource type exist as a global (cloud-wide) artifact, or
289
+ # is it localized to a region/zone?
290
+ # @return [Boolean]
291
+ def self.isGlobal?
292
+ true
293
+ end
294
+
271
295
  # Remove all roles associated with the currently loaded deployment.
272
296
  # @param noop [Boolean]: If true, will only print what would be done
273
297
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
274
298
  # @param region [String]: The cloud provider region
275
299
  # @return [void]
276
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
300
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
277
301
 
278
- resp = MU::Cloud::AWS.iam.list_policies(
302
+ resp = MU::Cloud::AWS.iam(credentials: credentials).list_policies(
279
303
  path_prefix: "/"+MU.deploy_id+"/"
280
304
  )
281
305
  if resp and resp.policies
282
306
  resp.policies.each { |policy|
283
307
  MU.log "Deleting IAM policy /#{MU.deploy_id}/#{policy.policy_name}"
284
308
  if !noop
285
- purgePolicy(policy.arn)
309
+ purgePolicy(policy.arn, credentials)
286
310
  end
287
311
  }
288
312
  end
289
313
 
290
- resp = MU::Cloud::AWS.iam.list_roles(
314
+ resp = MU::Cloud::AWS.iam(credentials: credentials).list_roles(
291
315
  path_prefix: "/"+MU.deploy_id+"/"
292
316
  )
293
317
  if resp and resp.roles
294
318
  resp.roles.each { |r|
295
319
  MU.log "Deleting IAM role #{r.role_name}"
296
320
  if !noop
321
+ # purgePolicy won't touch roles we don't own, so gently detach
322
+ # those first
323
+ detachables = MU::Cloud::AWS.iam(credentials: credentials).list_attached_role_policies(
324
+ role_name: r.role_name
325
+ ).attached_policies
326
+ detachables.each { |rp|
327
+ MU::Cloud::AWS.iam(credentials: credentials).detach_role_policy(
328
+ role_name: r.role_name,
329
+ policy_arn: rp.policy_arn
330
+ )
331
+ }
297
332
 
298
333
  begin
299
- MU::Cloud::AWS.iam.remove_role_from_instance_profile(
334
+ MU::Cloud::AWS.iam(credentials: credentials).remove_role_from_instance_profile(
300
335
  instance_profile_name: r.role_name,
301
336
  role_name: r.role_name
302
337
  )
303
- MU::Cloud::AWS.iam.delete_instance_profile(instance_profile_name: r.role_name)
338
+ MU::Cloud::AWS.iam(credentials: credentials).delete_instance_profile(instance_profile_name: r.role_name)
304
339
  rescue Aws::IAM::Errors::ValidationError => e
305
340
  MU.log "Cleaning up IAM role #{r.role_name}: #{e.inspect}", MU::WARN
306
341
  rescue Aws::IAM::Errors::NoSuchEntity => e
307
342
  end
308
343
 
309
- MU::Cloud::AWS.iam.delete_role(
344
+ MU::Cloud::AWS.iam(credentials: credentials).delete_role(
310
345
  role_name: r.role_name
311
346
  )
312
347
  end
@@ -320,7 +355,7 @@ module MU
320
355
  # @param region [String]: The cloud provider region.
321
356
  # @param flags [Hash]: Optional flags
322
357
  # @return [OpenStruct]: The cloud provider's complete descriptions of matching user group.
323
- def self.find(cloud_id: nil, region: MU.curRegion, flags: {})
358
+ def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
324
359
  found = nil
325
360
 
326
361
  found
@@ -331,13 +366,22 @@ module MU
331
366
  def bindTo(entitytype, entityname)
332
367
  if entitytype == "instance_profile"
333
368
  begin
334
- MU::Cloud::AWS.iam.add_role_to_instance_profile(
335
- instance_profile_name: entityname,
336
- role_name: @mu_name
337
- )
369
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
370
+ instance_profile_name: entityname
371
+ ).instance_profile
372
+
373
+ if !resp.roles.map { |r| r.role_name}.include?(@mu_name)
374
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).add_role_to_instance_profile(
375
+ instance_profile_name: entityname,
376
+ role_name: @mu_name
377
+ )
378
+ end
379
+ rescue Exception => e
380
+ MU.log "Error binding role #{@mu_name} to instance profile #{entityname}: #{e.message}", MU::ERR
381
+ raise e
338
382
  end
339
383
  elsif ["user", "group", "role"].include?(entitytype)
340
- mypolicies = MU::Cloud::AWS.iam.list_policies(
384
+ mypolicies = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_policies(
341
385
  path_prefix: "/"+@deploy.deploy_id+"/"
342
386
  ).policies
343
387
  mypolicies.reject! { |p|
@@ -347,47 +391,57 @@ module MU
347
391
  if @config['import']
348
392
  @config['import'].each { |policy|
349
393
  if !policy.match(/^arn:/i)
350
- policy = "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/"+policy
394
+ p_arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/"+policy
395
+ end
396
+ retried = false
397
+ begin
398
+ mypolicies << MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy(
399
+ policy_arn: p_arn
400
+ ).policy
401
+ rescue Aws::IAM::Errors::NoSuchEntity => e
402
+ if !retried
403
+ p_arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/service-role/"+policy
404
+ retried = true
405
+ retry
406
+ end
407
+ raise e
351
408
  end
352
- mypolicies << MU::Cloud::AWS.iam.get_policy(
353
- policy_arn: policy
354
- ).policy
355
409
  }
356
410
  end
357
411
 
358
412
  mypolicies.each { |p|
359
413
  if entitytype == "user"
360
- resp = MU::Cloud::AWS.iam.list_attached_user_policies(
414
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_user_policies(
361
415
  path_prefix: "/"+@deploy.deploy_id+"/",
362
416
  user_name: entityname
363
417
  )
364
418
  if !resp or !resp.attached_policies.map { |p| p.policy_name }.include?(p.policy_name)
365
419
  MU.log "Attaching IAM policy #{p.policy_name} to user #{entityname}", MU::NOTICE
366
- MU::Cloud::AWS.iam.attach_user_policy(
420
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_user_policy(
367
421
  policy_arn: p.arn,
368
422
  user_name: entityname
369
423
  )
370
424
  end
371
425
  elsif entitytype == "group"
372
- resp = MU::Cloud::AWS.iam.list_attached_group_policies(
426
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_group_policies(
373
427
  path_prefix: "/"+@deploy.deploy_id+"/",
374
428
  group_name: entityname
375
429
  )
376
430
  if !resp or !resp.attached_policies.map { |p| p.policy_name }.include?(p.policy_name)
377
431
  MU.log "Attaching policy #{p.policy_name} to group #{entityname}", MU::NOTICE
378
- MU::Cloud::AWS.iam.attach_group_policy(
432
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_group_policy(
379
433
  policy_arn: p.arn,
380
434
  group_name: entityname
381
435
  )
382
436
  end
383
437
  elsif entitytype == "role"
384
- resp = MU::Cloud::AWS.iam.list_attached_role_policies(
438
+ resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_role_policies(
385
439
  role_name: entityname
386
440
  )
387
441
 
388
442
  if !resp or !resp.attached_policies.map { |p| p.policy_name }.include?(p.policy_name)
389
443
  MU.log "Attaching policy #{p.policy_name} to role #{entityname}", MU::NOTICE
390
- MU::Cloud::AWS.iam.attach_role_policy(
444
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_role_policy(
391
445
  policy_arn: p.arn,
392
446
  role_name: entityname
393
447
  )
@@ -407,18 +461,19 @@ module MU
407
461
  end
408
462
 
409
463
  resp = begin
410
- MU::Cloud::AWS.iam.create_instance_profile(
464
+ MU.log "Creating instance profile #{@mu_name} #{@config['credentials']}"
465
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_instance_profile(
411
466
  instance_profile_name: @mu_name
412
467
  )
413
468
  rescue Aws::IAM::Errors::EntityAlreadyExists => e
414
- MU::Cloud::AWS.iam.get_instance_profile(
469
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
415
470
  instance_profile_name: @mu_name
416
471
  )
417
472
  end
418
473
 
419
474
  # make sure it's really there before moving on
420
475
  begin
421
- MU::Cloud::AWS.iam.get_instance_profile(instance_profile_name: @mu_name)
476
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(instance_profile_name: @mu_name)
422
477
  rescue Aws::IAM::Errors::NoSuchEntity => e
423
478
  MU.log e.inspect, MU::WARN
424
479
  sleep 10
@@ -469,6 +524,12 @@ module MU
469
524
  "description" => "Type of entity which will be permitted to assume this role. See +entity_id+ for details.",
470
525
  "enum" => ["service", "aws", "federated"]+aws_resource_types
471
526
  },
527
+ "assume_method" => {
528
+ "type" => "string",
529
+ "description" => "https://docs.aws.amazon.com/STS/latest/APIReference/API_Operations.html",
530
+ "enum" => ["basic", "saml", "web"],
531
+ "default" => "basic"
532
+ },
472
533
  "entity_id" => {
473
534
  "type" => "string",
474
535
  "description" => "An identifier appropriate for the +entity_type+ which is allowed to assume this role- see details for valid formats.\n
@@ -501,15 +562,24 @@ module MU
501
562
 
502
563
  if role['import']
503
564
  role['import'].each { |policy|
504
- if !policy.match(/^arn:/i)
505
- policy = "arn:"+(MU::Cloud::AWS.isGovCloud?(role["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/"+policy
565
+ arn = if !policy.match(/^arn:/i)
566
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(role["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/"+policy
567
+ else
568
+ policy
506
569
  end
570
+ retried = false
507
571
  begin
508
- MU::Cloud::AWS.iam.get_policy(policy_arn: policy)
572
+ MU::Cloud::AWS.iam(credentials: role['credentials']).get_policy(policy_arn: arn)
509
573
  rescue Aws::IAM::Errors::NoSuchEntity => e
510
- MU.log "No such canned AWS IAM policy '#{policy}'", MU::ERR
574
+ if !retried
575
+ arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(role["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/service-role/"+policy
576
+ retried = true
577
+ retry
578
+ end
579
+ MU.log "No such canned AWS IAM policy '#{arn}'", MU::ERR
511
580
  ok = false
512
581
  end
582
+ policy = arn
513
583
  }
514
584
  end
515
585
 
@@ -524,6 +594,19 @@ module MU
524
594
  ok = false
525
595
  end
526
596
 
597
+ if role['policies']
598
+ role['policies'].each { |policy|
599
+ policy['targets'].each { |target|
600
+ if target['type']
601
+ role['dependencies'] ||= []
602
+ role['dependencies'] << {
603
+ "name" => target['identifier'],
604
+ "type" => target['type']
605
+ }
606
+ end
607
+ }
608
+ }
609
+ end
527
610
 
528
611
  ok
529
612
  end
@@ -540,7 +623,7 @@ module MU
540
623
  "Version" => "2012-10-17",
541
624
  "Statement" => [
542
625
  {
543
- "Sid" => policy["name"],
626
+ "Sid" => policy["name"].gsub(/[^0-9A-Za-z]*/, ""),
544
627
  "Effect" => policy['flag'].capitalize,
545
628
  "Action" => [],
546
629
  "Resource" => []
@@ -598,13 +681,18 @@ module MU
598
681
  role_policy_doc = {
599
682
  "Version" => "2012-10-17",
600
683
  }
601
- # XXX support AssumeRole, AssumeRoleWithSAML, and AssumeRoleWithWebIdentity
684
+
602
685
  statements = []
603
686
  if @config['can_assume']
687
+ act_map = {
688
+ "basic" => "sts:AssumeRole",
689
+ "saml" => "sts:AssumeRoleWithSAML",
690
+ "web" => "sts:AssumeRoleWithWebIdentity"
691
+ }
604
692
  @config['can_assume'].each { |svc|
605
693
  statement = {
606
694
  "Effect" => "Allow",
607
- "Action" => "sts:AssumeRole",
695
+ "Action" => act_map[svc['assume_method']],
608
696
  "Principal" => {}
609
697
  }
610
698
  if ["service", "iam", "federated"].include?(svc["entity_type"])
@@ -632,17 +720,17 @@ module MU
632
720
  # Update a policy, handling deletion of old versions as needed
633
721
  def update_policy(arn, doc)
634
722
  begin
635
- MU::Cloud::AWS.iam.create_policy_version(
723
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy_version(
636
724
  policy_arn: arn,
637
725
  set_as_default: true,
638
726
  policy_document: JSON.generate(doc)
639
727
  )
640
728
  rescue Aws::IAM::Errors::LimitExceeded => e
641
- delete_version = MU::Cloud::AWS.iam.list_policy_versions(
729
+ delete_version = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_policy_versions(
642
730
  policy_arn: arn,
643
731
  ).versions.last.version_id
644
732
  MU.log "Purging oldest version (#{delete_version}) of IAM policy #{arn}", MU::NOTICE
645
- MU::Cloud::AWS.iam.delete_policy_version(
733
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).delete_policy_version(
646
734
  policy_arn: arn,
647
735
  version_id: delete_version
648
736
  )