新規アカウントでもこれ一発!CloudTrailを全リージョンで有効化するスクリプトを書いた

2014.08.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。望月です。 先日(といっても結構前ですが)、ついに待望のCloudTrailの全リージョン対応が発表されました。 その前からそうだったかは未調査ですが、CloudTrailを有効にしていない場合に、AWSのTrusted Advisorで警告が出るようになりました。


ct_2

この警告を消すためにも、全リージョンでCloudTrailを有効にしましょう!

スクリプト化してみた

ですが、手作業でぽちぽちやるのはアレなので、自動化しました。既にj3tm0t0さんがAWS - 全リージョンのCloudTrailロギングを有効にするワンライナー - Qiitaにてワンライナーを公開されていますので、そちらを使うのもいいと思います。このワンライナーではS3バケットと適切なBucket Policyが設定されていることが大前提なので、まだBucketを作成していないアカウントでは、先にS3バケットを公式ドキュメントに従って作成しておく必要があります。

そんなに大変な作業ではないのですが、それすら面倒臭がる私のような人向けにS3バケットの作成とBucket Policyの設定まで一発でやってくれるスクリプトを書いてみました。
ソースはGistにもあげてあります。

#!/usr/bin/env ruby
# Activate Cloudtrail and create S3 Bucket in each region.
# Usage: ./activate.rb --profile <profile_name>
# or
# Usage: ./activate.rb -k <access_key> -s <secret_key>

require 'aws-sdk-v1'
require 'optparse'

# get account ID from yourself ARN.
# even if you have no permission to IAM, you can get your account ID from error message 
# because it contains IAM User ARN.
def get_account_id
  iam = AWS::IAM::Client.new
  begin
    iam.get_user.user.arn =~ /arn:aws:iam::(\d{12}):user\/.*/
  rescue => e
    e.message =~ /arn:aws:iam::(\d{12}):user\/.*/
  end
  # return 12 digits account number
  # if failed, raise RuntimeError
  if $1.nil?
    raise RuntimeError
  end
  $1
end

# Set Bucket Policy for CloudTrail
# Ref : https://docs.aws.amazon.com/awscloudtrail/latest/userguide/create_trail_bucket_policy.html
def cloudtrail_bucket_policy bucket_name, account_id

  policy_hash = {
    "Version" => "2012-10-17",
    "Statement" => [
      {
        "Sid" => "AWSCloudTrailAclCheck20131101",
        "Effect" => "Allow",
        "Principal" => {
          "AWS" => [
            "arn:aws:iam::903692715234:root",
            "arn:aws:iam::859597730677:root",
            "arn:aws:iam::814480443879:root",
            "arn:aws:iam::216624486486:root",
            "arn:aws:iam::086441151436:root",
            "arn:aws:iam::388731089494:root",
            "arn:aws:iam::284668455005:root",
            "arn:aws:iam::113285607260:root",
            "arn:aws:iam::035351147821:root"
          ]
        },
        "Action" => "s3:GetBucketAcl",
        "Resource" => "arn:aws:s3:::#{bucket_name}"
      },
      {
        "Sid" => "AWSCloudTrailWrite20131101",
        "Effect" => "Allow",
        "Principal" => {
          "AWS" => [
            "arn:aws:iam::903692715234:root",
            "arn:aws:iam::859597730677:root",
            "arn:aws:iam::814480443879:root",
            "arn:aws:iam::216624486486:root",
            "arn:aws:iam::086441151436:root",
            "arn:aws:iam::388731089494:root",
            "arn:aws:iam::284668455005:root",
            "arn:aws:iam::113285607260:root",
            "arn:aws:iam::035351147821:root"
          ]
        },
        "Action" => "s3:PutObject",
        "Resource" => "arn:aws:s3:::#{bucket_name}/AWSLogs/#{account_id}/*",
        "Condition" => {
          "StringEquals" => {
            "s3:x-amz-acl" => "bucket-owner-full-control"
          }
        }
      }
    ]
  }
  AWS::S3::Policy.from_json(policy_hash.to_json)
end

ARGV.options do |opt|
  begin
    aws_opts = {}
    is_debug = false

    opt.on('-h', '--help')                  { puts opt.help; exit 0 }
    opt.on('-k', '--access-key ACCESS_KEY') { |v| aws_opts[:access_key_id]      = v }
    opt.on('-s', '--secret-key SECRET_KEY') { |v| aws_opts[:secret_access_key]  = v }
    opt.on('--debug')                       { is_debug = true }
    opt.on('--profile PROFILE')             { |v| aws_opts[:credential_provider] = AWS::Core::CredentialProviders::SharedCredentialFileProvider.new(profile_name: v) }
    opt.parse!

    if aws_opts.empty?
      puts opt.help
      exit 1
    end

    if is_debug
      aws_opts[:logger]    = Logger.new($stdout)
      aws_opts[:log_level] = :debug
    end

    AWS.config(aws_opts)

  rescue => e
    $stderr.puts e
    exit 1
  end
end

account_id = get_account_id

AWS.regions.each do |region|

  AWS.config(region: region.name)

  ct = AWS::CloudTrail::Client.new

  # Skip if CloudTrail is already enabled in current region
  unless ct.describe_trails.data[:trail_list].count == 0
    puts "CloudTrail is already activated in #{region.name}. Skipping this region."
    next
  end

  s3 = AWS::S3.new
  bucket_name = "cloudtrail-#{region.name}-#{account_id}"

  begin
    unless s3.buckets[bucket_name].exists?
      s3.buckets.create(bucket_name)
      puts "Created S3 Bucket #{bucket_name}"

      s3.buckets[bucket_name].policy = cloudtrail_bucket_policy(bucket_name, account_id)
      puts "Successfully Attached S3 Bucket Policy"
    else
      puts "S3 bucket #{bucket_name} already exists."
    end

    option = {
      :name => 'Default',
      :s3_bucket_name => bucket_name,
      :s3_key_prefix => '',
      :include_global_service_events => false,
    }
    option[:include_global_service_events] = true if region.name == "ap-northeast-1"
    ct.create_trail(option)
    ct.start_logging({:name => "Default"})

    puts "Successfully enabled trailing in #{region.name}"
  rescue => e
    $stderr.puts e.message
    next
  end
end

スクリプトを実行した後、もう一度Trusted Advisorを確認してみましょう。


ct_1

緑色になりました!これで一安心ですね。