これはDMM.com #2 Advent Calendar 2017 8日目の記事です。

カレンダーのURLはこちら
DMM.com #1 Advent Calendar 2017
DMM.com #2 Advent Calendar 2017

こんにちは@mafuyukです。
最近はAWSでクロスアカウントなログ収集基盤の設計、実装、構成の自動化をしていました。

本記事では実際に作成した、ログ収集基盤の構成を参考にTerraformにおけるクロスアカウント構成なモジュールについて紹介したいと思います。

ログ収集基盤の構成紹介

Untitled Diagram (3).png

構成図の説明

別々のAWSアカウントのログを一元管理したいという要件を満たすために用意したログ収集基盤の構成図です。

図の上部にあるAWSアカウントでは、ログの一元管理を行っていて、同じAWSアカウント内のログや複数の別AWSアカウントのログを1つのアカウントに集約しています。
AWSアカウント同士の連携はAssumeRoleで実現しています。

ログ収集の流れ

  1. CWLのログをサブスクリプションフィルターをトリガーとしたLambdaで取得
  2. LambdaはAssume Roleでアクセス権限を取得後、Kinesis FirehoseのAPIをcallしログをストリームに流し入れる
  3. ストリームはログ保管用S3とログ可視化用Elasticsearch Serviceに出力する

構成図の赤丸で囲った部分(上記の1と2の部分)をモジュール化します。

Terraform新機能

実践に入る前におめでたい話

祝Terraform v0.11 リリース

2017年11月16日にTerraform v0.11がリリースされましたー:tada::tada::tada::tada:

今回作成するモジュールのテンプレートではv0.11で新たに追加されたprovidersというオプションを利用するのでTerraformのversionを確認してください:bow:

対象version 0.11.1

v0.11で追加されたmoduleの新オプションprovidersとは??

v0.10まで

v0.10までmoduleを利用する際にはプロバイダの継承が暗黙的に行われていました

provider "aws" {
  version                 = "~> 1.0"
  region                  = "us-west-2"
  shared_credentials_file = "${var.shared_credentials_file}"
}

module "create_module" {
  source = "git::https://github.com/mafuyuk/tf-aws-template?ref=master"
  // moduleを利用しているTerraform環境のデフォルトのプロパイダに対しての操作になる 
}

v0.11から

v0.11からはmoduleに対してプロバイダ情報をprovidersを使って明示的に渡す事ができるようになりました。

provider "aws" {
  version                 = "~> 1.0"
  region                  = "us-west-2"
  shared_credentials_file = "${var.shared_credentials_file}"
}

provider "aws" {
  version                 = "~> 1.0"
  alias                   = "foo"
  region                  = "us-west-2"
  shared_credentials_file = "${var.shared_credentials_file}"
}

module "create_module" {
  source = "git::https://github.com/mafuyuk/tf-aws-template?ref=master"
  providers = {
    "aws" = "aws.foo" // module内のaws(デフォルト)の値がaws.fooプロパイダになる
    "aws.bar" = "aws" // module内のaw.barの値がデフォルトプロパイダになる
  }
}

:warning: module内で1つのプロバイダのみに対して操作する場合は、従来の暗黙的なプロバイダ継承を推奨すると公式に記載されていましたのでうまく使い分けましょう。

実践

module側プロパイダ設定

クロスアカウント構成を実現するためにmodule側でプロパイダ設定を2つ受け取れるようにします。

aws.tf
// ログがあるAWSアカウントのプロバイダ
provider "aws" {
  alias  = "src"
}

// ログを集約するAWSアカウントのプロバイダ
provider "aws" {
  alias  = "dst"
}

moduleを利用する

このモジュールを利用する場合は以下のようにprovidersを渡します。

provider "aws" {
  version                 = "~> 1.0"
  alias                   = "myprov"
  profile                 = "myprov"
  region                  = "${var.myprov_region}"
  shared_credentials_file = "${var.shared_credentials_file}"
}

provider "aws" {
  version                 = "~> 1.0"
  alias                   = "logprov"
  profile                 = "logprov"
  region                  = "${var.logprov_region}"
  shared_credentials_file = "${var.shared_credentials_file}"
}

module "create_resource" {
  source = "git::https://github.com/mafuyuk/tf-aws-template?ref=master"
  providers = {
    "aws.src" = "aws.myprov"
    "aws.dst" = "aws.logprov"
  }
}

受け取った2つのプロバイダ情報を利用する

参考にしたログの収集基盤では、AssumeRole周りで2つのプロパイダ構成を利用する必要がありました。
実際に今回の構成のどの部分に使ったのかみてみましょう。

iam.tf
resource "aws_iam_role" "lambda" {
  provider = "aws.src"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "writes_to_cwl_policy" {
  // ただのCWLへの書き込み権限なので省略
}

// src側のRoleにdst側のRoleに対してAssumeRoleを行えるポリシー付与
resource "aws_iam_role_policy" "fh_sts_policy" {
  provider = "aws.src"
  role     = "${aws_iam_role.lambda.id}"

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "${aws_iam_role.lambda_assume.arn}"
    }
}
EOF
}

// dst側ではsrc側のロールがAssumeRoleを行った際に
// KinesisFirehoseの実行権限を持った一時クレデンシャルを発行することが可能なRoleを作成
resource "aws_iam_role" "lambda_assume" {
  provider = "aws.dst"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "${aws_iam_role.lambda.arn}"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "writes_to_fh_policy" {
  provider = "aws.dst"
  role     = "${aws_iam_role.lambda_assume.id}"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "firehose:PutRecordBatch",
      "Resource": [
        "${var.fh_stream_arn}" // 出力先のFirehoseのARN
      ]
    }
  ]
}
EOF
}

Terraformにおけるクロスアカウント構成なモジュールに必要な実装の紹介は以上です。
これで複数のAWSアカウントを股にかけたモジュールの作成ができるようになったと思います:100: