miam 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1c277e2932203a2740972ef45fb3588f25e08ced
4
- data.tar.gz: 560435cc28118cc841397919249697a68a2e6851
3
+ metadata.gz: dac584f8c0f3829974bbf66a703a0514b0bba56b
4
+ data.tar.gz: 4c1554986b97d252d2e6fea8c17dbdee4e7c0072
5
5
  SHA512:
6
- metadata.gz: f631ea1267d5c62723f85129e26508dc712b5189ea53e47ada4aacbcbc4b75f22bf56971364dc7490734343d2e62d885a6bbf68636d622550495a35bda2585b5
7
- data.tar.gz: fa71e40019ca1342ec1472c07c1ae376b4233e1bda0129ed3715e9c151c751b43d1339e08f11018752ab080ed6cec1debcc79bc9a7104891f13f6eb99c4b3490
6
+ metadata.gz: a197de1545bb2bfdaa906a215d23e61eb4ff63759875929a6f01605a38c6a3c4d5bd3dfa753a6e77af7c4521e0ef5f22ac36a70c1d049c8ada9e4a211f969601
7
+ data.tar.gz: cad4724a8a76be2bfc6c92ebc5ef97bf95a5e2b08f97469ce2f1213b95cc8ac42c89d694d71cf2805b3ce7e93258a3212354e076455a1852351c81e1a1bb9c59
data/README.md CHANGED
@@ -63,7 +63,7 @@ Usage: miam [options]
63
63
  require 'other/iamfile'
64
64
 
65
65
  user "bob", :path => "/developer/" do
66
- login_profile password_reset_required: true
66
+ login_profile :password_reset_required=>true
67
67
 
68
68
  groups(
69
69
  "Admin"
@@ -81,7 +81,7 @@ user "bob", :path => "/developer/" do
81
81
  end
82
82
 
83
83
  user "mary", :path => "/staff/" do
84
- # login_profile password_reset_required: true
84
+ # login_profile :password_reset_required=>true
85
85
 
86
86
  groups(
87
87
  # no group
@@ -113,6 +113,28 @@ group "Admin", :path => "/admin/" do
113
113
  {"Statement"=>[{"Effect"=>"Allow", "Action"=>"*", "Resource"=>"*"}]}
114
114
  end
115
115
  end
116
+
117
+ role "S3", :path => "/" do
118
+ instance_profiles(
119
+ "S3"
120
+ )
121
+
122
+ assume_role_policy_document do
123
+ {"Version"=>"2012-10-17",
124
+ "Statement"=>
125
+ [{"Sid"=>"",
126
+ "Effect"=>"Allow",
127
+ "Principal"=>{"Service"=>"ec2.amazonaws.com"},
128
+ "Action"=>"sts:AssumeRole"}]}
129
+ end
130
+
131
+ policy "S3-role-policy" do
132
+ {"Version"=>"2012-10-17",
133
+ "Statement"=>[{"Effect"=>"Allow", "Action"=>"*", "Resource"=>"*"}]}
134
+ end
135
+ end
136
+
137
+ instance_profile "S3", :path => "/"
116
138
  ```
117
139
 
118
140
  ## Rename
data/bin/miam CHANGED
@@ -94,8 +94,9 @@ begin
94
94
  output_file = DEFAULT_FILENAME if output_file == '-'
95
95
  requires = []
96
96
 
97
- client.export do |users_or_groups, dsl|
98
- iam_file = File.join(File.dirname(output_file), "#{users_or_groups}.iam")
97
+ client.export do |type, dsl|
98
+ next if dsl.strip.empty?
99
+ iam_file = File.join(File.dirname(output_file), "#{type}.iam")
99
100
  requires << iam_file
100
101
  logger.info(" write `#{iam_file}`")
101
102
 
@@ -114,10 +115,10 @@ begin
114
115
  else
115
116
  if output_file == '-'
116
117
  logger.info('# Export IAM')
117
- puts client.export
118
+ puts client.export.strip
118
119
  else
119
120
  logger.info("Export IAM to `#{output_file}`")
120
- open(output_file, 'wb') {|f| f.puts client.export }
121
+ open(output_file, 'wb') {|f| f.puts client.export.strip }
121
122
  end
122
123
  end
123
124
  when :apply
data/lib/miam.rb CHANGED
@@ -15,6 +15,7 @@ require 'miam/driver'
15
15
  require 'miam/dsl'
16
16
  require 'miam/dsl/context'
17
17
  require 'miam/dsl/context/group'
18
+ require 'miam/dsl/context/role'
18
19
  require 'miam/dsl/context/user'
19
20
  require 'miam/dsl/converter'
20
21
  require 'miam/exporter'
data/lib/miam/client.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  class Miam::Client
2
+ include Miam::Logger::Helper
3
+
2
4
  def initialize(options = {})
3
5
  @options = options
4
6
  aws_config = options.delete(:aws_config) || {}
@@ -8,15 +10,15 @@ class Miam::Client
8
10
  end
9
11
 
10
12
  def export
11
- exported, group_users = Miam::Exporter.export(@iam, @options) do |export_options|
13
+ exported, group_users, instance_profile_roles = Miam::Exporter.export(@iam, @options) do |export_options|
12
14
  progress(*export_options.values_at(:progress_total, :progress))
13
15
  end
14
16
 
15
17
  if block_given?
16
- [:users, :groups].each do |users_or_groups|
17
- splitted = {:users => {}, :groups => {}}
18
- splitted[users_or_groups] = exported[users_or_groups]
19
- yield(users_or_groups, Miam::DSL.convert(splitted, @options).strip)
18
+ [:users, :groups, :roles, :instance_profiles].each do |type|
19
+ splitted = {:users => {}, :groups => {}, :roles => {}, :instance_profiles => {}}
20
+ splitted[type] = exported[type]
21
+ yield(type, Miam::DSL.convert(splitted, @options).strip)
20
22
  end
21
23
  else
22
24
  Miam::DSL.convert(exported, @options)
@@ -32,12 +34,14 @@ class Miam::Client
32
34
  def walk(file)
33
35
  expected = load_file(file)
34
36
 
35
- actual, group_users = Miam::Exporter.export(@iam, @options) do |export_options|
37
+ actual, group_users, instance_profile_roles = Miam::Exporter.export(@iam, @options) do |export_options|
36
38
  progress(*export_options.values_at(:progress_total, :progress))
37
39
  end
38
40
 
39
41
  updated = walk_groups(expected[:groups], actual[:groups], actual[:users], group_users)
40
42
  updated = walk_users(expected[:users], actual[:users], group_users) || updated
43
+ updated = walk_instance_profiles(expected[:instance_profiles], actual[:instance_profiles], actual[:roles], instance_profile_roles) || updated
44
+ updated = walk_roles(expected[:roles], actual[:roles], instance_profile_roles) || updated
41
45
 
42
46
  if @options[:dry_run]
43
47
  false
@@ -168,6 +172,125 @@ class Miam::Client
168
172
  walk_policies(:group, group_name, expected_attrs[:policies], actual_attrs[:policies])
169
173
  end
170
174
 
175
+ def walk_roles(expected, actual, instance_profile_roles)
176
+ updated = false
177
+
178
+ expected.each do |role_name, expected_attrs|
179
+ actual_attrs = actual.delete(role_name)
180
+
181
+ if actual_attrs
182
+ updated = walk_role(role_name, expected_attrs, actual_attrs) || updated
183
+ else
184
+ actual_attrs = @driver.create_role(role_name, expected_attrs)
185
+ walk_role(role_name, expected_attrs, actual_attrs)
186
+ updated = true
187
+ end
188
+ end
189
+
190
+ actual.each do |role_name, attrs|
191
+ instance_profile_names = []
192
+
193
+ instance_profile_roles.each do |instance_profile_name, roles|
194
+ if roles.include?(role_name)
195
+ instance_profile_names << instance_profile_name
196
+ end
197
+ end
198
+
199
+ @driver.delete_role(role_name, instance_profile_names, attrs)
200
+
201
+ instance_profile_roles.each do |instance_profile_name, roles|
202
+ roles.delete(role_name)
203
+ end
204
+
205
+ updated = true
206
+ end
207
+
208
+ updated
209
+ end
210
+
211
+ def walk_role(role_name, expected_attrs, actual_attrs)
212
+ if expected_attrs.values_at(:path) != actual_attrs.values_at(:path)
213
+ log(:warn, "Role `#{role_name}`: 'path' cannot be updated", :color => :yellow)
214
+ end
215
+
216
+ updated = walk_assume_role_policy(role_name, expected_attrs[:assume_role_policy_document], actual_attrs[:assume_role_policy_document])
217
+ updated = walk_role_instance_profiles(role_name, expected_attrs[:instance_profiles], actual_attrs[:instance_profiles]) || updated
218
+ walk_policies(:role, role_name, expected_attrs[:policies], actual_attrs[:policies]) || updated
219
+ end
220
+
221
+ def walk_assume_role_policy(role_name, expected_assume_role_policy, actual_assume_role_policy)
222
+ updated = false
223
+
224
+ if expected_assume_role_policy != actual_assume_role_policy
225
+ @driver.update_assume_role_policy(role_name, expected_assume_role_policy)
226
+ updated = true
227
+ end
228
+
229
+ updated
230
+ end
231
+
232
+ def walk_role_instance_profiles(role_name, expected_instance_profiles, actual_instance_profiles)
233
+ expected_instance_profiles = expected_instance_profiles.sort
234
+ actual_instance_profiles = actual_instance_profiles.sort
235
+ updated = false
236
+
237
+ if expected_instance_profiles != actual_instance_profiles
238
+ add_instance_profiles = expected_instance_profiles - actual_instance_profiles
239
+ remove_instance_profiles = actual_instance_profiles - expected_instance_profiles
240
+
241
+ unless add_instance_profiles.empty?
242
+ @driver.add_role_to_instance_profiles(role_name, add_instance_profiles)
243
+ end
244
+
245
+ unless remove_instance_profiles.empty?
246
+ @driver.remove_role_from_instance_profiles(role_name, remove_instance_profiles)
247
+ end
248
+
249
+ updated = true
250
+ end
251
+
252
+ updated
253
+ end
254
+
255
+ def walk_instance_profiles(expected, actual, actual_roles, instance_profile_roles)
256
+ updated = false
257
+
258
+ expected.each do |instance_profile_name, expected_attrs|
259
+ actual_attrs = actual.delete(instance_profile_name)
260
+
261
+ if actual_attrs
262
+ updated = walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs) || updated
263
+ else
264
+ actual_attrs = @driver.create_instance_profile(instance_profile_name, expected_attrs)
265
+ walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs)
266
+ updated = true
267
+ end
268
+ end
269
+
270
+ actual.each do |instance_profile_name, attrs|
271
+ roles_in_instance_profile = instance_profile_roles.delete(instance_profile_name) || []
272
+ @driver.delete_instance_profile(instance_profile_name, attrs, roles_in_instance_profile)
273
+
274
+ actual_roles.each do |role_name, role_attrs|
275
+ role_attrs[:instance_profiles].delete(instance_profile_name)
276
+ end
277
+
278
+ updated = true
279
+ end
280
+
281
+ updated
282
+ end
283
+
284
+ def walk_instance_profile(instance_profile_name, expected_attrs, actual_attrs)
285
+ updated = false
286
+
287
+ if expected_attrs != actual_attrs
288
+ log(:warn, "InstanceProfile `#{instance_profile_name}`: 'path' cannot be updated", :color => :yellow)
289
+ end
290
+
291
+ updated
292
+ end
293
+
171
294
  def scan_rename(type, expected, actual, group_users)
172
295
  updated = false
173
296
 
data/lib/miam/driver.rb CHANGED
@@ -151,6 +151,106 @@ class Miam::Driver
151
151
  end
152
152
  end
153
153
 
154
+ def create_role(role_name, attrs)
155
+ log(:info, "Create Role `#{role_name}`", :color => :cyan)
156
+ assume_role_policy_document = attrs.fetch(:assume_role_policy_document)
157
+
158
+ unless_dry_run do
159
+ params = {
160
+ :role_name => role_name,
161
+ :assume_role_policy_document => encode_document(assume_role_policy_document),
162
+ }
163
+
164
+ params[:path] = attrs[:path] if attrs[:path]
165
+ @iam.create_role(params)
166
+ end
167
+
168
+ new_role_attrs = {
169
+ :instance_profiles => [],
170
+ :assume_role_policy_document => assume_role_policy_document,
171
+ :policies => {}
172
+ }
173
+
174
+ new_role_attrs[:path] = attrs[:path] if attrs[:path]
175
+ new_role_attrs
176
+ end
177
+
178
+ def delete_role(role_name, instance_profile_names, attrs)
179
+ log(:info, "Delete Role `#{role_name}`", :color => :red)
180
+
181
+ unless_dry_run do
182
+ attrs[:policies].keys.each do |policy_name|
183
+ @iam.delete_role_policy(:role_name => role_name, :policy_name => policy_name)
184
+ end
185
+
186
+ instance_profile_names.each do |instance_profile_name|
187
+ @iam.remove_role_from_instance_profile(:instance_profile_name => instance_profile_name, :role_name => role_name)
188
+ end
189
+
190
+ @iam.delete_role(:role_name => role_name)
191
+ end
192
+ end
193
+
194
+ def add_role_to_instance_profiles(role_name, instance_profile_names)
195
+ log(:info, "Update Role `#{role_name}`", :color => :green)
196
+ log(:info, " add instance_profiles=#{instance_profile_names.join(',')}", :color => :green)
197
+
198
+ unless_dry_run do
199
+ instance_profile_names.each do |instance_profile_name|
200
+ @iam.add_role_to_instance_profile(:instance_profile_name => instance_profile_name, :role_name => role_name)
201
+ end
202
+ end
203
+ end
204
+
205
+ def remove_role_from_instance_profiles(role_name, instance_profile_names)
206
+ log(:info, "Update Role `#{role_name}`", :color => :green)
207
+ log(:info, " remove instance_profiles=#{instance_profile_names.join(',')}", :color => :green)
208
+
209
+ unless_dry_run do
210
+ instance_profile_names.each do |instance_profile_name|
211
+ @iam.remove_role_from_instance_profile(:instance_profile_name => instance_profile_name, :role_name => role_name)
212
+ end
213
+ end
214
+ end
215
+
216
+ def update_assume_role_policy(role_name, policy_document)
217
+ log(:info, "Update Role `#{role_name}` > AssumeRolePolicy", :color => :green)
218
+ log(:info, " #{policy_document.pretty_inspect.gsub("\n", "\n ").strip}", :color => :green)
219
+
220
+ unless_dry_run do
221
+ @iam.update_assume_role_policy(
222
+ :role_name => role_name,
223
+ :policy_document => encode_document(policy_document),
224
+ )
225
+ end
226
+ end
227
+
228
+ def create_instance_profile(instance_profile_name, attrs)
229
+ log(:info, "Create InstanceIrofile `#{instance_profile_name}`", :color => :cyan)
230
+
231
+ unless_dry_run do
232
+ params = {:instance_profile_name => instance_profile_name}
233
+ params[:path] = attrs[:path] if attrs[:path]
234
+ @iam.create_instance_profile(params)
235
+ end
236
+
237
+ new_instance_profile_attrs = {}
238
+ new_instance_profile_attrs[:path] = attrs[:path] if attrs[:path]
239
+ new_instance_profile_attrs
240
+ end
241
+
242
+ def delete_instance_profile(instance_profile_name, attrs, roles_in_instance_profile)
243
+ log(:info, "Delete InstanceProfile `#{instance_profile_name}`", :color => :red)
244
+
245
+ unless_dry_run do
246
+ roles_in_instance_profile.each do |role_name|
247
+ @iam.remove_role_from_instance_profile(:instance_profile_name => instance_profile_name, :role_name => role_name)
248
+ end
249
+
250
+ @iam.delete_instance_profile(:instance_profile_name => instance_profile_name)
251
+ end
252
+ end
253
+
154
254
  def update_name(type, user_or_group_name, new_name)
155
255
  log(:info, "Update #{Miam::Utils.camelize(type.to_s)} `#{user_or_group_name}`", :color => :green)
156
256
  log(:info, " set name=#{new_name}", :color => :green)
@@ -10,7 +10,7 @@ class Miam::DSL::Context
10
10
  def initialize(path, options = {}, &block)
11
11
  @path = path
12
12
  @options = options
13
- @result = {:users => {}, :groups => {}}
13
+ @result = {:users => {}, :groups => {}, :roles => {}, :instance_profiles => {}}
14
14
  instance_eval(&block)
15
15
  end
16
16
 
@@ -49,4 +49,25 @@ class Miam::DSL::Context
49
49
  attrs = Miam::DSL::Context::Group.new(name, &block).result
50
50
  @result[:groups][name] = group_options.merge(attrs)
51
51
  end
52
+
53
+ def role(name, role_options = {}, &block)
54
+ name = name.to_s
55
+
56
+ if @result[:roles][name]
57
+ raise "Role `#{name}` is already defined"
58
+ end
59
+
60
+ attrs = Miam::DSL::Context::Role.new(name, &block).result
61
+ @result[:roles][name] = role_options.merge(attrs)
62
+ end
63
+
64
+ def instance_profile(name, instance_profile_options = {}, &block)
65
+ name = name.to_s
66
+
67
+ if @result[:instance_profiles][name]
68
+ raise "instance_profile `#{name}` is already defined"
69
+ end
70
+
71
+ @result[:instance_profiles][name] = instance_profile_options
72
+ end
52
73
  end
@@ -1,6 +1,6 @@
1
1
  class Miam::DSL::Context::Group
2
2
  def initialize(name, &block)
3
- @name = name
3
+ @group_name = name
4
4
  @result = {:policies => {}}
5
5
  instance_eval(&block)
6
6
  end
@@ -13,13 +13,13 @@ class Miam::DSL::Context::Group
13
13
  name = name.to_s
14
14
 
15
15
  if @result[:policies][name]
16
- raise "Group `#{name}` > Policy `#{name}`: already defined"
16
+ raise "Group `#{@group_name}` > Policy `#{name}`: already defined"
17
17
  end
18
18
 
19
19
  policy_document = yield
20
20
 
21
21
  unless policy_document.kind_of?(Hash)
22
- raise "Group `#{name}` > Policy `#{name}`: wrong argument type #{policy_document.class} (expected Hash)"
22
+ raise "Group `#{@group_name}` > Policy `#{name}`: wrong argument type #{policy_document.class} (expected Hash)"
23
23
  end
24
24
 
25
25
  @result[:policies][name] = policy_document
@@ -0,0 +1,51 @@
1
+ class Miam::DSL::Context::Role
2
+ def initialize(name, &block)
3
+ @role_name = name
4
+ @result = {:instance_profiles => [], :policies => {}}
5
+ instance_eval(&block)
6
+ end
7
+
8
+ def result
9
+ unless @result[:assume_role_policy_document]
10
+ raise "Role `#{@role_name}`: AssumeRolePolicyDocument is not defined"
11
+ end
12
+
13
+ @result
14
+ end
15
+
16
+ private
17
+
18
+ def instance_profiles(*profiles)
19
+ @result[:instance_profiles].concat(profiles.map {|i| i.to_s })
20
+ end
21
+
22
+ def assume_role_policy_document
23
+ if @result[:assume_role_policy_document]
24
+ raise "Role `#{@role_name}` > AssumeRolePolicyDocument: already defined"
25
+ end
26
+
27
+ assume_role_policy_document = yield
28
+
29
+ unless assume_role_policy_document.kind_of?(Hash)
30
+ raise "Role `#{@role_name}` > AssumeRolePolicyDocument: wrong argument type #{policy_document.class} (expected Hash)"
31
+ end
32
+
33
+ @result[:assume_role_policy_document] = assume_role_policy_document
34
+ end
35
+
36
+ def policy(name)
37
+ name = name.to_s
38
+
39
+ if @result[:policies][name]
40
+ raise "Role `#{@role_name}` > Policy `#{name}`: already defined"
41
+ end
42
+
43
+ policy_document = yield
44
+
45
+ unless policy_document.kind_of?(Hash)
46
+ raise "Role `#{@role_name}` > Policy `#{name}`: wrong argument type #{policy_document.class} (expected Hash)"
47
+ end
48
+
49
+ @result[:policies][name] = policy_document
50
+ end
51
+ end