IAMのユーザ一覧を出力する(グループ・アクセスキー付き)

#!/usr/bin/env ruby
require 'cgi'
require 'base64'
require 'net/https'
require 'openssl'
require 'time'
require 'rexml/document'

class IAMClient
  API_VERSION = '2010-05-08'
  HOST = 'iam.amazonaws.com'
  ALGORITHM = :SHA1
  #ALGORITHM = :SHA256

  def initialize(accessKeyId, secretAccessKey)
    @accessKeyId = accessKeyId
    @secretAccessKey = secretAccessKey
  end

  def query(action, params = {})
    params = {
      :Action           => action,
      :Version          => API_VERSION,
      :Timestamp        => Time.now.getutc.strftime('%Y-%m-%dT%H:%M:%SZ'),
      :SignatureVersion => 2,
      :SignatureMethod  => "Hmac#{ALGORITHM}",
      :AWSAccessKeyId   => @accessKeyId,
    }.merge(params)

    signature = aws_sign(params)
    params[:Signature] = signature

    Net::HTTP.version_1_2
    https = Net::HTTP.new(HOST, 443)
    https.use_ssl = true
    https.verify_mode = OpenSSL::SSL::VERIFY_NONE

    https.start do |w|
      req = Net::HTTP::Post.new('/',
        'Host' => HOST,
        'Content-Type' => 'application/x-www-form-urlencoded'
      )

      req.set_form_data(params)
      res = w.request(req)

      res.body
    end
  end

  private
  def aws_sign(params)
    params = params.sort_by {|a, b| a.to_s }.map {|k, v| "#{k}=#{CGI.escape(v.to_s)}" }.join('&')
    string_to_sign = "POST\n#{HOST}\n/\n#{params}"
    digest = OpenSSL::HMAC.digest(OpenSSL::Digest.const_get(ALGORITHM).new, @secretAccessKey, string_to_sign)
    Base64.encode64(digest).gsub("\n", '')
  end
end

AWSAccessKeyId = '...'
AWSSecretAccessKey = '...' 

iamcli = IAMClient.new(AWSAccessKeyId, AWSSecretAccessKey)
source = iamcli.query('ListUsers', 'MaxItems' => 512)
doc = REXML::Document.new(source)

doc.each_element('/ListUsersResponse/ListUsersResult/Users/member') do |element|
  user_id, path, user_name, arn, create_date = %w(
    UserId
    Path
    UserName
    Arn
    CreateDate
  ).map {|i| element.get_text(i).to_s }

  groups = []

  REXML::Document.new(iamcli.query('ListGroupsForUser', 'UserName' => user_name, 'MaxItems' => 512)).each_element('//GroupName') do |element|
    groups << element.text.to_s
  end

  access_keys = []

  REXML::Document.new(iamcli.query('ListAccessKeys', 'UserName' => user_name, 'MaxItems' => 512)).each_element('/ListAccessKeysResponse/ListAccessKeysResult/AccessKeyMetadata/member') do |element|
    access_key_id = element.get_text('AccessKeyId').to_s
    status = element.get_text('Status').to_s
    access_keys << "#{access_key_id} (#{status})"
  end

  puts [user_name, path, create_date, groups.join(','), access_keys.join(',')].join("\t")
end