cloud-mu 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +10 -13
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -3
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +135 -37
  23. data/cloud-mu.gemspec +22 -20
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +165 -111
  45. data/modules/mu/adoption.rb +401 -68
  46. data/modules/mu/cleanup.rb +199 -306
  47. data/modules/mu/cloud.rb +100 -1632
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +46 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +920 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +165 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +4 -4
  61. data/modules/mu/config/cache_cluster.rb +1 -1
  62. data/modules/mu/config/collection.rb +4 -4
  63. data/modules/mu/config/container_cluster.rb +9 -4
  64. data/modules/mu/config/database.rb +83 -104
  65. data/modules/mu/config/database.yml +1 -2
  66. data/modules/mu/config/dnszone.rb +6 -6
  67. data/modules/mu/config/doc_helpers.rb +516 -0
  68. data/modules/mu/config/endpoint.rb +4 -4
  69. data/modules/mu/config/firewall_rule.rb +103 -4
  70. data/modules/mu/config/folder.rb +4 -4
  71. data/modules/mu/config/function.rb +3 -3
  72. data/modules/mu/config/group.rb +4 -4
  73. data/modules/mu/config/habitat.rb +4 -4
  74. data/modules/mu/config/loadbalancer.rb +60 -14
  75. data/modules/mu/config/log.rb +4 -4
  76. data/modules/mu/config/msg_queue.rb +4 -4
  77. data/modules/mu/config/nosqldb.rb +4 -4
  78. data/modules/mu/config/notifier.rb +3 -3
  79. data/modules/mu/config/ref.rb +365 -0
  80. data/modules/mu/config/role.rb +4 -4
  81. data/modules/mu/config/schema_helpers.rb +509 -0
  82. data/modules/mu/config/search_domain.rb +4 -4
  83. data/modules/mu/config/server.rb +97 -70
  84. data/modules/mu/config/server.yml +1 -0
  85. data/modules/mu/config/server_pool.rb +5 -9
  86. data/modules/mu/config/storage_pool.rb +1 -1
  87. data/modules/mu/config/tail.rb +200 -0
  88. data/modules/mu/config/user.rb +4 -4
  89. data/modules/mu/config/vpc.rb +70 -27
  90. data/modules/mu/config/vpc.yml +0 -1
  91. data/modules/mu/defaults/AWS.yaml +83 -60
  92. data/modules/mu/defaults/Azure.yaml +1 -0
  93. data/modules/mu/defaults/Google.yaml +3 -2
  94. data/modules/mu/deploy.rb +30 -26
  95. data/modules/mu/groomer.rb +17 -2
  96. data/modules/mu/groomers/ansible.rb +188 -41
  97. data/modules/mu/groomers/chef.rb +116 -55
  98. data/modules/mu/logger.rb +127 -148
  99. data/modules/mu/master.rb +389 -2
  100. data/modules/mu/master/chef.rb +3 -4
  101. data/modules/mu/master/ldap.rb +3 -3
  102. data/modules/mu/master/ssl.rb +12 -3
  103. data/modules/mu/mommacat.rb +217 -2612
  104. data/modules/mu/mommacat/daemon.rb +397 -0
  105. data/modules/mu/mommacat/naming.rb +473 -0
  106. data/modules/mu/mommacat/search.rb +495 -0
  107. data/modules/mu/mommacat/storage.rb +722 -0
  108. data/modules/mu/{clouds → providers}/README.md +1 -1
  109. data/modules/mu/{clouds → providers}/aws.rb +271 -112
  110. data/modules/mu/{clouds → providers}/aws/alarm.rb +5 -3
  111. data/modules/mu/{clouds → providers}/aws/bucket.rb +26 -22
  112. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +33 -67
  113. data/modules/mu/{clouds → providers}/aws/collection.rb +24 -23
  114. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +681 -721
  115. data/modules/mu/providers/aws/database.rb +1744 -0
  116. data/modules/mu/{clouds → providers}/aws/dnszone.rb +64 -63
  117. data/modules/mu/{clouds → providers}/aws/endpoint.rb +22 -27
  118. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +214 -244
  119. data/modules/mu/{clouds → providers}/aws/folder.rb +7 -7
  120. data/modules/mu/{clouds → providers}/aws/function.rb +17 -22
  121. data/modules/mu/{clouds → providers}/aws/group.rb +23 -23
  122. data/modules/mu/{clouds → providers}/aws/habitat.rb +17 -14
  123. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +57 -48
  124. data/modules/mu/{clouds → providers}/aws/log.rb +15 -12
  125. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +17 -16
  126. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +18 -11
  127. data/modules/mu/{clouds → providers}/aws/notifier.rb +11 -6
  128. data/modules/mu/{clouds → providers}/aws/role.rb +112 -86
  129. data/modules/mu/{clouds → providers}/aws/search_domain.rb +39 -33
  130. data/modules/mu/{clouds → providers}/aws/server.rb +835 -1133
  131. data/modules/mu/{clouds → providers}/aws/server_pool.rb +56 -60
  132. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +24 -42
  133. data/modules/mu/{clouds → providers}/aws/user.rb +21 -22
  134. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  135. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  136. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  137. data/modules/mu/{clouds → providers}/aws/vpc.rb +523 -929
  138. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  139. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  140. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  141. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  142. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  143. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  144. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  145. data/modules/mu/{clouds → providers}/azure/server.rb +95 -48
  146. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  147. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  148. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  149. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  150. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  151. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  152. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  153. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  156. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  160. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  161. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  162. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  163. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  164. data/modules/mu/{clouds → providers}/google.rb +67 -30
  165. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  166. data/modules/mu/{clouds → providers}/google/container_cluster.rb +84 -77
  167. data/modules/mu/{clouds → providers}/google/database.rb +10 -20
  168. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  169. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  170. data/modules/mu/{clouds → providers}/google/function.rb +139 -167
  171. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  172. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  173. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +18 -20
  174. data/modules/mu/{clouds → providers}/google/role.rb +92 -58
  175. data/modules/mu/{clouds → providers}/google/server.rb +242 -155
  176. data/modules/mu/{clouds → providers}/google/server_pool.rb +25 -44
  177. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  178. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  179. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  180. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  181. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  182. data/modules/tests/bucket.yml +4 -0
  183. data/modules/tests/centos6.yaml +11 -0
  184. data/modules/tests/centos7.yaml +11 -0
  185. data/modules/tests/centos8.yaml +12 -0
  186. data/modules/tests/ecs.yaml +23 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/rds.yaml +108 -0
  189. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  190. data/modules/tests/regrooms/bucket.yml +19 -0
  191. data/modules/tests/regrooms/rds.yaml +123 -0
  192. data/modules/tests/server-with-scrub-muisms.yaml +1 -0
  193. data/modules/tests/super_simple_bok.yml +1 -3
  194. data/modules/tests/win2k12.yaml +17 -5
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +232 -154
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1985
@@ -203,6 +203,9 @@ module MU
203
203
  # @param region [String]: The cloud provider region
204
204
  # @return [void]
205
205
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
206
+ MU.log "AWS::Log.cleanup: need to support flags['known']", MU::DEBUG, details: flags
207
+ MU.log "Placeholder: AWS Log artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
208
+
206
209
  log_groups = self.find(credentials: credentials, region: region).values
207
210
  if !log_groups.empty?
208
211
  log_groups.each{ |lg|
@@ -227,13 +230,13 @@ module MU
227
230
  }
228
231
  end
229
232
 
230
- unless noop
231
- MU::Cloud::AWS.iam(credentials: credentials).list_roles.roles.each{ |role|
232
- match_string = "#{MU.deploy_id}.*CloudTrail"
233
- # Maybe we should have a more generic way to delete IAM profiles and policies. The call itself should be moved from MU::Cloud::AWS::Server.
234
- # MU::Cloud::AWS::Server.removeIAMProfile(role.role_name) if role.role_name.match(match_string)
235
- }
236
- end
233
+ # unless noop
234
+ # MU::Cloud::AWS.iam(credentials: credentials).list_roles.roles.each{ |role|
235
+ # match_string = "#{MU.deploy_id}.*CloudTrail"
236
+ # Maybe we should have a more generic way to delete IAM profiles and policies. The call itself should be moved from MU::Cloud.resourceClass("AWS", "Server").
237
+ # MU::Cloud.resourceClass("AWS", "Server").removeIAMProfile(role.role_name) if role.role_name.match(match_string)
238
+ # }
239
+ # end
237
240
  end
238
241
 
239
242
  # Locate an existing log group.
@@ -264,7 +267,7 @@ module MU
264
267
  # Reverse-map our cloud description into a runnable config hash.
265
268
  # We assume that any values we have in +@config+ are placeholders, and
266
269
  # calculate our own accordingly based on what's live in the cloud.
267
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
270
+ def toKitten(**_args)
268
271
  bok = {
269
272
  "cloud" => "AWS",
270
273
  "credentials" => @config['credentials'],
@@ -304,9 +307,9 @@ module MU
304
307
 
305
308
 
306
309
  # Cloud-specific configuration properties.
307
- # @param config [MU::Config]: The calling MU::Config object
310
+ # @param _config [MU::Config]: The calling MU::Config object
308
311
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
309
- def self.schema(config)
312
+ def self.schema(_config)
310
313
  toplevel_required = []
311
314
  schema = {
312
315
  "retention_period" => {
@@ -357,9 +360,9 @@ module MU
357
360
 
358
361
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::logs}, bare and unvalidated.
359
362
  # @param log [Hash]: The resource to process and validate
360
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
363
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
361
364
  # @return [Boolean]: True if validation succeeded, False otherwise
362
- def self.validateConfig(log, configurator)
365
+ def self.validateConfig(log, _configurator)
363
366
  ok = true
364
367
 
365
368
  if log["filters"] && !log["filters"].empty?
@@ -53,14 +53,14 @@ module MU
53
53
  new_attrs = genQueueAttrs
54
54
 
55
55
  changed = false
56
- new_attrs.each_pair { |k, v|
56
+ new_attrs.each_pair { |k, _v|
57
57
  if !cur_attrs.has_key?(k) or cur_attrs[k] != new_attrs[k]
58
58
  changed = true
59
59
  end
60
60
  }
61
61
  if changed
62
62
  MU.log "Updating SQS queue #{@mu_name}", MU::NOTICE, details: new_attrs
63
- resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).set_queue_attributes(
63
+ MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).set_queue_attributes(
64
64
  queue_url: @cloud_id,
65
65
  attributes: new_attrs
66
66
  )
@@ -74,10 +74,13 @@ module MU
74
74
  "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":sqs:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":"+@cloud_id
75
75
  end
76
76
 
77
+ @cloud_desc_cache = nil
77
78
  # Retrieve the AWS descriptor for this SQS queue. AWS doesn't exactly
78
79
  # provide one; if you want real information for SQS ask notify()
79
80
  # @return [Hash]: AWS doesn't return anything but the SQS URL, so supplement with attributes
80
- def cloud_desc
81
+ def cloud_desc(use_cache: true)
82
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
83
+
81
84
  if !@cloud_id
82
85
  resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).list_queues(
83
86
  queue_name_prefix: @mu_name
@@ -92,11 +95,12 @@ module MU
92
95
  end
93
96
 
94
97
  return nil if !@cloud_id
95
- MU::Cloud::AWS::MsgQueue.find(
98
+ @cloud_desc_cache = MU::Cloud::AWS::MsgQueue.find(
96
99
  cloud_id: @cloud_id.dup,
97
100
  region: @config['region'],
98
101
  credentials: @config['credentials']
99
102
  )
103
+ @cloud_desc_cache
100
104
  end
101
105
 
102
106
  # Return the metadata for this MsgQueue rule
@@ -130,6 +134,9 @@ module MU
130
134
  # @param region [String]: The cloud provider region
131
135
  # @return [void]
132
136
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
137
+ MU.log "AWS::MsgQueue.cleanup: need to support flags['known']", MU::DEBUG, details: flags
138
+ MU.log "Placeholder: AWS MsgQueue artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
139
+
133
140
  resp = MU::Cloud::AWS.sqs(credentials: credentials, region: region).list_queues(
134
141
  queue_name_prefix: MU.deploy_id
135
142
  )
@@ -182,7 +189,7 @@ module MU
182
189
  args[:cloud_id] = resp.queue_url if resp and resp.queue_url
183
190
  end
184
191
  end
185
- rescue ::Aws::SQS::Errors::NonExistentQueue => e
192
+ rescue ::Aws::SQS::Errors::NonExistentQueue
186
193
  end
187
194
 
188
195
  # Go fetch its attributes
@@ -211,9 +218,9 @@ module MU
211
218
  end
212
219
 
213
220
  # Cloud-specific configuration properties.
214
- # @param config [MU::Config]: The calling MU::Config object
221
+ # @param _config [MU::Config]: The calling MU::Config object
215
222
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
216
- def self.schema(config)
223
+ def self.schema(_config)
217
224
  toplevel_required = []
218
225
  schema = {
219
226
  "max_msg_size" => {
@@ -320,16 +327,10 @@ module MU
320
327
  failq.delete("failqueue")
321
328
  ok = false if !configurator.insertKitten(failq, "msg_queues")
322
329
  queue['failqueue']['name'] = failq['name']
323
- queue['dependencies'] << {
324
- "name" => failq['name'],
325
- "type" => "msg_queue"
326
- }
330
+ MU::Config.addDependency(queue, failq["name"], "msg_queue")
327
331
  else
328
332
  if configurator.haveLitterMate?(queue['failqueue']['name'], "msg_queue")
329
- queue['dependencies'] << {
330
- "name" => queue['failqueue']['name'],
331
- "type" => "msg_queue"
332
- }
333
+ MU::Config.addDependency(queue, queue['failqueue']['name'], "msg_queue")
333
334
  else
334
335
  failq = MU::Cloud::AWS::MsgQueue.find(cloud_id: queue['failqueue']['name'])
335
336
  if !failq
@@ -382,7 +383,7 @@ module MU
382
383
  end
383
384
  begin
384
385
  MU::Cloud::AWS.kms(region: queue['region']).describe_key(key_id: queue['kms']['key_id'])
385
- rescue Aws::KMS::Errors::NotFoundException => e
386
+ rescue Aws::KMS::Errors::NotFoundException
386
387
  MU.log "KMS key '#{queue['kms']['key_id']}' specified in Queue '#{queue['name']}' was not found.", MU::ERR, details: "Key IDs are of the form bf64a093-2c3d-46fa-0d4f-8232fa7ed53. Keys can be created at https://console.aws.amazon.com/iam/home#/encryptionKeys/#{queue['region']}"
387
388
  ok = false
388
389
  end
@@ -164,6 +164,8 @@ module MU
164
164
  # @param region [String]: The cloud provider region
165
165
  # @return [void]
166
166
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
167
+ MU.log "AWS::NoSQLDb.cleanup: need to support flags['known']", MU::DEBUG, details: flags
168
+
167
169
  resp = MU::Cloud::AWS.dynamo(credentials: credentials, region: region).list_tables
168
170
  if resp and resp.table_names
169
171
  resp.table_names.each { |table|
@@ -178,16 +180,23 @@ module MU
178
180
  begin
179
181
  tags = MU::Cloud::AWS.dynamo(credentials: credentials, region: region).list_tags_of_resource(resource_arn: desc.table_arn)
180
182
  if tags and tags.tags
183
+ deploy_match = false
184
+ master_match = false
181
185
  tags.tags.each { |tag|
182
186
  if tag.key == "MU-ID" and tag.value == MU.deploy_id
183
- MU.log "Deleting DynamoDB table #{desc.table_name}"
184
- if !noop
185
- MU::Cloud::AWS.dynamo(credentials: credentials, region: region).delete_table(table_name: desc.table_name)
186
- end
187
+ deploy_match = true
188
+ elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
189
+ master_match = true
187
190
  end
188
191
  }
192
+ if deploy_match and (master_match or ignoremaster)
193
+ MU.log "Deleting DynamoDB table #{desc.table_name}"
194
+ if !noop
195
+ MU::Cloud::AWS.dynamo(credentials: credentials, region: region).delete_table(table_name: desc.table_name)
196
+ end
197
+ end
189
198
  end
190
- rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
199
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
191
200
  end
192
201
 
193
202
  }
@@ -236,9 +245,9 @@ module MU
236
245
  end
237
246
 
238
247
  # Cloud-specific configuration properties.
239
- # @param config [MU::Config]: The calling MU::Config object
248
+ # @param _config [MU::Config]: The calling MU::Config object
240
249
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
241
- def self.schema(config)
250
+ def self.schema(_config)
242
251
  toplevel_required = ["attributes"]
243
252
 
244
253
 
@@ -360,9 +369,9 @@ module MU
360
369
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::nosqldbs}, bare and unvalidated.
361
370
 
362
371
  # @param db [Hash]: The resource to process and validate
363
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
372
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
364
373
  # @return [Boolean]: True if validation succeeded, False otherwise
365
- def self.validateConfig(db, configurator)
374
+ def self.validateConfig(db, _configurator)
366
375
  ok = true
367
376
 
368
377
  partition = nil
@@ -399,8 +408,6 @@ module MU
399
408
  ok
400
409
  end
401
410
 
402
- private
403
-
404
411
  end
405
412
  end
406
413
  end
@@ -66,12 +66,17 @@ module MU
66
66
  # @param region [String]: The cloud provider region
67
67
  # @return [void]
68
68
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
69
+ MU.log "AWS::Notifier.cleanup: need to support flags['known']", MU::DEBUG, details: flags
70
+ MU.log "Placeholder: AWS Notifier artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
71
+
69
72
  MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
70
73
  if topic.topic_arn.match(MU.deploy_id)
71
74
  # We don't have a way to tag our SNS topics, so we will delete any topic that has the MU-ID in its ARN.
72
75
  # This may fail to find notifier groups in some cases (eg. cache_cluster) so we might want to delete from each API as well.
73
- MU::Cloud::AWS.sns(region: region, credentials: credentials).delete_topic(topic_arn: topic.topic_arn)
74
- MU.log "Deleted SNS topic: #{topic.topic_arn}"
76
+ MU.log "Deleting SNS topic: #{topic.topic_arn}"
77
+ if !noop
78
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).delete_topic(topic_arn: topic.topic_arn)
79
+ end
75
80
  end
76
81
  }
77
82
  end
@@ -116,9 +121,9 @@ module MU
116
121
  end
117
122
 
118
123
  # Cloud-specific configuration properties.
119
- # @param config [MU::Config]: The calling MU::Config object
124
+ # @param _config [MU::Config]: The calling MU::Config object
120
125
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
121
- def self.schema(config)
126
+ def self.schema(_config)
122
127
  toplevel_required = []
123
128
  schema = {
124
129
  "subscriptions" => {
@@ -143,9 +148,9 @@ module MU
143
148
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::notifier}, bare and unvalidated.
144
149
 
145
150
  # @param notifier [Hash]: The resource to process and validate
146
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
151
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
147
152
  # @return [Boolean]: True if validation succeeded, False otherwise
148
- def self.validateConfig(notifier, configurator)
153
+ def self.validateConfig(notifier, _configurator)
149
154
  ok = true
150
155
 
151
156
  if notifier['subscriptions']
@@ -43,7 +43,7 @@ module MU
43
43
 
44
44
  policy_name = @mu_name+"-"+policy.keys.first.upcase
45
45
  MU.log "Creating IAM policy #{policy_name}"
46
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
46
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
47
47
  policy_name: policy_name,
48
48
  path: "/"+@deploy.deploy_id+"/",
49
49
  policy_document: JSON.generate(policy.values.first),
@@ -56,8 +56,8 @@ module MU
56
56
  MU.log "Creating IAM role #{@mu_name}"
57
57
  @cloud_id = @mu_name
58
58
  path = @config['strip_path'] ? nil : "/"+@deploy.deploy_id+"/"
59
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
60
- path: nil,
59
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
60
+ path: path,
61
61
  role_name: @mu_name,
62
62
  description: "Generated by Mu",
63
63
  assume_role_policy_document: gen_assume_role_policy_doc,
@@ -89,7 +89,6 @@ module MU
89
89
  end
90
90
 
91
91
  if @config['raw_policies'] or @config['attachable_policies']
92
- attached_policies = []
93
92
  configured_policies = []
94
93
 
95
94
  if @config['raw_policies']
@@ -128,7 +127,7 @@ module MU
128
127
 
129
128
  # XXX not sure we're binding these sanely, validate that
130
129
  if @config['raw_policies']
131
- pol_arns = MU::Cloud::AWS::Role.manageRawPolicies(
130
+ MU::Cloud::AWS::Role.manageRawPolicies(
132
131
  @config['raw_policies'],
133
132
  basename: @deploy.getResourceName(@config['name']),
134
133
  credentials: @credentials
@@ -183,7 +182,7 @@ module MU
183
182
  desc
184
183
  end
185
184
 
186
- rescue Aws::IAM::Errors::NoSuchEntity => e
185
+ rescue Aws::IAM::Errors::NoSuchEntity
187
186
  MU.log "Creating IAM policy #{policy_name}", details: policy.values.first
188
187
  MU::Cloud::AWS.iam(credentials: credentials).create_policy(
189
188
  policy_name: policy_name,
@@ -202,65 +201,72 @@ module MU
202
201
  def arn
203
202
  desc = cloud_desc
204
203
  if desc["role"]
205
- desc["role"].arn
204
+ if desc['role'].is_a?(Hash)
205
+ desc["role"][:arn] # why though
206
+ else
207
+ desc["role"].arn
208
+ end
206
209
  else
207
210
  nil
208
211
  end
209
212
  end
210
213
 
214
+ @cloud_desc_cache = nil
211
215
  # Return a hash containing a +role+ element and a +policies+ element,
212
216
  # populated with one or both depending on what this resource has
213
217
  # defined.
214
- def cloud_desc
215
- desc = {}
218
+ def cloud_desc(use_cache: true)
219
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
220
+
221
+ @cloud_desc_cache = {}
216
222
  if @config['bare_policies']
217
223
  if @cloud_id
218
224
  pol_desc = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
219
225
  if pol_desc
220
- desc['policies'] = [pol_desc]
221
- return desc
226
+ @cloud_desc_cache['policies'] = [pol_desc]
227
+ return @cloud_desc_cache
222
228
  end
223
229
  end
224
230
 
225
231
  if @deploy and @deploy.deploy_id
226
- desc["policies"] = MU::Cloud::AWS.iam(credentials: @credentials).list_policies(
232
+ @cloud_desc_cache["policies"] = MU::Cloud::AWS.iam(credentials: @credentials).list_policies(
227
233
  path_prefix: "/"+@deploy.deploy_id+"/"
228
234
  ).policies
229
- desc["policies"].reject! { |p|
235
+ @cloud_desc_cache["policies"].reject! { |p|
230
236
  !p.policy_name.match(/^#{Regexp.quote(@mu_name)}-/)
231
237
  }
232
238
  # this is quasi-wrong because we can be mulitple cloud is, but
233
239
  # we can't really set this type to has_multiples because that's
234
240
  # just how managed policies work not anything else, goddammit
235
241
  # AWS why can't you just bundle everything in roles
236
- if desc["policies"] and desc["policies"].size > 0
237
- @cloud_id ||= desc["policies"].first.arn
242
+ if @cloud_desc_cache["policies"] and @cloud_desc_cache["policies"].size > 0
243
+ @cloud_id ||= @cloud_desc_cache["policies"].first.arn
238
244
  end
239
245
  end
240
246
  else
241
247
  if @cloud_id.match(/^arn:aws(:?-us-gov)?:[^:]*:[^:]*:\d*:policy\//)
242
248
  pol_desc = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
243
249
  if pol_desc
244
- desc['policies'] = [pol_desc]
245
- return desc
250
+ @cloud_desc_cache['policies'] = [pol_desc]
251
+ return @cloud_desc_cache
246
252
  end
247
253
  end
248
254
  begin
249
- desc['role'] = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
250
- desc['role'] ||= MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @mu_name).values.first
255
+ @cloud_desc_cache['role'] = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
256
+ @cloud_desc_cache['role'] ||= MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @mu_name).values.first
251
257
  MU::Cloud::AWS.iam(credentials: @credentials).list_attached_role_policies(
252
258
  role_name: @mu_name
253
259
  ).attached_policies.each { |p|
254
- desc["policies"] ||= []
255
- desc["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
260
+ @cloud_desc_cache["policies"] ||= []
261
+ @cloud_desc_cache["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
256
262
  policy_arn: p.policy_arn
257
263
  ).policy
258
264
  }
259
265
 
260
266
  inline = MU::Cloud::AWS.iam(credentials: @credentials).list_role_policies(role_name: @mu_name).policy_names
261
267
  inline.each { |pol_name|
262
- desc["policies"] ||= []
263
- desc["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_role_policy(
268
+ @cloud_desc_cache["policies"] ||= []
269
+ @cloud_desc_cache["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_role_policy(
264
270
  role_name: @mu_name,
265
271
  policy_name: pol_name
266
272
  )
@@ -270,9 +276,9 @@ rescue ::Aws::IAM::Errors::ValidationError => e
270
276
  MU.log @cloud_id+" "+@mu_name, MU::WARN, details: e.inspect
271
277
  end
272
278
  end
273
- desc['cloud_id'] ||= @cloud_id
279
+ @cloud_desc_cache['cloud_id'] ||= @cloud_id
274
280
 
275
- desc
281
+ @cloud_desc_cache
276
282
  end
277
283
 
278
284
  # Return the metadata for this user cofiguration
@@ -284,26 +290,25 @@ end
284
290
  # Insert a new target entity into an existing policy.
285
291
  # @param policy [String]: The name of the policy to which we're appending, which must already exist as part of this role resource
286
292
  # @param targets [Array<String>]: The target resource. If +target_type+ isn't specified, this should be a fully-resolved ARN.
287
- # @param mu_type [String]: A valid Mu resource type
288
- def injectPolicyTargets(policy, targets, mu_type = nil)
293
+ def injectPolicyTargets(policy, targets)
289
294
  if !policy.match(/^#{@deploy.deploy_id}/)
290
295
  policy = @mu_name+"-"+policy.upcase
291
296
  end
292
-
293
- my_policies = cloud_desc["policies"]
297
+ my_policies = cloud_desc(use_cache: false)["policies"]
294
298
  my_policies ||= []
295
-
299
+
300
+ seen_policy = false
296
301
  my_policies.each { |p|
297
302
  if p.policy_name == policy
303
+ seen_policy = true
298
304
  old = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy_version(
299
305
  policy_arn: p.arn,
300
306
  version_id: p.default_version_id
301
307
  ).policy_version
302
308
 
303
309
  doc = JSON.parse URI.decode_www_form_component old.document
304
-
305
310
  need_update = false
306
-
311
+
307
312
  doc["Statement"].each { |s|
308
313
  targets.each { |target|
309
314
  target_string = target
@@ -332,6 +337,10 @@ end
332
337
  end
333
338
  end
334
339
  }
340
+
341
+ if !seen_policy
342
+ MU.log "Was given new targets for policy #{policy}, but I don't see any such policy attached to role #{@cloud_id}", MU::WARN, details: targets
343
+ end
335
344
  end
336
345
 
337
346
  # Delete an IAM policy, along with attendant versions and attachments.
@@ -409,9 +418,8 @@ end
409
418
  # Remove all roles associated with the currently loaded deployment.
410
419
  # @param noop [Boolean]: If true, will only print what would be done
411
420
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
412
- # @param region [String]: The cloud provider region
413
421
  # @return [void]
414
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
422
+ def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
415
423
 
416
424
  resp = MU::Cloud::AWS.iam(credentials: credentials).list_policies(
417
425
  path_prefix: "/"+MU.deploy_id+"/"
@@ -438,12 +446,18 @@ end
438
446
  # check tags. Hardly seems efficient.
439
447
  desc = MU::Cloud::AWS.iam(credentials: credentials).get_role(role_name: r.role_name)
440
448
  if desc.role and desc.role.tags and desc.role.tags
449
+ master_match = false
450
+ deploy_match = false
441
451
  desc.role.tags.each { |t|
442
452
  if t.key == "MU-ID" and t.value == MU.deploy_id
443
- deleteme << r
444
- break
453
+ deploy_match = true
454
+ elsif t.key == "MU-MASTER-IP" and t.value == MU.mu_public_ip
455
+ master_match = true
445
456
  end
446
457
  }
458
+ if deploy_match and (master_match or ignoremaster)
459
+ deleteme << r
460
+ end
447
461
  end
448
462
  }
449
463
 
@@ -481,7 +495,7 @@ end
481
495
  MU::Cloud::AWS.iam(credentials: credentials).delete_instance_profile(instance_profile_name: r.role_name)
482
496
  rescue Aws::IAM::Errors::ValidationError => e
483
497
  MU.log "Cleaning up IAM role #{r.role_name}: #{e.inspect}", MU::WARN
484
- rescue Aws::IAM::Errors::NoSuchEntity => e
498
+ rescue Aws::IAM::Errors::NoSuchEntity
485
499
  end
486
500
 
487
501
  MU::Cloud::AWS.iam(credentials: credentials).delete_role(
@@ -519,7 +533,7 @@ end
519
533
  end
520
534
  rescue ::Aws::IAM::Errors::NoSuchEntity
521
535
  end
522
-
536
+
523
537
  else
524
538
  marker = nil
525
539
  begin
@@ -552,7 +566,7 @@ end
552
566
  # Reverse-map our cloud description into a runnable config hash.
553
567
  # We assume that any values we have in +@config+ are placeholders, and
554
568
  # calculate our own accordingly based on what's live in the cloud.
555
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
569
+ def toKitten(**_args)
556
570
  bok = {
557
571
  "cloud" => "AWS",
558
572
  "credentials" => @config['credentials'],
@@ -601,14 +615,13 @@ end
601
615
  )
602
616
  JSON.parse(URI.decode(version.policy_version.document))
603
617
  end
604
-
605
618
  bok["policies"] = MU::Cloud::AWS::Role.doc2MuPolicies(pol.policy_name, doc, bok["policies"])
606
619
  end
607
620
  }
608
621
 
609
622
  return bok if @config['bare_policies']
610
623
  end
611
-
624
+
612
625
  if desc.tags and desc.tags.size > 0
613
626
  bok["tags"] = MU.structToHash(desc.tags, stringify_keys: true)
614
627
  end
@@ -681,6 +694,7 @@ end
681
694
  end
682
695
 
683
696
  bok["attachable_policies"].uniq! if bok["attachable_policies"]
697
+ bok["name"].gsub!(/[^a-zA-Z0-9_\-]/, "_")
684
698
 
685
699
  bok
686
700
  end
@@ -693,6 +707,10 @@ end
693
707
  def self.doc2MuPolicies(basename, doc, policies = [])
694
708
  policies ||= []
695
709
 
710
+ if !doc["Statement"].is_a?(Array)
711
+ doc["Statement"] = [doc["Statement"]]
712
+ end
713
+
696
714
  doc["Statement"].each { |s|
697
715
  if !s["Action"]
698
716
  MU.log "Statement in policy document for #{basename} didn't have an Action field", MU::WARN, details: doc
@@ -721,7 +739,7 @@ end
721
739
  "targets" => s["Resource"].map { |r|
722
740
  if r.match(/^arn:aws(-us-gov)?:([^:]+):.*?:([^:]*)$/)
723
741
  # XXX which cases even count for blind references to sibling resources?
724
- type = if Regexp.last_match[1] == "s3"
742
+ if Regexp.last_match[1] == "s3"
725
743
  "bucket"
726
744
  elsif Regexp.last_match[1]
727
745
  MU.log "Service #{Regexp.last_match[1]} to type...", MU::WARN, details: r
@@ -741,7 +759,7 @@ end
741
759
 
742
760
  # Attach this role or group of loose policies to the specified entity.
743
761
  # @param entitytype [String]: The type of entity (user, group or role for policies; instance_profile for roles)
744
- def bindTo(entitytype, entityname, policies = [])
762
+ def bindTo(entitytype, entityname)
745
763
  if entitytype == "instance_profile"
746
764
  begin
747
765
  resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
@@ -754,7 +772,7 @@ end
754
772
  role_name: @mu_name
755
773
  )
756
774
  end
757
- rescue Exception => e
775
+ rescue StandardError => e
758
776
  MU.log "Error binding role #{@mu_name} to instance profile #{entityname}: #{e.message}", MU::ERR
759
777
  raise e
760
778
  end
@@ -783,7 +801,6 @@ end
783
801
  rescue Aws::IAM::Errors::NoSuchEntity => e
784
802
  if subpaths.size > 0
785
803
  p_arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/#{subpaths.shift}/"+policy
786
- retried = true
787
804
  retry
788
805
  end
789
806
  raise e
@@ -833,6 +850,7 @@ end
833
850
  else
834
851
  raise MuError, "Invalid entitytype '#{entitytype}' passed to MU::Cloud::AWS::Role.bindTo. Must be be one of: user, group, role, instance_profile"
835
852
  end
853
+ cloud_desc(use_cache: false)
836
854
  end
837
855
 
838
856
  # Create an instance profile for EC2 instances, named identically and
@@ -847,7 +865,7 @@ end
847
865
  MU::Cloud::AWS.iam(credentials: @config['credentials']).create_instance_profile(
848
866
  instance_profile_name: @mu_name
849
867
  )
850
- rescue Aws::IAM::Errors::EntityAlreadyExists => e
868
+ rescue Aws::IAM::Errors::EntityAlreadyExists
851
869
  MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
852
870
  instance_profile_name: @mu_name
853
871
  )
@@ -905,13 +923,13 @@ end
905
923
  end
906
924
 
907
925
  # Cloud-specific configuration properties.
908
- # @param config [MU::Config]: The calling MU::Config object
926
+ # @param _config [MU::Config]: The calling MU::Config object
909
927
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
910
- def self.schema(config)
928
+ def self.schema(_config)
911
929
  toplevel_required = []
912
930
  aws_resource_types = MU::Cloud.resource_types.keys.reject { |t|
913
931
  begin
914
- MU::Cloud.loadCloudType("AWS", t)
932
+ MU::Cloud.resourceClass("AWS", t)
915
933
  false
916
934
  rescue MuCloudResourceNotImplemented
917
935
  true
@@ -1012,10 +1030,9 @@ end
1012
1030
  subpaths = ["service-role", "aws-service-role", "job-function"]
1013
1031
  begin
1014
1032
  MU::Cloud::AWS.iam(credentials: credentials).get_policy(policy_arn: arn)
1015
- rescue Aws::IAM::Errors::NoSuchEntity => e
1033
+ rescue Aws::IAM::Errors::NoSuchEntity
1016
1034
  if subpaths.size > 0
1017
1035
  arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws")+":iam::aws:policy/#{subpaths.shift}/"+ref["id"]
1018
- retried = true
1019
1036
  retry
1020
1037
  end
1021
1038
  MU.log "No such canned AWS IAM policy '#{arn}'", MU::ERR
@@ -1029,9 +1046,9 @@ end
1029
1046
 
1030
1047
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::roles}, bare and unvalidated.
1031
1048
  # @param role [Hash]: The resource to process and validate
1032
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
1049
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
1033
1050
  # @return [Boolean]: True if validation succeeded, False otherwise
1034
- def self.validateConfig(role, configurator)
1051
+ def self.validateConfig(role, _configurator)
1035
1052
  ok = true
1036
1053
 
1037
1054
  # munge things declared with the deprecated import keyword into
@@ -1074,11 +1091,7 @@ end
1074
1091
  role['policies'].each { |policy|
1075
1092
  policy['targets'].each { |target|
1076
1093
  if target['type']
1077
- role['dependencies'] ||= []
1078
- role['dependencies'] << {
1079
- "name" => target['identifier'],
1080
- "type" => target['type']
1081
- }
1094
+ MU::Config.addDependency(role, target['identifier'], target['type'])
1082
1095
  end
1083
1096
  }
1084
1097
  }
@@ -1094,9 +1107,7 @@ end
1094
1107
  # @param policies [Array<Hash>]: One or more policy chunks
1095
1108
  # @param deploy_obj [MU::MommaCat]: Deployment object to use when looking up sibling Mu resources
1096
1109
  # @return [Array<Hash>]
1097
- def self.genPolicyDocument(policies, deploy_obj: nil)
1098
- iam_policies = []
1099
-
1110
+ def self.genPolicyDocument(policies, deploy_obj: nil, bucket_style: false)
1100
1111
  if policies
1101
1112
  name = nil
1102
1113
  doc = {
@@ -1134,12 +1145,21 @@ end
1134
1145
  )
1135
1146
  if sibling
1136
1147
  id = sibling.cloudobj.arn
1137
- statement["Principal"] << id
1148
+ if bucket_style
1149
+ statement["Principal"] << { "AWS" => id }
1150
+ else
1151
+ statement["Principal"] << id
1152
+ end
1138
1153
  else
1139
1154
  raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["identifier"]} when generating IAM policy"
1140
1155
  end
1141
1156
  else
1142
- statement["Principal"] << grantee["identifier"]
1157
+ bucket_prefix = grantee["identifier"].match(/^[^\.]+\.amazonaws\.com$/) ? "Service" : "AWS"
1158
+ if bucket_style
1159
+ statement["Principal"] << { bucket_prefix => grantee["identifier"] }
1160
+ else
1161
+ statement["Principal"] << grantee["identifier"]
1162
+ end
1143
1163
  end
1144
1164
  }
1145
1165
  if policy["grant_to"].size == 1
@@ -1162,9 +1182,11 @@ end
1162
1182
  stream_id = id.sub(/:([^:]+)$/, ":log-stream:*")
1163
1183
  # "arn:aws:logs:us-east-2:accountID:log-group:log_group_name:log-stream:CloudTrail_log_stream_name_prefix*"
1164
1184
  statement["Resource"] << stream_id
1185
+ elsif id.match(/:s3:/)
1186
+ statement["Resource"] << id+"/*"
1165
1187
  end
1166
1188
  else
1167
- raise MuError, "Couldn't find a #{target["entity_type"]} named #{target["identifier"]} when generating IAM policy"
1189
+ raise MuError, "Couldn't find a #{target["type"]} named #{target["identifier"]} when generating IAM policy"
1168
1190
  end
1169
1191
  else
1170
1192
  target["identifier"] += target["path"] if target["path"]
@@ -1180,6 +1202,32 @@ end
1180
1202
  []
1181
1203
  end
1182
1204
 
1205
+ # Update a policy, handling deletion of old versions as needed
1206
+ # @param arn [String]:
1207
+ # @param doc [Hash]:
1208
+ # @param credentials [String]:
1209
+ def self.update_policy(arn, doc, credentials: nil)
1210
+ # XXX this is just blindly replacing identical versions, when it should check
1211
+ # and guard
1212
+ begin
1213
+ MU::Cloud::AWS.iam(credentials: credentials).create_policy_version(
1214
+ policy_arn: arn,
1215
+ set_as_default: true,
1216
+ policy_document: JSON.generate(doc)
1217
+ )
1218
+ rescue Aws::IAM::Errors::LimitExceeded
1219
+ delete_version = MU::Cloud::AWS.iam(credentials: credentials).list_policy_versions(
1220
+ policy_arn: arn,
1221
+ ).versions.last.version_id
1222
+ MU.log "Purging oldest version (#{delete_version}) of IAM policy #{arn}", MU::NOTICE
1223
+ MU::Cloud::AWS.iam(credentials: credentials).delete_policy_version(
1224
+ policy_arn: arn,
1225
+ version_id: delete_version
1226
+ )
1227
+ retry
1228
+ end
1229
+ end
1230
+
1183
1231
  private
1184
1232
 
1185
1233
  # Convert entries from the cloud-neutral @config['policies'] list into
@@ -1261,28 +1309,6 @@ end
1261
1309
  MU::Cloud::AWS::Role.update_policy(arn, doc, credentials: @credentials)
1262
1310
  end
1263
1311
 
1264
- # Update a policy, handling deletion of old versions as needed
1265
- def self.update_policy(arn, doc, credentials: nil)
1266
- # XXX this is just blindly replacing identical versions, when it should check
1267
- # and guard
1268
- begin
1269
- MU::Cloud::AWS.iam(credentials: credentials).create_policy_version(
1270
- policy_arn: arn,
1271
- set_as_default: true,
1272
- policy_document: JSON.generate(doc)
1273
- )
1274
- rescue Aws::IAM::Errors::LimitExceeded => e
1275
- delete_version = MU::Cloud::AWS.iam(credentials: credentials).list_policy_versions(
1276
- policy_arn: arn,
1277
- ).versions.last.version_id
1278
- MU.log "Purging oldest version (#{delete_version}) of IAM policy #{arn}", MU::NOTICE
1279
- MU::Cloud::AWS.iam(credentials: credentials).delete_policy_version(
1280
- policy_arn: arn,
1281
- version_id: delete_version
1282
- )
1283
- retry
1284
- end
1285
- end
1286
1312
 
1287
1313
  end
1288
1314
  end