TerraformでAWSのIAMユーザのグループポリシーを管理する


# はじめに

皆さんAWSのIAMの権限管理ってどうしてますか？IAMはAWSを使う以上は避けては通れない機能ですが、すこぶる使い勝手が悪く、悩ましい問題です。人数が少なければ全員Administratorsでもいいんですけど、人数増えてくると全員Administratorsなのもちょっと微妙。かと言って、開発チーム内でIAM権限絞ると、既存の業務から外れた、何か新しいことをしようとすると権限足りないエラーになって、権限ある人にお願いしないと権限変更できないとかなると、開発のスピードが落ちます。あと、権限ある人もIAMポリシーを人のために調べて書くのは苦痛です。できればポリシーは権限が欲しい人に調べて書いてもらいたいところです。

そこで、Terraformを使ってIAMのポリシーを管理すれば、IAMのコンソールでJSONを編集するという苦行から開放され、gitで管理できるようになり、かつ権限を欲しい人がプルリクエストベースで依頼することができるのでは？と考えて、設定方法を検証してみました。権限変更の差分がレビューできるようになるし、記録にも残るし、いいコトづくしですしおすし。

# TerraformでIAMユーザのグループポリシーを管理する
## 方針
IAMユーザのポリシーの管理を行う上で、Terrafromの特性を考慮すべきことがあります。それはTerraformは、自分で認識しているリソースを `terraform.tfstate` ファイルという中間ファイルで保持しているため、既に存在しているAWSリソースをTerraformでは操作できません。

特にIAMユーザは再作成するとアクセスキーなどが変わってしまったり、MFAデバイスの再設定をしないといけなかったり、なかなか気軽に再作成できないです。

そこで今回の戦略は、新規にグループを作成し、そのグループに対してポリシーを割当、そのグループにユーザを追加することで、ユーザにポリシーを適用するという戦略を取ります。ポリシーをグループ単位で管理すること自体はベストプラクティスとして異論はないと思いますが、最終的にはユーザもTerraformの管理下に置きたいけど、現行の運用に影響を与えずに移行するために一旦ユーザはTerraformの管理外として扱います。

(2016/5/26追記）Terraformingを使って既存のIAMユーザをTerraform管理下に入れる方法については以下に書きました。
[Terraformingで既存のIAMユーザをTerraform管理下に入れる](http://engineer.crowdworks.jp/entry/2016/05/26/163012)

## Terraformの導入

TerraformはHashiCorp製のクラウドリソースの構成管理ツールです。誤解を恐れず大雑把に説明すると、AWSのCloudFormationみたいなののAWSに限定されないベンダフリーなツールです。
CloudFormationと違って、terraform planというdry-runなことができたり、いいこともあります。

インストールはバイナリのzipを公式サイトからダウンロードしてきて、展開してパスを通すだけなので割愛します。

https://terraform.io/downloads.html

Terraformそのもの説明は他にもいろいろ解説記事があるので適宜ググってください。

## Terraformの設定ファイルを作成する
でわ、具体的なTerraformの設定ファイルを作っていきましょう。

完成品はGitHubの以下のリポジトリに置いておきました。
https://github.com/minamijoyo/iam-terraform-example


まずはグループを定義します。

```aws_iam_groups.tf
resource "aws_iam_group" "group1" {
    name = "group1"
}

resource "aws_iam_group" "group2" {
    name = "group2"
}
```

次に、グループに対してポリシーを割り当てます。
ポリシーのJSON自体はインラインのヒアドキュメントでも書けるのですが、組み込みのfile関数を使って、外部のJSONファイルを読み込むようにしておけば、ポリシーのJSON自体は好みのエディタでシンタックスハイライトが効くようになったりして、たぶん幸せになれます。

```aws_iam_group_policies.tf
resource "aws_iam_group_policy" "group1_policy" {
    name = "group1_policy"
    group = "${aws_iam_group.group1.id}"
    policy = "${file("aws_iam_group_policies/group1_policy.json")}"
}

resource "aws_iam_group_policy" "group2_policy" {
    name = "group2_policy"
    group = "${aws_iam_group.group2.id}"
    policy = "${file("aws_iam_group_policies/group2_policy.json")}"
}
```

ポリシーのJSONは目的に合わせて適宜カスタマイズします。

```aws_iam_group_policies/group1_policy.json
{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "iam:ChangePassword",
            "iam:GetAccountPasswordPolicy"
        ],
        "Resource": "*"
    }
}
```

```aws_iam_group_policies/group2_policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "aws-portal:View*",
            "Resource": "*"
        }
    ]
}
```

最後に、グループにメンバーを追加します。ユーザはTerraformの管理外とするので、ここでは既存のIAMユーザ名を指定しています。

```aws_iam_group_memberships.tf
resource "aws_iam_group_membership" "group1" {
    name = "group1-membership"
    users = [
        "user1",
        "user2",
    ]
    group = "group1"
}

resource "aws_iam_group_membership" "group2" {
    name = "group2-membership"
    users = [
        "user2",
    ]
    group = "group2"
}
```

1人のユーザが複数のグループに参加することが可能なので、ロール毎にグループを用意して、ユーザは必要なグループに参加することで権限を得ることができます。グループポリシーはただのJSONファイルなので、gitで管理して、必要な権限の追加があった場合は、プルリクエストベースの運用ができます。

# TerraformでAWSに反映する
ファイルの準備ができたら実際にTerrafromでAWSのリソースを作ります。
AWSのアクセスキーなどの設定はterraformの設定側でもできるのですが、簡単に環境変数から読み込みます。

```bash
$ export AWS_ACCESS_KEY_ID=XXXX
$ export AWS_SECRET_ACCESS_KEY=XXXX
$ export AWS_DEFAULT_REGION=ap-northeast-1
```

`terraform plan` で変更内容を確認し、

```bash
$ terraform plan
```

`terraform apply` で反映します。

```bash
$ terraform apply
```

AWSコンソールから確認すると、たしかにIAMのグループができて、ポリシーとユーザが割り当てられていることが確認できます。

## terraform.tfstateをAtlasで管理する
terraformの状態は `terraform.tfstate` ファイルで管理されているので、terraformを複数人で使う場合にはこのファイルを共有しておく必要があります。

このファイルをgitで管理するのはオススメしません。複数ブランチで作業するとコンフリクトしてしまうからです。`terraform.tfstate` ファイルはAtlasで管理します。

AtlasはHashiCorp製のインフラを統合管理するプラットフォームです。
とりあえず今のところは(?)無料で使えるので、アカウントがなければ取得してください。

https://atlas.hashicorp.com/

（2016/5/26追記）Atlasは有料化されるようです。tfstateの共有だけであればS3に置くという選択肢もあります。[Amazon S3 で Terraform の状態管理ファイル terraform.tfstate を管理 / 共有する](http://qiita.com/dtan4/items/04632f1c2f35388a3283)

Atlasにログインしたら設定のトークンのところからAPIトークンを取得します。
https://atlas.hashicorp.com/settings/tokens

トークンを取得したら環境変数 `ATLAS_TOKEN` としてエクスポートします。

```bash
$ export ATLAS_TOKEN=XXXXXXXXXX
```

terraformコマンドのバックエンドにAtlasを指定します。
nameの箇所は(ユーザ名)/(Environments)を適宜読み替えてください。EnvironmentsはAtlas上のプロジェクト名みたいなもので、該当するものがなければ新規に作成されます。

```bash
$ terraform remote config -backend-config "name=minamijoyo/iam-terraform-example"
```

これで `terraform.tfstate` が自動的にAtlasと同期するようになります。
一度設定すると、基本的に `terraform apply` するだけで自動で反映されてますが、明示的にpull/pushするには `terraform remote pull` / `terraform remote push` でできます。

AtlasはGithubとの連携もできるようなので、こちらも何ができるか別途試して見たいです。

（2016/02/02追記）AtlasとGitHubとの連携方法を以下に書きました。
[Terraform+Atlas+GitHubでAWSのIAMユーザのグループポリシーをいいかんじに管理する](http://engineer.crowdworks.jp/entry/2016/02/02/101330)

# おわりに

あとはIAMのグループポリシーのJSON書くだけですね。それが一番つらいんですが。。。。