miam 0.2.3 → 0.2.5.beta1

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
- SHA1:
3
- metadata.gz: bb232253b3506a0387caa0817c099a76b0d5ec5f
4
- data.tar.gz: e10d873066aef538049bb703bf2977064c1ab586
2
+ SHA256:
3
+ metadata.gz: b0a35750c70a9a9f30ff1a1482a3da3a66c994e691fa3802b2c54992408415df
4
+ data.tar.gz: c33e5e326f59da9810b8735620cea049cc619b013b1e3525d86416e44dd91af1
5
5
  SHA512:
6
- metadata.gz: d07415f6abcf2efa6d0572bf61302ee6d5334cc76f2a7739dbde62cf8b3b0cc0ceece4cf9613bbcc66776e0ad3758bfb5cf75196a286cfe47d95de467f02fa33
7
- data.tar.gz: eb2ecd520a5d43173249082dfdb29dd0b31fa36acc254cb356a57b04d9dd2befc4caaae70f4a7376189ffda32395b425195f7f014c0f223501dc1ac96d324f9b
6
+ metadata.gz: ce11c734155e09c454e913ec7dc2d7ea8eb68cf60b0eb7803f0e01ae5b2469c865172e4a0a87f2e0ebb71d72fe9653b0b009d21a23913a4cd763b0ebbd1e6b72
7
+ data.tar.gz: e6001cb1c8c03903846f4d936e130f198fec72a695e086ab5ed36a8795be9891bd6f5143f4876b123d7cc5c27e50a846dc20f8a2ce521c66c61ed60546cea6d3
data/.rspec CHANGED
@@ -1,4 +1,5 @@
1
1
  --require rspec/instafail
2
- --format RSpec::Instafail
2
+ --format documentation
3
+ #RSpec::Instafail
3
4
  --colour
4
5
  --require spec_helper
data/.travis.yml CHANGED
@@ -1,12 +1,14 @@
1
1
  sudo: false
2
2
  language: ruby
3
+ cache: bundler
3
4
  rvm:
4
- - 2.0.0
5
+ - 2.3.4
5
6
  script:
6
7
  - bundle install
7
- - bundle exec rake
8
+ - travis_wait 60 bundle exec rake
8
9
  env:
9
10
  global:
10
- - secure: "Ec8bwSfp06anSzLJpGhkKjPz9EocMwl7H8t2LKoI3bKV/cz9JU0GhhUdUUmg3mCFH9+8/YGLzcbFKcxZIqnDK7ukdgTGsnGDevnctis0QwM7/nKHdkaK8JWhCx41TUXOqoiCKkNEnm1EbDtrILt4rJwb0BGXGnPjv6nLAyvdE2Y="
11
- - secure: "Ky5Dpxc7SIqbQ4Y10m/jik/rVdeGMeh1439m3KB+tiV/Bz1hhWv4+If150ajLjbmWCZBPMLhxrTL0EePoyqYXpOGHvINvYM+/XCPcpP4iYkaTzr1MVIOMpa8A8aAOOgQWoGOwfyWoxfrYzv/TRvtMietnm+dZwVcbbkQ4ut7oa4="
11
+ - secure: c5kyaYSGrKMg3bsVMCnIYe2w/3jJ4rxkoIwM06YGLmw5lPW0twWgpwpuBsGf6WKvDLv0eQjGq3B0I1DqHmdYHq4dQ+PdsvWg0kQUpzWpKD2ccVXevyPeDs5pC1UcLtDUpRk0Rv1Hpo4v5kiT2zsmLx29Z6F4n7agtzqK5Q2vdPs=
12
+ - secure: ipdvAzZ5EwTQui8CyINqRYLFNNe4ZyZP617xEmUS3YalhRMOEZ+NSq7BlqliE4IJPg8YlQjT5q35xFtREIr/KYS1tmU1/nFA68GC/9T+uJAsc3LSmUoYMn8Ot3654u1SjlZ86benyToZ2MgHglmHigS46SOoxBpqHrKMUW9RUz0=
12
13
  - AWS_REGION=ap-northeast-1
14
+ - APPLY_WAIT=10
data/README.md CHANGED
@@ -5,7 +5,7 @@ Miam is a tool to manage IAM.
5
5
  It defines the state of IAM using DSL, and updates IAM according to DSL.
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/miam.svg)](http://badge.fury.io/rb/miam)
8
- [![Build Status](https://travis-ci.org/winebarrel/miam.svg?branch=master)](https://travis-ci.org/winebarrel/miam)
8
+ [![Build Status](https://travis-ci.org/codenize-tools/miam.svg?branch=master)](https://travis-ci.org/codenize-tools/miam)
9
9
  [![Coverage Status](https://coveralls.io/repos/winebarrel/miam/badge.svg?branch=master&service=github)](https://coveralls.io/github/winebarrel/miam?branch=master)
10
10
 
11
11
  **Notice**
@@ -22,6 +22,12 @@ It defines the state of IAM using DSL, and updates IAM according to DSL.
22
22
  * Sort policy array
23
23
  * `>= 0.2.3`
24
24
  * Support Custom Managed Policy
25
+ * `>= 0.2.4`
26
+ * Fix for Password Policy ([RP#22](https://github.com/winebarrel/miam/pull/22))
27
+ * Fix `--target` option for Policies ([RP#21](https://github.com/winebarrel/miam/pull/21))
28
+ * Fix for `Rate exceeded` ([PR#23](https://github.com/winebarrel/miam/pull/23))
29
+ * Fix for non-User credentials ([PR#17](https://github.com/winebarrel/miam/pull/17))
30
+ * Add `--exclude` option
25
31
 
26
32
  ## Installation
27
33
 
@@ -71,6 +77,7 @@ Usage: miam [options]
71
77
  --format=FORMAT
72
78
  --export-concurrency N
73
79
  --target REGEXP
80
+ --exclude REGEXP
74
81
  --ignore-login-profile
75
82
  --no-color
76
83
  --no-progress
data/bin/miam CHANGED
@@ -19,10 +19,11 @@ MAGIC_COMMENT = <<-EOS
19
19
  EOS
20
20
 
21
21
  options = {
22
- :dry_run => false,
23
- :format => :ruby,
24
- :color => true,
25
- :debug => false,
22
+ :dry_run => false,
23
+ :format => :ruby,
24
+ :color => true,
25
+ :debug => false,
26
+ :show_backtrace => false
26
27
  }
27
28
 
28
29
  options[:password_manager] = Miam::PasswordManager.new(account_output, options)
@@ -51,11 +52,14 @@ ARGV.options do |opt|
51
52
  opt.on('' , '--split-more') { split = :more }
52
53
  opt.on('', '--format=FORMAT', [:ruby, :json]) {|v| format_passed = true; options[:format] = v }
53
54
  opt.on('' , '--export-concurrency N', Integer) {|v| options[:export_concurrency] = v }
54
- opt.on('' , '--target REGEXP') {|v| options[:target] = Regexp.new(v) }
55
+ opt.on('' , '--target REGEXP') {|v| (options[:target] ||= []) << Regexp.new(v) }
56
+ opt.on('' , '--exclude REGEXP') {|v| (options[:exclude] ||= []) << Regexp.new(v) }
55
57
  opt.on('' , '--ignore-login-profile') { options[:ignore_login_profile] = true }
58
+ opt.on('' , '--no-access-key') { options[:no_access_key] = true }
56
59
  opt.on('' , '--no-color') { options[:color] = false }
57
60
  opt.on('' , '--no-progress') { options[:no_progress] = true }
58
61
  opt.on('' , '--debug') { options[:debug] = true }
62
+ opt.on('' , '--show-backtrace') { options[:show_backtrace] = true }
59
63
  opt.parse!
60
64
 
61
65
  aws_opts = {}
@@ -184,6 +188,7 @@ rescue => e
184
188
  raise e
185
189
  else
186
190
  $stderr.puts("[ERROR] #{e.message}".red)
191
+ $stderr.puts(e.backtrace.map(&:yellow)) if options[:show_backtrace]
187
192
  exit 1
188
193
  end
189
194
  end
data/lib/miam/client.rb CHANGED
@@ -2,10 +2,14 @@ class Miam::Client
2
2
  include Miam::Logger::Helper
3
3
 
4
4
  def initialize(options = {})
5
- @options = {:format => :ruby}.merge(options)
5
+ @options = {
6
+ format: :ruby,
7
+ exclude: []
8
+ }.merge(options)
6
9
  aws_config = options.delete(:aws_config) || {}
7
10
  @iam = Aws::IAM::Client.new(aws_config)
8
- @driver = Miam::Driver.new(@iam, options)
11
+ @sts = Aws::STS::Client.new(aws_config)
12
+ @driver = Miam::Driver.new(@iam, @sts, options)
9
13
  @password_manager = options[:password_manager] || Miam::PasswordManager.new('-', options)
10
14
  end
11
15
 
@@ -57,6 +61,7 @@ class Miam::Client
57
61
 
58
62
  def walk(file)
59
63
  expected = load_file(file)
64
+ @options[:exclude] += expected[:exclude]
60
65
 
61
66
  actual, group_users, instance_profile_roles = Miam::Exporter.export(@iam, @options)
62
67
  updated = pre_walk_managed_policies(expected[:policies], actual[:policies])
@@ -86,7 +91,7 @@ class Miam::Client
86
91
  updated = walk_user(user_name, expected_attrs, actual_attrs) || updated
87
92
  else
88
93
  actual_attrs = @driver.create_user(user_name, expected_attrs)
89
- access_key = @driver.create_access_key(user_name)
94
+ access_key = @driver.create_access_key(user_name) unless @options[:no_access_key]
90
95
 
91
96
  if access_key
92
97
  @password_manager.puts_password(user_name, access_key[:access_key_id], access_key[:secret_access_key])
@@ -129,7 +134,7 @@ class Miam::Client
129
134
  end
130
135
 
131
136
  if expected_login_profile and not actual_login_profile
132
- expected_login_profile[:password] ||= @password_manager.identify(user_name, :login_profile)
137
+ expected_login_profile[:password] ||= @password_manager.identify(user_name, :login_profile, @driver.password_policy)
133
138
  @driver.create_login_profile(user_name, expected_login_profile)
134
139
  updated = true
135
140
  elsif not expected_login_profile and actual_login_profile
@@ -137,7 +142,7 @@ class Miam::Client
137
142
  updated = true
138
143
  elsif expected_login_profile != actual_login_profile
139
144
  if @options[:ignore_login_profile]
140
- log(:warn, "User `#{user_name}`: difference of loging profile has been ignored: expected=#{expected_login_profile.inspect}, actual=#{actual_login_profile.inspect}", :color => :yellow)
145
+ log(:warn, "User `#{user_name}`: difference of login profile has been ignored: expected=#{expected_login_profile.inspect}, actual=#{actual_login_profile.inspect}", :color => :yellow)
141
146
  else
142
147
  @driver.update_login_profile(user_name, expected_login_profile, actual_login_profile)
143
148
  updated = true
@@ -254,17 +259,45 @@ class Miam::Client
254
259
  log(:warn, "Role `#{role_name}`: 'path' cannot be updated", :color => :yellow)
255
260
  end
256
261
 
257
- updated = walk_assume_role_policy(role_name, expected_attrs[:assume_role_policy_document], actual_attrs[:assume_role_policy_document])
262
+ updated = walk_role_settings(role_name, {max_session_duration: expected_attrs[:max_session_duration]}, {max_session_duration: actual_attrs[:max_session_duration]})
263
+ updated = walk_assume_role_policy(role_name, expected_attrs[:assume_role_policy_document], actual_attrs[:assume_role_policy_document]) || updated
258
264
  updated = walk_role_instance_profiles(role_name, expected_attrs[:instance_profiles], actual_attrs[:instance_profiles]) || updated
259
265
  updated = walk_attached_managed_policies(:role, role_name, expected_attrs[:attached_managed_policies], actual_attrs[:attached_managed_policies]) || updated
260
266
  walk_policies(:role, role_name, expected_attrs[:policies], actual_attrs[:policies]) || updated
261
267
  end
262
268
 
269
+ def walk_role_settings(role_name, expected_settings, actual_settings)
270
+ updated = false
271
+
272
+ if expected_settings != actual_settings
273
+ @driver.update_role_settings(role_name, expected_settings, actual_settings)
274
+ updated = true
275
+ end
276
+
277
+ updated
278
+ end
279
+
263
280
  def walk_assume_role_policy(role_name, expected_assume_role_policy, actual_assume_role_policy)
264
281
  updated = false
265
282
  expected_assume_role_policy.sort_array!
266
283
  actual_assume_role_policy.sort_array!
267
284
 
285
+ # With only one entity granted
286
+ # On IAM
287
+ # (1) Statement => [ { Principal => AWS => arn } ]
288
+ # Should be able to specify like:
289
+ # (2) Statement => [ { Principal => AWS => [arn] } ]
290
+ # Actually (1) is reflected when config (2) is applied
291
+ expected_arp_stmt = expected_assume_role_policy.fetch('Statement', [])
292
+ expected_arp_stmt = expected_arp_stmt.select {|i| i.key?('Principal') }
293
+
294
+ expected_arp_stmt.each do |stmt|
295
+ stmt['Principal'].each do |k, v|
296
+ entities = Array(v)
297
+ stmt['Principal'][k] = entities.first if entities.length < 2
298
+ end
299
+ end
300
+
268
301
  if expected_assume_role_policy != actual_assume_role_policy
269
302
  @driver.update_assume_role_policy(role_name, expected_assume_role_policy, actual_assume_role_policy)
270
303
  updated = true
@@ -445,6 +478,7 @@ class Miam::Client
445
478
  updated = false
446
479
 
447
480
  expected.each do |policy_name, expected_attrs|
481
+ next unless target_matched?(policy_name)
448
482
  actual_attrs = actual.delete(policy_name)
449
483
 
450
484
  if actual_attrs
@@ -452,7 +486,7 @@ class Miam::Client
452
486
  log(:warn, "ManagedPolicy `#{policy_name}`: 'path' cannot be updated", :color => :yellow)
453
487
  end
454
488
 
455
- updated = walk_managed_policy(policy_name, expected_attrs[:document], actual_attrs[:document]) || updated
489
+ updated = walk_managed_policy(policy_name, actual_attrs[:path], expected_attrs[:document], actual_attrs[:document]) || updated
456
490
  else
457
491
  @driver.create_managed_policy(policy_name, expected_attrs)
458
492
  updated = true
@@ -462,13 +496,13 @@ class Miam::Client
462
496
  updated
463
497
  end
464
498
 
465
- def walk_managed_policy(policy_name, expected_document, actual_document)
499
+ def walk_managed_policy(policy_name, policy_path, expected_document, actual_document)
466
500
  updated = false
467
501
  expected_document.sort_array!
468
502
  actual_document.sort_array!
469
503
 
470
504
  if expected_document != actual_document
471
- @driver.update_managed_policy(policy_name, expected_document, actual_document)
505
+ @driver.update_managed_policy(policy_name, policy_path, expected_document, actual_document)
472
506
  updated = true
473
507
  end
474
508
 
@@ -479,7 +513,8 @@ class Miam::Client
479
513
  updated = false
480
514
 
481
515
  actual.each do |policy_name, actual_attrs|
482
- @driver.delete_managed_policy(policy_name)
516
+ next unless target_matched?(policy_name)
517
+ @driver.delete_managed_policy(policy_name, actual_attrs[:path])
483
518
  updated = true
484
519
  end
485
520
 
@@ -505,11 +540,17 @@ class Miam::Client
505
540
  end
506
541
 
507
542
  def target_matched?(name)
543
+ result = true
544
+
545
+ if @options[:exclude]
546
+ result &&= @options[:exclude].all? {|r| name !~ r }
547
+ end
548
+
508
549
  if @options[:target]
509
- name =~ @options[:target]
510
- else
511
- true
550
+ result &&= @options[:target].any? {|r| name =~ r}
512
551
  end
552
+
553
+ result
513
554
  end
514
555
 
515
556
  def exec_by_format(proc_by_format)
data/lib/miam/driver.rb CHANGED
@@ -4,9 +4,11 @@ class Miam::Driver
4
4
  MAX_POLICY_SIZE = 2048
5
5
  MAX_POLICY_VERSIONS = 5
6
6
 
7
- def initialize(iam, options = {})
7
+ def initialize(iam, sts, options = {})
8
8
  @iam = iam
9
+ @sts = sts
9
10
  @options = options
11
+ @account_id = nil
10
12
  end
11
13
 
12
14
  def create_user(user_name, attrs)
@@ -67,6 +69,14 @@ class Miam::Driver
67
69
  @iam.delete_signing_certificate(:user_name => user_name, :certificate_id => certificate_id)
68
70
  end
69
71
 
72
+ mfa_devices = @iam.list_mfa_devices(:user_name => user_name).map {|resp|
73
+ resp.mfa_devices
74
+ }.flatten
75
+
76
+ mfa_devices.each do |md|
77
+ @iam.deactivate_mfa_device(:user_name => user_name, :serial_number => md.serial_number)
78
+ end
79
+
70
80
  @iam.delete_user(:user_name => user_name)
71
81
  end
72
82
  end
@@ -168,6 +178,7 @@ class Miam::Driver
168
178
  params = {
169
179
  :role_name => role_name,
170
180
  :assume_role_policy_document => encode_document(assume_role_policy_document),
181
+ :max_session_duration => attrs.fetch(:max_session_duration)
171
182
  }
172
183
 
173
184
  params[:path] = attrs[:path] if attrs[:path]
@@ -179,6 +190,7 @@ class Miam::Driver
179
190
  :assume_role_policy_document => assume_role_policy_document,
180
191
  :policies => {},
181
192
  :attached_managed_policies => [],
193
+ :max_session_duration => attrs.fetch(:max_session_duration),
182
194
  }
183
195
 
184
196
  new_role_attrs[:path] = attrs[:path] if attrs[:path]
@@ -227,6 +239,14 @@ class Miam::Driver
227
239
  end
228
240
  end
229
241
 
242
+ def update_role_settings(role_name, new_settings, old_settings)
243
+ log(:info, "Update Role `#{role_name}` > Settings", :color => :green)
244
+ log(:info, Miam::Utils.diff(old_settings, new_settings, :color => @options[:color]), :color => false)
245
+ unless_dry_run do
246
+ @iam.update_role(new_settings.merge(role_name: role_name))
247
+ end
248
+ end
249
+
230
250
  def update_assume_role_policy(role_name, policy_document, old_policy_document)
231
251
  log(:info, "Update Role `#{role_name}` > AssumeRolePolicy", :color => :green)
232
252
  log(:info, Miam::Utils.diff(old_policy_document, policy_document, :color => @options[:color]), :color => false)
@@ -338,7 +358,7 @@ class Miam::Driver
338
358
  type_s = type.slice(0, 1).upcase + type.slice(1..-1)
339
359
 
340
360
  log(:info, "Update #{type_s} `#{name}`", :color => :green)
341
- log(:info, " detach policies=#{policies.join(',')}", :color => :green)
361
+ log(:info, " detach policies=#{policies.join(',')}", :color => :red)
342
362
 
343
363
  unless_dry_run do
344
364
  policies.each do |arn|
@@ -377,12 +397,12 @@ class Miam::Driver
377
397
  end
378
398
  end
379
399
 
380
- def delete_managed_policy(policy_name)
400
+ def delete_managed_policy(policy_name, policy_path)
381
401
  log(:info, "Delete ManagedPolicy `#{policy_name}`", :color => :red)
382
402
 
383
403
  unless_dry_run do
384
404
  policy_versions = @iam.list_policy_versions(
385
- :policy_arn => policy_arn(policy_name),
405
+ :policy_arn => policy_arn(policy_name, policy_path),
386
406
  :max_items => MAX_POLICY_VERSIONS
387
407
  )
388
408
 
@@ -390,24 +410,24 @@ class Miam::Driver
390
410
  pv.is_default_version
391
411
  }.each {|pv|
392
412
  @iam.delete_policy_version(
393
- :policy_arn => policy_arn(policy_name),
413
+ :policy_arn => policy_arn(policy_name, policy_path),
394
414
  :version_id => pv.version_id
395
415
  )
396
416
  }
397
417
 
398
418
  @iam.delete_policy(
399
- :policy_arn => policy_arn(policy_name)
419
+ :policy_arn => policy_arn(policy_name, policy_path)
400
420
  )
401
421
  end
402
422
  end
403
423
 
404
- def update_managed_policy(policy_name, policy_document, old_policy_document)
424
+ def update_managed_policy(policy_name, policy_path, policy_document, old_policy_document)
405
425
  log(:info, "Update ManagedPolicy `#{policy_name}`", :color => :green)
406
426
  log(:info, Miam::Utils.diff(old_policy_document, policy_document, :color => @options[:color]), :color => false)
407
427
 
408
428
  unless_dry_run do
409
429
  policy_versions = @iam.list_policy_versions(
410
- :policy_arn => policy_arn(policy_name),
430
+ :policy_arn => policy_arn(policy_name, policy_path),
411
431
  :max_items => MAX_POLICY_VERSIONS
412
432
  )
413
433
 
@@ -417,19 +437,27 @@ class Miam::Driver
417
437
  }.sort_by {|pv| pv.version_id[1..-1].to_i }.first
418
438
 
419
439
  @iam.delete_policy_version(
420
- :policy_arn => policy_arn(policy_name),
440
+ :policy_arn => policy_arn(policy_name, policy_path),
421
441
  :version_id => delete_policy_version.version_id
422
442
  )
423
443
  end
424
444
 
425
445
  @iam.create_policy_version(
426
- :policy_arn => policy_arn(policy_name),
446
+ :policy_arn => policy_arn(policy_name, policy_path),
427
447
  :policy_document => encode_document(policy_document),
428
448
  set_as_default: true
429
449
  )
430
450
  end
431
451
  end
432
452
 
453
+ def password_policy
454
+ return @password_policy if instance_variable_defined?(:@password_policy)
455
+
456
+ @password_policy = @iam.get_account_password_policy.password_policy
457
+ rescue Aws::IAM::Errors::NoSuchEntity
458
+ @password_policy = nil
459
+ end
460
+
433
461
  private
434
462
 
435
463
  def encode_document(policy_document)
@@ -455,11 +483,13 @@ class Miam::Driver
455
483
  yield unless @options[:dry_run]
456
484
  end
457
485
 
458
- def user_id
459
- @user_id ||= @iam.get_user.user.user_id
486
+ def account_id
487
+ # https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
488
+ # http://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html
489
+ @account_id ||= @sts.get_caller_identity.account
460
490
  end
461
491
 
462
- def policy_arn(policy_name)
463
- "arn:aws:iam::#{user_id}:policy/#{policy_name}"
492
+ def policy_arn(policy_name, policy_path)
493
+ File.join("arn:aws:iam::#{account_id}:policy", policy_path, policy_name)
464
494
  end
465
495
  end
@@ -4,7 +4,7 @@ class Miam::DSL::Context::Role
4
4
  def initialize(context, name, &block)
5
5
  @role_name = name
6
6
  @context = context.merge(:role_name => name)
7
- @result = {:instance_profiles => [], :policies => {}, :attached_managed_policies => []}
7
+ @result = {:instance_profiles => [], :max_session_duration => 3600, :policies => {}, :attached_managed_policies => []}
8
8
  instance_eval(&block)
9
9
  end
10
10
 
@@ -22,6 +22,10 @@ class Miam::DSL::Context::Role
22
22
  @result[:instance_profiles].concat(profiles.map(&:to_s))
23
23
  end
24
24
 
25
+ def max_session_duration(duration)
26
+ @result[:max_session_duration] = duration
27
+ end
28
+
25
29
  def assume_role_policy_document
26
30
  if @result[:assume_role_policy_document]
27
31
  raise "Role `#{@role_name}` > AssumeRolePolicyDocument: already defined"
@@ -12,7 +12,7 @@ class Miam::DSL::Context
12
12
  def initialize(path, options = {}, &block)
13
13
  @path = path
14
14
  @options = options
15
- @result = {:users => {}, :groups => {}, :roles => {}, :instance_profiles => {}, :policies => {}}
15
+ @result = {:users => {}, :groups => {}, :roles => {}, :instance_profiles => {}, :policies => {}, :exclude => []}
16
16
 
17
17
  @context = Hashie::Mash.new(
18
18
  :path => path,
@@ -41,6 +41,10 @@ class Miam::DSL::Context
41
41
  end
42
42
  end
43
43
 
44
+ def exclude(pattern)
45
+ @result[:exclude] << pattern
46
+ end
47
+
44
48
  def user(name, user_options = {}, &block)
45
49
  name = name.to_s
46
50
 
@@ -95,6 +95,8 @@ end
95
95
  role #{role_name.inspect}, #{Miam::Utils.unbrace(role_options.inspect)} do
96
96
  #{output_role_instance_profiles(attrs[:instance_profiles])}
97
97
 
98
+ #{output_role_max_session_duration(attrs[:max_session_duration])}
99
+
98
100
  #{output_assume_role_policy_document(attrs[:assume_role_policy_document])}
99
101
 
100
102
  #{output_policies(attrs[:policies])}
@@ -122,6 +124,12 @@ end
122
124
  }.select {|i| i }.join("\n")
123
125
  end
124
126
 
127
+ def output_role_max_session_duration(max_session_duration)
128
+ <<-EOS.strip
129
+ max_session_duration #{max_session_duration}
130
+ EOS
131
+ end
132
+
125
133
  def output_assume_role_policy_document(assume_role_policy_document)
126
134
  assume_role_policy_document = assume_role_policy_document.pretty_inspect
127
135
  assume_role_policy_document.gsub!("\n", "\n ").strip!
@@ -193,10 +201,16 @@ end
193
201
  end
194
202
 
195
203
  def target_matched?(name)
204
+ result = true
205
+
206
+ if @options[:exclude]
207
+ result &&= @options[:exclude].all? {|r| name !~ r}
208
+ end
209
+
196
210
  if @options[:target]
197
- name =~ @options[:target]
198
- else
199
- true
211
+ result &&= @options[:target].any? {|r| name =~ r}
200
212
  end
213
+
214
+ result
201
215
  end
202
216
  end
data/lib/miam/exporter.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  class Miam::Exporter
3
3
  AWS_MANAGED_POLICY_PREFIX = 'arn:aws:iam::aws:'
4
+ AWS_CN_MANAGED_POLICY_PREFIX = 'arn:aws-cn:iam::aws:'
4
5
 
5
6
  def self.export(iam, options = {})
6
7
  self.new(iam, options).export
@@ -143,6 +144,8 @@ class Miam::Exporter
143
144
  instance_profiles = role.instance_profile_list.map {|i| i.instance_profile_name }
144
145
  policies = export_role_policies(role)
145
146
  attached_managed_policies = role.attached_managed_policies.map(&:policy_arn)
147
+ role_data = @iam.get_role(role_name: role_name).role
148
+ max_session_duration = role_data.max_session_duration
146
149
 
147
150
  @mutex.synchronize do
148
151
  instance_profiles.each do |instance_profile_name|
@@ -158,6 +161,7 @@ class Miam::Exporter
158
161
  :instance_profiles => instance_profiles,
159
162
  :policies => policies,
160
163
  :attached_managed_policies => attached_managed_policies,
164
+ :max_session_duration => max_session_duration,
161
165
  }
162
166
 
163
167
  progress
@@ -200,7 +204,7 @@ class Miam::Exporter
200
204
  result = {}
201
205
 
202
206
  Parallel.each(policies, :in_threads => @concurrency) do |policy|
203
- if policy.arn.start_with?(AWS_MANAGED_POLICY_PREFIX)
207
+ if policy.arn.start_with?(AWS_MANAGED_POLICY_PREFIX) or policy.arn.start_with?(AWS_CN_MANAGED_POLICY_PREFIX)
204
208
  next
205
209
  end
206
210
 
@@ -262,10 +266,10 @@ class Miam::Exporter
262
266
  @iam.get_account_authorization_details.each do |resp|
263
267
  keys.each do |key|
264
268
  account_authorization_details[key].concat(resp[key])
269
+ end
265
270
 
266
- unless @options[:no_progress]
267
- progressbar.increment
268
- end
271
+ unless @options[:no_progress]
272
+ progressbar.increment
269
273
  end
270
274
  end
271
275
 
@@ -1,13 +1,19 @@
1
1
  class Miam::PasswordManager
2
2
  include Miam::Logger::Helper
3
3
 
4
+ LOWERCASES = ('a'..'z').to_a
5
+ UPPERCASES = ('A'..'Z').to_a
6
+ NUMBERS = ('0'..'9').to_a
7
+ SYMBOLS = "!@\#$%^&*()_+-=[]{}|'".split(//)
8
+
4
9
  def initialize(output, options = {})
5
10
  @output = output
6
11
  @options = options
7
12
  end
8
13
 
9
- def identify(user, type)
10
- password = mkpasswd
14
+ def identify(user, type, policy)
15
+ password = mkpasswd(policy)
16
+ log(:debug, "mkpasswd: #{password}")
11
17
  puts_password(user, type, password)
12
18
  password
13
19
  end
@@ -22,8 +28,21 @@ class Miam::PasswordManager
22
28
 
23
29
  private
24
30
 
25
- def mkpasswd(len = 8)
26
- [*1..9, *'A'..'Z', *'a'..'z'].shuffle.slice(0, len).join
31
+ def mkpasswd(policy)
32
+ chars = []
33
+ len = 8
34
+
35
+ if policy
36
+ len = policy.minimum_password_length if policy.minimum_password_length > len
37
+ chars << LOWERCASES.shuffle.first if policy.require_lowercase_characters
38
+ chars << UPPERCASES.shuffle.first if policy.require_uppercase_characters
39
+ chars << NUMBERS.shuffle.first if policy.require_numbers
40
+ chars << SYMBOLS.shuffle.first if policy.require_symbols
41
+
42
+ len -= chars.length
43
+ end
44
+
45
+ (chars + [*1..9, *'A'..'Z', *'a'..'z'].shuffle.slice(0, len)).shuffle.join
27
46
  end
28
47
 
29
48
  def open_output
@@ -1,6 +1,8 @@
1
1
  module Miam
2
2
  module TemplateHelper
3
3
  def include_template(template_name, context = {})
4
+ @template_name = template_name
5
+ @caller = caller[0]
4
6
  tmplt = @context.templates[template_name.to_s]
5
7
 
6
8
  unless tmplt
@@ -16,5 +18,14 @@ module Miam
16
18
  def context
17
19
  @context
18
20
  end
21
+
22
+ def required(*args)
23
+ missing_args = args.map(&:to_s) - @context.keys.map(&:to_s)
24
+ unless missing_args.empty?
25
+ ex = ArgumentError.new("Missing arguments: [#{missing_args.join(", ")}] in template: '#{@template_name}'")
26
+ ex.set_backtrace(@caller)
27
+ raise ex
28
+ end
29
+ end
19
30
  end
20
31
  end
data/lib/miam/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Miam
2
- VERSION = '0.2.3'
2
+ VERSION = '0.2.5.beta1'
3
3
  end
data/lib/miam.rb CHANGED
@@ -5,7 +5,7 @@ require 'pp'
5
5
  require 'singleton'
6
6
  require 'thread'
7
7
 
8
- require 'aws-sdk-core'
8
+ require 'aws-sdk-iam'
9
9
  Aws.use_bundled_cert!
10
10
 
11
11
  require 'ruby-progressbar'
data/miam.gemspec CHANGED
@@ -8,11 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Miam::VERSION
9
9
  spec.authors = ['Genki Sugawara']
10
10
  spec.email = ['sgwr_dts@yahoo.co.jp']
11
- spec.summary = %q{TODO: Write a short summary. Required.}
12
- spec.description = %q{TODO: Write a longer description. Optional.}
13
11
  spec.summary = %q{Miam is a tool to manage IAM.}
14
12
  spec.description = %q{Miam is a tool to manage IAM. It defines the state of IAM using DSL, and updates IAM according to DSL.}
15
- spec.homepage = 'http://miam.codenize.tools/'
13
+ spec.homepage = 'https://github.com/codenize-tools/miam'
16
14
  spec.license = 'MIT'
17
15
 
18
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -20,7 +18,7 @@ Gem::Specification.new do |spec|
20
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
19
  spec.require_paths = ['lib']
22
20
 
23
- spec.add_dependency 'aws-sdk-core', '>= 2.0.42'
21
+ spec.add_dependency 'aws-sdk-iam', '~> 1'
24
22
  spec.add_dependency 'ruby-progressbar'
25
23
  spec.add_dependency 'parallel'
26
24
  spec.add_dependency 'term-ansicolor'
@@ -31,4 +29,5 @@ Gem::Specification.new do |spec|
31
29
  spec.add_development_dependency 'rspec', '>= 3.0.0'
32
30
  spec.add_development_dependency 'rspec-instafail'
33
31
  spec.add_development_dependency 'coveralls'
32
+ spec.add_development_dependency 'nokogiri'
34
33
  end