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

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 (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
  )