Terraformにおけるクロスアカウント構成なモジュール

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


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

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

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

#ログ収集基盤の構成紹介
![Untitled Diagram (3).png](https://qiita-image-store.s3.amazonaws.com/0/59905/f5bb5003-c3cd-2ab5-cd25-7a04ec086472.png)

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

図の上部にあるAWSアカウントでは、ログの一元管理を行っていて、同じAWSアカウント内のログや複数の別AWSアカウントのログを１つのアカウントに集約しています。
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:

- [Upgrading to Terraform v0.11][3]
- [terraform/CHANGELOG.md][4]
- [module provider inheritance #16379][5]

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

対象version 0.11.1

##v0.11で追加されたmoduleの新オプションprovidersとは？？
###v0.10まで
v0.10までmoduleを利用する際にはプロバイダの継承が暗黙的に行われていました

```hcl
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`を使って明示的に渡す事ができるようになりました。

```hcl
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内で１つのプロバイダのみに対して操作する場合は、従来の暗黙的なプロバイダ継承を推奨すると[公式][6]に記載されていましたのでうまく使い分けましょう。

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

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

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


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

```hcl
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"
  }
}
```

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

```hcl: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:

[1]: https://qiita.com/advent-calendar/2017/dmm
[2]: https://qiita.com/advent-calendar/2017/dmm2
[3]: https://www.terraform.io/upgrade-guides/0-11.html
[4]: https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md
[5]: https://github.com/hashicorp/terraform/pull/16379
[6]: https://www.terraform.io/docs/modules/usage.html#implicit-provider-inheritance