cloud-mu 3.1.6 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +15 -12
  4. data/bin/mu-azure-tests +57 -0
  5. data/bin/mu-cleanup +2 -4
  6. data/bin/mu-configure +37 -1
  7. data/bin/mu-deploy +3 -3
  8. data/bin/mu-findstray-tests +25 -0
  9. data/bin/mu-gen-docs +2 -4
  10. data/bin/mu-load-config.rb +2 -1
  11. data/bin/mu-run-tests +37 -12
  12. data/cloud-mu.gemspec +4 -4
  13. data/cookbooks/mu-tools/attributes/default.rb +7 -0
  14. data/cookbooks/mu-tools/libraries/helper.rb +87 -3
  15. data/cookbooks/mu-tools/recipes/apply_security.rb +39 -23
  16. data/cookbooks/mu-tools/recipes/aws_api.rb +13 -0
  17. data/cookbooks/mu-tools/recipes/google_api.rb +4 -0
  18. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  19. data/cookbooks/mu-tools/resources/disk.rb +33 -12
  20. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  21. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  22. data/extras/clean-stock-amis +10 -2
  23. data/extras/generate-stock-images +7 -3
  24. data/extras/image-generators/AWS/centos7.yaml +19 -16
  25. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  26. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  27. data/modules/mommacat.ru +2 -2
  28. data/modules/mu.rb +84 -97
  29. data/modules/mu/adoption.rb +359 -59
  30. data/modules/mu/cleanup.rb +67 -44
  31. data/modules/mu/cloud.rb +108 -1754
  32. data/modules/mu/cloud/database.rb +49 -0
  33. data/modules/mu/cloud/dnszone.rb +44 -0
  34. data/modules/mu/cloud/machine_images.rb +212 -0
  35. data/modules/mu/cloud/providers.rb +81 -0
  36. data/modules/mu/cloud/resource_base.rb +929 -0
  37. data/modules/mu/cloud/server.rb +40 -0
  38. data/modules/mu/cloud/server_pool.rb +1 -0
  39. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  40. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  41. data/modules/mu/cloud/wrappers.rb +178 -0
  42. data/modules/mu/config.rb +122 -80
  43. data/modules/mu/config/alarm.rb +2 -6
  44. data/modules/mu/config/bucket.rb +32 -3
  45. data/modules/mu/config/cache_cluster.rb +2 -2
  46. data/modules/mu/config/cdn.rb +100 -0
  47. data/modules/mu/config/collection.rb +1 -1
  48. data/modules/mu/config/container_cluster.rb +2 -2
  49. data/modules/mu/config/database.rb +84 -105
  50. data/modules/mu/config/database.yml +1 -2
  51. data/modules/mu/config/dnszone.rb +5 -4
  52. data/modules/mu/config/doc_helpers.rb +4 -5
  53. data/modules/mu/config/endpoint.rb +2 -1
  54. data/modules/mu/config/firewall_rule.rb +3 -19
  55. data/modules/mu/config/folder.rb +1 -1
  56. data/modules/mu/config/function.rb +17 -8
  57. data/modules/mu/config/group.rb +1 -1
  58. data/modules/mu/config/habitat.rb +1 -1
  59. data/modules/mu/config/job.rb +89 -0
  60. data/modules/mu/config/loadbalancer.rb +57 -11
  61. data/modules/mu/config/log.rb +1 -1
  62. data/modules/mu/config/msg_queue.rb +1 -1
  63. data/modules/mu/config/nosqldb.rb +1 -1
  64. data/modules/mu/config/notifier.rb +8 -19
  65. data/modules/mu/config/ref.rb +81 -9
  66. data/modules/mu/config/role.rb +1 -1
  67. data/modules/mu/config/schema_helpers.rb +30 -34
  68. data/modules/mu/config/search_domain.rb +1 -1
  69. data/modules/mu/config/server.rb +5 -13
  70. data/modules/mu/config/server_pool.rb +3 -7
  71. data/modules/mu/config/storage_pool.rb +1 -1
  72. data/modules/mu/config/tail.rb +10 -0
  73. data/modules/mu/config/user.rb +1 -1
  74. data/modules/mu/config/vpc.rb +13 -17
  75. data/modules/mu/defaults/AWS.yaml +106 -106
  76. data/modules/mu/defaults/Azure.yaml +1 -0
  77. data/modules/mu/defaults/Google.yaml +1 -0
  78. data/modules/mu/deploy.rb +33 -19
  79. data/modules/mu/groomer.rb +15 -0
  80. data/modules/mu/groomers/chef.rb +3 -0
  81. data/modules/mu/logger.rb +120 -144
  82. data/modules/mu/master.rb +22 -1
  83. data/modules/mu/mommacat.rb +71 -26
  84. data/modules/mu/mommacat/daemon.rb +23 -14
  85. data/modules/mu/mommacat/naming.rb +82 -3
  86. data/modules/mu/mommacat/search.rb +59 -16
  87. data/modules/mu/mommacat/storage.rb +119 -48
  88. data/modules/mu/{clouds → providers}/README.md +1 -1
  89. data/modules/mu/{clouds → providers}/aws.rb +248 -62
  90. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  91. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  92. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  93. data/modules/mu/providers/aws/cdn.rb +782 -0
  94. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  95. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +65 -63
  96. data/modules/mu/providers/aws/database.rb +1747 -0
  97. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  98. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  99. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  100. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  101. data/modules/mu/{clouds → providers}/aws/function.rb +291 -133
  102. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  103. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  104. data/modules/mu/providers/aws/job.rb +469 -0
  105. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  106. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  107. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  108. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  109. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  110. data/modules/mu/{clouds → providers}/aws/role.rb +112 -78
  111. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  112. data/modules/mu/{clouds → providers}/aws/server.rb +120 -145
  113. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  114. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  115. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  116. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  117. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  118. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  119. data/modules/mu/{clouds → providers}/aws/vpc.rb +141 -73
  120. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  121. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  122. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  123. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  124. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  126. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  127. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  128. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  129. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  130. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  132. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  133. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  134. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  135. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  136. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  137. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  138. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  142. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  143. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  144. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  145. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  146. data/modules/mu/{clouds → providers}/google.rb +15 -6
  147. data/modules/mu/{clouds → providers}/google/bucket.rb +2 -2
  148. data/modules/mu/{clouds → providers}/google/container_cluster.rb +29 -14
  149. data/modules/mu/{clouds → providers}/google/database.rb +2 -9
  150. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +3 -3
  151. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  152. data/modules/mu/{clouds → providers}/google/function.rb +4 -4
  153. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  154. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  155. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +2 -2
  156. data/modules/mu/{clouds → providers}/google/role.rb +46 -35
  157. data/modules/mu/{clouds → providers}/google/server.rb +26 -11
  158. data/modules/mu/{clouds → providers}/google/server_pool.rb +11 -11
  159. data/modules/mu/{clouds → providers}/google/user.rb +32 -22
  160. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  161. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  162. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  163. data/modules/mu/{clouds → providers}/google/vpc.rb +38 -3
  164. data/modules/tests/aws-jobs-functions.yaml +46 -0
  165. data/modules/tests/centos6.yaml +15 -0
  166. data/modules/tests/centos7.yaml +15 -0
  167. data/modules/tests/centos8.yaml +12 -0
  168. data/modules/tests/ecs.yaml +2 -2
  169. data/modules/tests/eks.yaml +1 -1
  170. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  171. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  172. data/modules/tests/microservice_app.yaml +288 -0
  173. data/modules/tests/rds.yaml +108 -0
  174. data/modules/tests/regrooms/rds.yaml +123 -0
  175. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  176. data/modules/tests/super_complex_bok.yml +2 -2
  177. data/modules/tests/super_simple_bok.yml +2 -2
  178. data/spec/mu/clouds/azure_spec.rb +2 -2
  179. metadata +126 -98
  180. data/modules/mu/clouds/aws/database.rb +0 -1974
  181. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -27,8 +27,8 @@ module MU
27
27
 
28
28
  # Called automatically by {MU::Deploy#createResources}
29
29
  def create
30
- MU::Cloud::AWS.sns(region: @config['region'], credentials: @config['credentials']).create_topic(name: @mu_name)
31
30
  @cloud_id = @mu_name
31
+ MU::Cloud::AWS.sns(region: @config['region'], credentials: @config['credentials']).create_topic(name: @cloud_id)
32
32
  MU.log "Created SNS topic #{@mu_name}"
33
33
  end
34
34
 
@@ -36,17 +36,48 @@ module MU
36
36
  def groom
37
37
  if @config['subscriptions']
38
38
  @config['subscriptions'].each { |sub|
39
- MU::Cloud::AWS::Notifier.subscribe(
40
- arn: arn,
41
- endpoint: sub['endpoint'],
42
- region: @config['region'],
43
- credentials: @config['credentials'],
44
- protocol: sub['type']
45
- )
39
+ if sub['resource'] and !sub['endpoint']
40
+ endpoint_obj = nil
41
+ MU.retrier([], max: 5, wait: 9, loop_if: Proc.new { endpoint_obj.nil? }) {
42
+ endpoint_obj = MU::Config::Ref.get(sub['resource']).kitten(@deploy)
43
+ }
44
+ sub['endpoint'] = endpoint_obj.arn
45
+ end
46
+ subscribe(sub['endpoint'], sub['type'])
46
47
  }
47
48
  end
48
49
  end
49
50
 
51
+ # Subscribe something to this SNS topic
52
+ # @param endpoint [String]: The address, identifier, or ARN of the resource being subscribed
53
+ # @param protocol [String]: The protocol being subscribed
54
+ def subscribe(endpoint, protocol)
55
+ self.class.subscribe(arn, endpoint, protocol, region: @config['region'], credentials: @credentials)
56
+ end
57
+
58
+ # Subscribe something to an SNS topic
59
+ # @param cloud_id [String]: The short name or ARN of an existing SNS topic
60
+ # @param endpoint [String]: The address, identifier, or ARN of the resource being subscribed
61
+ # @param protocol [String]: The protocol being subscribed
62
+ # @param region [String]: The region of the target SNS topic
63
+ # @param credentials [String]:
64
+ def self.subscribe(cloud_id, endpoint, protocol, region: nil, credentials: nil)
65
+ topic = find(cloud_id: cloud_id, region: region, credentials: credentials).values.first
66
+ if !topic
67
+ raise MuError, "Failed to find SNS Topic #{cloud_id} in #{region}"
68
+ end
69
+ arn = topic["TopicArn"]
70
+
71
+ resp = MU::Cloud::AWS.sns(region: region, credentials: credentials).list_subscriptions_by_topic(topic_arn: arn).subscriptions
72
+
73
+ resp.each { |subscription|
74
+ return subscription if subscription.protocol == protocol and subscription.endpoint == endpoint
75
+ }
76
+
77
+ MU.log "Subscribing #{endpoint} (#{protocol}) to SNS topic #{arn}", MU::NOTICE
78
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).subscribe(topic_arn: arn, protocol: protocol, endpoint: endpoint)
79
+ end
80
+
50
81
  # Does this resource type exist as a global (cloud-wide) artifact, or
51
82
  # is it localized to a region/zone?
52
83
  # @return [Boolean]
@@ -65,12 +96,12 @@ module MU
65
96
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
66
97
  # @param region [String]: The cloud provider region
67
98
  # @return [void]
68
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
99
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
69
100
  MU.log "AWS::Notifier.cleanup: need to support flags['known']", MU::DEBUG, details: flags
70
101
  MU.log "Placeholder: AWS Notifier artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
71
102
 
72
103
  MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
73
- if topic.topic_arn.match(MU.deploy_id)
104
+ if topic.topic_arn.match(deploy_id)
74
105
  # 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.
75
106
  # This may fail to find notifier groups in some cases (eg. cache_cluster) so we might want to delete from each API as well.
76
107
  MU.log "Deleting SNS topic: #{topic.topic_arn}"
@@ -91,6 +122,7 @@ module MU
91
122
  # Return the metadata for this user cofiguration
92
123
  # @return [Hash]
93
124
  def notify
125
+ return nil if !@cloud_id or !cloud_desc(use_cache: false)
94
126
  desc = MU::Cloud::AWS.sns(region: @config["region"], credentials: @config["credentials"]).get_topic_attributes(topic_arn: arn).attributes
95
127
  MU.structToHash(desc)
96
128
  end
@@ -101,9 +133,16 @@ module MU
101
133
  found = {}
102
134
 
103
135
  if args[:cloud_id]
104
- arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(args[:region]) ? "aws-us-gov" : "aws")+":sns:"+args[:region]+":"+MU::Cloud::AWS.credToAcct(args[:credentials])+":"+args[:cloud_id]
105
- desc = MU::Cloud::AWS.sns(region: args[:region], credentials: args[:credentials]).get_topic_attributes(topic_arn: arn).attributes
106
- found[args[:cloud_id]] = desc if desc
136
+ arn = if args[:cloud_id].match(/^arn:/)
137
+ args[:cloud_id]
138
+ else
139
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(args[:region]) ? "aws-us-gov" : "aws")+":sns:"+args[:region]+":"+MU::Cloud::AWS.credToAcct(args[:credentials])+":"+args[:cloud_id]
140
+ end
141
+ begin
142
+ desc = MU::Cloud::AWS.sns(region: args[:region], credentials: args[:credentials]).get_topic_attributes(topic_arn: arn).attributes
143
+ found[args[:cloud_id]] = desc if desc
144
+ rescue ::Aws::SNS::Errors::NotFound
145
+ end
107
146
  else
108
147
  next_token = nil
109
148
  begin
@@ -120,6 +159,58 @@ module MU
120
159
  found
121
160
  end
122
161
 
162
+ # Reverse-map our cloud description into a runnable config hash.
163
+ # We assume that any values we have in +@config+ are placeholders, and
164
+ # calculate our own accordingly based on what's live in the cloud.
165
+ def toKitten(**_args)
166
+ bok = {
167
+ "cloud" => "AWS",
168
+ "credentials" => @config['credentials'],
169
+ "cloud_id" => @cloud_id,
170
+ "region" => @config['region']
171
+ }
172
+
173
+ if !cloud_desc
174
+ MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
175
+ return nil
176
+ end
177
+
178
+ bok['name'] = cloud_desc["DisplayName"].empty? ? @cloud_id : cloud_desc["DisplayName"]
179
+ svcmap = {
180
+ "lambda" => "functions",
181
+ "sqs" => "msg_queues"
182
+ }
183
+ MU::Cloud::AWS.sns(region: @config['region'], credentials: @credentials).list_subscriptions_by_topic(topic_arn: cloud_desc["TopicArn"]).subscriptions.each { |sub|
184
+ bok['subscriptions'] ||= []
185
+
186
+ bok['subscriptions'] << if sub.endpoint.match(/^arn:[^:]+:(sqs|lambda):([^:]+):(\d+):.*?([^:\/]+)$/)
187
+ _wholestring, service, region, account, id = Regexp.last_match.to_a
188
+ {
189
+ "type" => sub.protocol,
190
+ "resource" => MU::Config::Ref.get(
191
+ type: svcmap[service],
192
+ region: region,
193
+ credentials: @credentials,
194
+ id: id,
195
+ cloud: "AWS",
196
+ habitat: MU::Config::Ref.get(
197
+ id: account,
198
+ cloud: "AWS",
199
+ credentials: @credentials
200
+ )
201
+ )
202
+ }
203
+ else
204
+ {
205
+ "type" => sub.protocol,
206
+ "endpoint" => sub.endpoint
207
+ }
208
+ end
209
+ }
210
+
211
+ bok
212
+ end
213
+
123
214
  # Cloud-specific configuration properties.
124
215
  # @param _config [MU::Config]: The calling MU::Config object
125
216
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
@@ -130,11 +221,10 @@ module MU
130
221
  "type" => "array",
131
222
  "items" => {
132
223
  "type" => "object",
133
- "required" => ["endpoint"],
134
224
  "properties" => {
135
225
  "type" => {
136
226
  "type" => "string",
137
- "description" => "",
227
+ "description" => "Type of endpoint or resource which should receive notifications. If not specified, will attempt to auto-detect.",
138
228
  "enum" => ["http", "https", "email", "email-json", "sms", "sqs", "application", "lambda"]
139
229
  }
140
230
  }
@@ -148,26 +238,42 @@ module MU
148
238
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::notifier}, bare and unvalidated.
149
239
 
150
240
  # @param notifier [Hash]: The resource to process and validate
151
- # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
241
+ # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
152
242
  # @return [Boolean]: True if validation succeeded, False otherwise
153
- def self.validateConfig(notifier, _configurator)
243
+ def self.validateConfig(notifier, configurator)
154
244
  ok = true
155
245
 
156
246
  if notifier['subscriptions']
157
247
  notifier['subscriptions'].each { |sub|
248
+ if sub['resource'] and configurator.haveLitterMate?(sub['resource']['name'], sub['resource']['type'])
249
+ sub['resource']['cloud'] = "AWS"
250
+ MU::Config.addDependency(notifier, sub['resource']['name'], sub['resource']['type'])
251
+ end
158
252
  if !sub["type"]
159
- if sub["endpoint"].match(/^http:/i)
160
- sub["type"] = "http"
161
- elsif sub["endpoint"].match(/^https:/i)
162
- sub["type"] = "https"
163
- elsif sub["endpoint"].match(/^sqs:/i)
164
- sub["type"] = "sqs"
165
- elsif sub["endpoint"].match(/^\+?[\d\-]+$/)
166
- sub["type"] = "sms"
167
- elsif sub["endpoint"].match(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
168
- sub["type"] = "email"
169
- else
170
- MU.log "Notifier #{notifier['name']} subscription #{sub['endpoint']} did not specify a type, and I'm unable to guess one", MU::ERR
253
+ sub['type'] = if sub['resource']
254
+ if sub['resource']['type'] == "functions"
255
+ "lambda"
256
+ elsif sub['resource']['type'] == "msg_queues"
257
+ "sqs"
258
+ end
259
+ elsif sub['endpoint']
260
+ if sub["endpoint"].match(/^http:/i)
261
+ "http"
262
+ elsif sub["endpoint"].match(/^https:/i)
263
+ "https"
264
+ elsif sub["endpoint"].match(/:sqs:/i)
265
+ "sqs"
266
+ elsif sub["endpoint"].match(/:lambda:/i)
267
+ "lambda"
268
+ elsif sub["endpoint"].match(/^\+?[\d\-]+$/)
269
+ "sms"
270
+ elsif sub["endpoint"].match(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
271
+ "email"
272
+ end
273
+ end
274
+
275
+ if !sub['type']
276
+ MU.log "Notifier #{notifier['name']} subscription did not specify a type, and I'm unable to guess one", MU::ERR, details: sub
171
277
  ok = false
172
278
  end
173
279
  end
@@ -178,40 +284,6 @@ module MU
178
284
  end
179
285
 
180
286
 
181
- # Subscribe to a notifier group. This can either be an email address, SQS queue, application endpoint, etc...
182
- # Will create the subscription only if it doesn't already exist.
183
- # @param arn [String]: The cloud provider's identifier of the notifier group.
184
- # @param protocol [String]: The type of the subscription (eg. email,https, etc..).
185
- # @param endpoint [String]: The endpoint of the subscription. This will depend on the 'protocol' (as an example if protocol is email, endpoint will be the email address) ..
186
- # @param region [String]: The cloud provider region.
187
- def self.subscribe(arn: nil, protocol: nil, endpoint: nil, region: MU.curRegion, credentials: nil)
188
- retries = 0
189
- begin
190
- resp = MU::Cloud::AWS.sns(region: region, credentials: credentials).list_subscriptions_by_topic(topic_arn: arn).subscriptions
191
- rescue Aws::SNS::Errors::NotFound
192
- if retries < 5
193
- MU.log "Couldn't find topic #{arn}, retrying several times in case of a lagging resource"
194
- retries += 1
195
- sleep 30
196
- retry
197
- else
198
- raise MuError, "Couldn't find topic #{arn}, giving up"
199
- end
200
- end
201
-
202
- already_subscribed = false
203
- if resp && !resp.empty?
204
- resp.each { |subscription|
205
- already_subscribed = true if subscription.protocol == protocol && subscription.endpoint == endpoint
206
- }
207
- end
208
-
209
- unless already_subscribed
210
- MU::Cloud::AWS.sns(region: region, credentials: credentials).subscribe(topic_arn: arn, protocol: protocol, endpoint: endpoint)
211
- MU.log "Subscribed #{endpoint} to SNS topic #{arn}"
212
- end
213
- end
214
-
215
287
  # Test if a notifier group exists
216
288
  # Create a new notifier group. Will check if the group exists before creating it.
217
289
  # @param topic_name [String]: The cloud provider's name for the notifier group.
@@ -30,7 +30,7 @@ module MU
30
30
  end
31
31
  end
32
32
 
33
- @mu_name ||= @deploy.getResourceName(@config["name"])
33
+ @mu_name ||= @deploy.getResourceName(@config["name"], max_length: 64)
34
34
  end
35
35
 
36
36
  # Called automatically by {MU::Deploy#createResources}
@@ -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
- MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
46
+ MU::Cloud::AWS.iam(credentials: @credentials).create_policy(
47
47
  policy_name: policy_name,
48
48
  path: "/"+@deploy.deploy_id+"/",
49
49
  policy_document: JSON.generate(policy.values.first),
@@ -53,16 +53,18 @@ module MU
53
53
  end
54
54
 
55
55
  if !@config['bare_policies']
56
- MU.log "Creating IAM role #{@mu_name}"
57
56
  @cloud_id = @mu_name
58
57
  path = @config['strip_path'] ? nil : "/"+@deploy.deploy_id+"/"
59
- MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
60
- path: path,
61
- role_name: @mu_name,
62
- description: "Generated by Mu",
63
- assume_role_policy_document: gen_assume_role_policy_doc,
64
- tags: get_tag_params
65
- )
58
+ params = {
59
+ :path => path,
60
+ :role_name => @mu_name,
61
+ :description => "Generated by Mu",
62
+ :assume_role_policy_document => gen_assume_role_policy_doc,
63
+ :tags => get_tag_params
64
+ }
65
+
66
+ MU.log "Creating IAM role #{@mu_name} (#{@credentials})", details: params
67
+ MU::Cloud::AWS.iam(credentials: @credentials).create_role(params)
66
68
  end
67
69
  end
68
70
 
@@ -75,7 +77,7 @@ module MU
75
77
  end
76
78
 
77
79
  if !@config['bare_policies']
78
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_role(
80
+ resp = MU::Cloud::AWS.iam(credentials: @credentials).get_role(
79
81
  role_name: @mu_name
80
82
  ).role
81
83
  ext_tags = resp.tags.map { |t| t.to_h }
@@ -84,7 +86,7 @@ module MU
84
86
 
85
87
  if tag_param.size > 0
86
88
  MU.log "Updating tags on IAM role #{@mu_name}", MU::NOTICE, details: tag_param
87
- MU::Cloud::AWS.iam(credentials: @config['credentials']).tag_role(role_name: @mu_name, tags: tag_param)
89
+ MU::Cloud::AWS.iam(credentials: @credentials).tag_role(role_name: @mu_name, tags: tag_param)
88
90
  end
89
91
  end
90
92
 
@@ -92,13 +94,14 @@ module MU
92
94
  configured_policies = []
93
95
 
94
96
  if @config['raw_policies']
97
+ MU.log "Attaching #{@config['raw_policies'].size.to_s} raw #{@config['raw_policies'].size > 1 ? "policies" : "policy"} to role #{@mu_name}", MU::NOTICE
95
98
  configured_policies = @config['raw_policies'].map { |p|
96
99
  @mu_name+"-"+p.keys.first.upcase
97
100
  }
98
101
  end
99
102
 
100
103
  if @config['attachable_policies']
101
- MU.log "Attaching #{@config['attachable_policies'].size.to_s} #{@config['attachable_policies'].size > 1 ? "policies" : "policy"} to role #{@mu_name}", MU::NOTICE
104
+ MU.log "Attaching #{@config['attachable_policies'].size.to_s} external #{@config['attachable_policies'].size > 1 ? "policies" : "policy"} to role #{@mu_name}", MU::NOTICE
102
105
  configured_policies.concat(@config['attachable_policies'].map { |p|
103
106
  id = if p.is_a?(MU::Config::Ref)
104
107
  p.cloud_id
@@ -109,18 +112,17 @@ module MU
109
112
  end
110
113
  id.gsub(/.*?\/([^:\/]+)$/, '\1')
111
114
  })
112
- configured_policies.each { |pol|
113
- }
114
115
  end
115
116
 
117
+ # Purge anything that doesn't belong
116
118
  if !@config['bare_policies']
117
- attached_policies = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_role_policies(
119
+ attached_policies = MU::Cloud::AWS.iam(credentials: @credentials).list_attached_role_policies(
118
120
  role_name: @mu_name
119
121
  ).attached_policies
120
122
  attached_policies.each { |a|
121
123
  if !configured_policies.include?(a.policy_name)
122
- MU.log "Removing IAM policy #{a.policy_name} from role #{@mu_name}", MU::NOTICE
123
- MU::Cloud::AWS::Role.purgePolicy(a.policy_arn, @config['credentials'])
124
+ MU.log "Removing IAM policy #{a.policy_name} from role #{@mu_name}", MU::NOTICE, details: configured_policies
125
+ MU::Cloud::AWS::Role.purgePolicy(a.policy_arn, @credentials)
124
126
  end
125
127
  }
126
128
  end
@@ -153,8 +155,8 @@ module MU
153
155
  policy.values.each { |p|
154
156
  p["Version"] ||= "2012-10-17"
155
157
  }
156
- policy_name = basename+"-"+policy.keys.first.upcase
157
158
 
159
+ policy_name = basename+"-"+policy.keys.first.upcase
158
160
  arn = "arn:"+(MU::Cloud::AWS.isGovCloud? ? "aws-us-gov" : "aws")+":iam::"+MU::Cloud::AWS.credToAcct(credentials)+":policy#{path}/#{policy_name}"
159
161
  resp = begin
160
162
  desc = MU::Cloud::AWS.iam(credentials: credentials).get_policy(policy_arn: arn)
@@ -184,12 +186,17 @@ module MU
184
186
 
185
187
  rescue Aws::IAM::Errors::NoSuchEntity
186
188
  MU.log "Creating IAM policy #{policy_name}", details: policy.values.first
187
- MU::Cloud::AWS.iam(credentials: credentials).create_policy(
189
+ desc = MU::Cloud::AWS.iam(credentials: credentials).create_policy(
188
190
  policy_name: policy_name,
189
191
  path: path+"/",
190
192
  policy_document: JSON.generate(policy.values.first),
191
193
  description: "Raw policy from #{basename}"
192
194
  )
195
+ MU.retrier([Aws::IAM::Errors::NoSuchEntity], loop_if: Proc.new { desc.nil? }) {
196
+ desc = MU::Cloud::AWS.iam(credentials: credentials).get_policy(policy_arn: arn)
197
+ pp desc
198
+ }
199
+ desc
193
200
  end
194
201
  arns << resp.policy.arn
195
202
  }
@@ -216,7 +223,22 @@ module MU
216
223
  # populated with one or both depending on what this resource has
217
224
  # defined.
218
225
  def cloud_desc(use_cache: true)
219
- return @cloud_desc_cache if @cloud_desc_cache and use_cache
226
+
227
+ # we might inherit a naive cached description from the base cloud
228
+ # layer; rearrange it to our tastes
229
+ if @cloud_desc_cache.is_a?(::Aws::IAM::Types::Role)
230
+ new_desc = {
231
+ "role" => @cloud_desc_cache
232
+ }
233
+ @cloud_desc_cache = new_desc
234
+ elsif @cloud_desc_cache.is_a?(::Aws::IAM::Types::Policy)
235
+ new_desc = {
236
+ "policies" => [@cloud_desc_cache]
237
+ }
238
+ @cloud_desc_cache = new_desc
239
+ end
240
+
241
+ return @cloud_desc_cache if @cloud_desc_cache and !@cloud_desc_cache.empty? and use_cache
220
242
 
221
243
  @cloud_desc_cache = {}
222
244
  if @config['bare_policies']
@@ -301,7 +323,7 @@ end
301
323
  my_policies.each { |p|
302
324
  if p.policy_name == policy
303
325
  seen_policy = true
304
- old = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy_version(
326
+ old = MU::Cloud::AWS.iam(credentials: @credentials).get_policy_version(
305
327
  policy_arn: p.arn,
306
328
  version_id: p.default_version_id
307
329
  ).policy_version
@@ -419,14 +441,14 @@ end
419
441
  # @param noop [Boolean]: If true, will only print what would be done
420
442
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
421
443
  # @return [void]
422
- def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
444
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, credentials: nil, flags: {})
423
445
 
424
446
  resp = MU::Cloud::AWS.iam(credentials: credentials).list_policies(
425
- path_prefix: "/"+MU.deploy_id+"/"
447
+ path_prefix: "/"+deploy_id+"/"
426
448
  )
427
449
  if resp and resp.policies
428
450
  resp.policies.each { |policy|
429
- MU.log "Deleting IAM policy /#{MU.deploy_id}/#{policy.policy_name}"
451
+ MU.log "Deleting IAM policy /#{deploy_id}/#{policy.policy_name}"
430
452
  if !noop
431
453
  purgePolicy(policy.arn, credentials)
432
454
  end
@@ -437,19 +459,23 @@ end
437
459
  roles = MU::Cloud::AWS::Role.find(credentials: credentials).values
438
460
  roles.each { |r|
439
461
  next if !r.respond_to?(:role_name)
440
- if r.path.match(/^\/#{Regexp.quote(MU.deploy_id)}/)
462
+ if r.path.match(/^\/#{Regexp.quote(deploy_id)}/)
441
463
  deleteme << r
442
464
  next
443
465
  end
444
466
  # For some dumb reason, the list output that .find gets doesn't
445
467
  # include the tags, so we need to fetch each role individually to
446
468
  # check tags. Hardly seems efficient.
447
- desc = MU::Cloud::AWS.iam(credentials: credentials).get_role(role_name: r.role_name)
469
+ desc = begin
470
+ MU::Cloud::AWS.iam(credentials: credentials).get_role(role_name: r.role_name)
471
+ rescue Aws::IAM::Errors::NoSuchEntity
472
+ next
473
+ end
448
474
  if desc.role and desc.role.tags and desc.role.tags
449
475
  master_match = false
450
476
  deploy_match = false
451
477
  desc.role.tags.each { |t|
452
- if t.key == "MU-ID" and t.value == MU.deploy_id
478
+ if t.key == "MU-ID" and t.value == deploy_id
453
479
  deploy_match = true
454
480
  elsif t.key == "MU-MASTER-IP" and t.value == MU.mu_public_ip
455
481
  master_match = true
@@ -516,7 +542,7 @@ end
516
542
 
517
543
  begin
518
544
  # managed policies get fetched by ARN, roles by plain name. Ok!
519
- if args[:cloud_id].match(/^arn:/)
545
+ if args[:cloud_id].match(/^arn:.*?:policy\//)
520
546
  resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).get_policy(
521
547
  policy_arn: args[:cloud_id]
522
548
  )
@@ -525,39 +551,26 @@ end
525
551
  end
526
552
  else
527
553
  resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).get_role(
528
- role_name: args[:cloud_id]
554
+ role_name: args[:cloud_id].sub(/^arn:.*?\/([^:\/]+)$/, '\1') # XXX if it's an ARN, actually parse it and look in the correct account when applicable
529
555
  )
556
+
530
557
  if resp and resp.role
531
- found[args[:cloud_id]] = resp.role
558
+ found[resp.role.role_name] = resp.role
532
559
  end
533
560
  end
534
561
  rescue ::Aws::IAM::Errors::NoSuchEntity
535
562
  end
536
563
 
537
564
  else
538
- marker = nil
539
- begin
540
- resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).list_roles(
541
- marker: marker
542
- )
543
- break if !resp or !resp.roles
544
- resp.roles.each { |role|
545
- found[role.role_name] = role
546
- }
547
- marker = resp.marker
548
- end while marker
565
+ resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).list_roles
566
+ resp.roles.each { |role|
567
+ found[role.role_name] = role
568
+ }
549
569
 
550
- begin
551
- resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).list_policies(
552
- scope: "Local",
553
- marker: marker
554
- )
555
- break if !resp or !resp.policies
556
- resp.policies.each { |pol|
557
- found[pol.arn] = pol
558
- }
559
- marker = resp.marker
560
- end while marker
570
+ resp = MU::Cloud::AWS.iam(credentials: args[:credentials]).list_policies(scope: "Local")
571
+ resp.policies.each { |pol|
572
+ found[pol.arn] = pol
573
+ }
561
574
  end
562
575
 
563
576
  found
@@ -569,7 +582,7 @@ end
569
582
  def toKitten(**_args)
570
583
  bok = {
571
584
  "cloud" => "AWS",
572
- "credentials" => @config['credentials'],
585
+ "credentials" => @credentials,
573
586
  "cloud_id" => @cloud_id
574
587
  }
575
588
 
@@ -615,7 +628,6 @@ end
615
628
  )
616
629
  JSON.parse(URI.decode(version.policy_version.document))
617
630
  end
618
-
619
631
  bok["policies"] = MU::Cloud::AWS::Role.doc2MuPolicies(pol.policy_name, doc, bok["policies"])
620
632
  end
621
633
  }
@@ -695,6 +707,7 @@ end
695
707
  end
696
708
 
697
709
  bok["attachable_policies"].uniq! if bok["attachable_policies"]
710
+ bok["name"].gsub!(/[^a-zA-Z0-9_\-]/, "_")
698
711
 
699
712
  bok
700
713
  end
@@ -707,6 +720,10 @@ end
707
720
  def self.doc2MuPolicies(basename, doc, policies = [])
708
721
  policies ||= []
709
722
 
723
+ if !doc["Statement"].is_a?(Array)
724
+ doc["Statement"] = [doc["Statement"]]
725
+ end
726
+
710
727
  doc["Statement"].each { |s|
711
728
  if !s["Action"]
712
729
  MU.log "Statement in policy document for #{basename} didn't have an Action field", MU::WARN, details: doc
@@ -758,12 +775,12 @@ end
758
775
  def bindTo(entitytype, entityname)
759
776
  if entitytype == "instance_profile"
760
777
  begin
761
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
778
+ resp = MU::Cloud::AWS.iam(credentials: @credentials).get_instance_profile(
762
779
  instance_profile_name: entityname
763
780
  ).instance_profile
764
781
 
765
782
  if !resp.roles.map { |r| r.role_name}.include?(@mu_name)
766
- MU::Cloud::AWS.iam(credentials: @config['credentials']).add_role_to_instance_profile(
783
+ MU::Cloud::AWS.iam(credentials: @credentials).add_role_to_instance_profile(
767
784
  instance_profile_name: entityname,
768
785
  role_name: @mu_name
769
786
  )
@@ -773,7 +790,7 @@ end
773
790
  raise e
774
791
  end
775
792
  elsif ["user", "group", "role"].include?(entitytype)
776
- mypolicies = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_policies(
793
+ mypolicies = MU::Cloud::AWS.iam(credentials: @credentials).list_policies(
777
794
  path_prefix: "/"+@deploy.deploy_id+"/"
778
795
  ).policies
779
796
  mypolicies.reject! { |p|
@@ -791,7 +808,7 @@ end
791
808
 
792
809
  subpaths = ["service-role", "aws-service-role", "job-function"]
793
810
  begin
794
- mypolicies << MU::Cloud::AWS.iam(credentials: @config['credentials']).get_policy(
811
+ mypolicies << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
795
812
  policy_arn: p_arn
796
813
  ).policy
797
814
  rescue Aws::IAM::Errors::NoSuchEntity => e
@@ -804,39 +821,52 @@ end
804
821
  }
805
822
  end
806
823
 
824
+ if @config['raw_policies']
825
+ raw_arns = MU::Cloud::AWS::Role.manageRawPolicies(
826
+ @config['raw_policies'],
827
+ basename: @deploy.getResourceName(@config['name']),
828
+ credentials: @credentials
829
+ )
830
+ raw_arns.each { |p_arn|
831
+ mypolicies << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
832
+ policy_arn: p_arn
833
+ ).policy
834
+ }
835
+ end
836
+
807
837
  mypolicies.each { |p|
808
838
  if entitytype == "user"
809
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_user_policies(
839
+ resp = MU::Cloud::AWS.iam(credentials: @credentials).list_attached_user_policies(
810
840
  path_prefix: "/"+@deploy.deploy_id+"/",
811
841
  user_name: entityname
812
842
  )
813
843
  if !resp or !resp.attached_policies.map { |a_p| a_p.policy_name }.include?(p.policy_name)
814
844
  MU.log "Attaching IAM policy #{p.policy_name} to user #{entityname}", MU::NOTICE
815
- MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_user_policy(
845
+ MU::Cloud::AWS.iam(credentials: @credentials).attach_user_policy(
816
846
  policy_arn: p.arn,
817
847
  user_name: entityname
818
848
  )
819
849
  end
820
850
  elsif entitytype == "group"
821
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_group_policies(
851
+ resp = MU::Cloud::AWS.iam(credentials: @credentials).list_attached_group_policies(
822
852
  path_prefix: "/"+@deploy.deploy_id+"/",
823
853
  group_name: entityname
824
854
  )
825
855
  if !resp or !resp.attached_policies.map { |a_p| a_p.policy_name }.include?(p.policy_name)
826
856
  MU.log "Attaching policy #{p.policy_name} to group #{entityname}", MU::NOTICE
827
- MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_group_policy(
857
+ MU::Cloud::AWS.iam(credentials: @credentials).attach_group_policy(
828
858
  policy_arn: p.arn,
829
859
  group_name: entityname
830
860
  )
831
861
  end
832
862
  elsif entitytype == "role"
833
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).list_attached_role_policies(
863
+ resp = MU::Cloud::AWS.iam(credentials: @credentials).list_attached_role_policies(
834
864
  role_name: entityname
835
865
  )
836
866
 
837
867
  if !resp or !resp.attached_policies.map { |a_p| a_p.policy_name }.include?(p.policy_name)
838
868
  MU.log "Attaching policy #{p.policy_name} to role #{entityname}", MU::NOTICE
839
- MU::Cloud::AWS.iam(credentials: @config['credentials']).attach_role_policy(
869
+ MU::Cloud::AWS.iam(credentials: @credentials).attach_role_policy(
840
870
  policy_arn: p.arn,
841
871
  role_name: entityname
842
872
  )
@@ -857,19 +887,19 @@ end
857
887
  end
858
888
 
859
889
  resp = begin
860
- MU.log "Creating instance profile #{@mu_name} #{@config['credentials']}"
861
- MU::Cloud::AWS.iam(credentials: @config['credentials']).create_instance_profile(
890
+ MU.log "Creating instance profile #{@mu_name} #{@credentials}"
891
+ MU::Cloud::AWS.iam(credentials: @credentials).create_instance_profile(
862
892
  instance_profile_name: @mu_name
863
893
  )
864
894
  rescue Aws::IAM::Errors::EntityAlreadyExists
865
- MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
895
+ MU::Cloud::AWS.iam(credentials: @credentials).get_instance_profile(
866
896
  instance_profile_name: @mu_name
867
897
  )
868
898
  end
869
899
 
870
900
  # make sure it's really there before moving on
871
901
  begin
872
- MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(instance_profile_name: @mu_name)
902
+ MU::Cloud::AWS.iam(credentials: @credentials).get_instance_profile(instance_profile_name: @mu_name)
873
903
  rescue Aws::IAM::Errors::NoSuchEntity => e
874
904
  MU.log e.inspect, MU::WARN
875
905
  sleep 10
@@ -925,7 +955,7 @@ end
925
955
  toplevel_required = []
926
956
  aws_resource_types = MU::Cloud.resource_types.keys.reject { |t|
927
957
  begin
928
- MU::Cloud.loadCloudType("AWS", t)
958
+ MU::Cloud.resourceClass("AWS", t)
929
959
  false
930
960
  rescue MuCloudResourceNotImplemented
931
961
  true
@@ -1087,11 +1117,7 @@ end
1087
1117
  role['policies'].each { |policy|
1088
1118
  policy['targets'].each { |target|
1089
1119
  if target['type']
1090
- role['dependencies'] ||= []
1091
- role['dependencies'] << {
1092
- "name" => target['identifier'],
1093
- "type" => target['type']
1094
- }
1120
+ MU::Config.addDependency(role, target['identifier'], target['type'], no_create_wait: true)
1095
1121
  end
1096
1122
  }
1097
1123
  }
@@ -1107,13 +1133,14 @@ end
1107
1133
  # @param policies [Array<Hash>]: One or more policy chunks
1108
1134
  # @param deploy_obj [MU::MommaCat]: Deployment object to use when looking up sibling Mu resources
1109
1135
  # @return [Array<Hash>]
1110
- def self.genPolicyDocument(policies, deploy_obj: nil, bucket_style: false)
1136
+ def self.genPolicyDocument(policies, deploy_obj: nil, bucket_style: false, version: "2012-10-17", doc_id: nil)
1111
1137
  if policies
1112
1138
  name = nil
1113
1139
  doc = {
1114
- "Version" => "2012-10-17",
1140
+ "Version" => version,
1115
1141
  "Statement" => []
1116
1142
  }
1143
+ doc["Id"] = doc_id if doc_id
1117
1144
  policies.each { |policy|
1118
1145
  policy["flag"] ||= "Allow"
1119
1146
  statement = {
@@ -1154,7 +1181,14 @@ end
1154
1181
  raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["identifier"]} when generating IAM policy"
1155
1182
  end
1156
1183
  else
1157
- bucket_prefix = grantee["identifier"].match(/^[^\.]+\.amazonaws\.com$/) ? "Service" : "AWS"
1184
+ bucket_prefix = if grantee["identifier"].match(/^[^\.]+\.amazonaws\.com$/)
1185
+ "Service"
1186
+ elsif grantee["identifier"] =~ /^[a-f0-9]+$/
1187
+ "CanonicalUser"
1188
+ else
1189
+ "AWS"
1190
+ end
1191
+
1158
1192
  if bucket_style
1159
1193
  statement["Principal"] << { bucket_prefix => grantee["identifier"] }
1160
1194
  else