TerraformでIAMグループ・ユーザを作成する

- 今日は以前から社内からAWSの機能検証を気軽に実施できる環境がほしいというリクエストがあったため、新たにAWSアカウントを作成し、異なる権限をもったIAMグループを作成し、エンジニアごとに所属させたことを記事にしたいと思います。

### 作成するグループとそれに付与する管理ポリシー

* グループ名：administrator
  * 管理ポリシー名：AdministratorAccess

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}
```

* グループ名：development
  * 管理ポリシー名：PowerUserAccess

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "NotAction": [
                "iam:*",
                "organizations:*"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:CreateServiceLinkedRole",
                "iam:DeleteServiceLinkedRole",
                "iam:ListRoles",
                "organizations:DescribeOrganization"
            ],
            "Resource": "*"
        }
    ]
}
```

* グループ名：operators
  * 管理ポリシー名：未設定

### 作成するユーザ

* 11ユーザ作成
   * administratorグループへ追加

### 設定ファイル構成
```bash
.
├── README.md
├── iam_group_membership.tf
├── iam_user.tf
├── main.tf
├── terraform_remote_state.tf
└── variables.tf
```

- variables.tf

```tf
variable "aws_iam_user" {
  type = "list"

  default = [
    "exp.example.yell",
    "exp.example.ange",
    "exp.example.etoile",
    "exp.example.amor",
    "exp.example.macherie",
    "exp.example.whip",
    "exp.example.custerd",
    "exp.example.gelato",
    "exp.example.macaron",
    "exp.example.chocolat",
    "exp.example.parfait",

  ]
}

variable "aws_iam_group" {
  type = "list"

  default = [
    "administrator",
    "development",
    "operators",
  ]
}
```
* iam_user.tf
  * シークレットキーは[keybase](https://keybase.io/)を使用して暗号化し、ユーザIDと関連付けしてoutputさせています

```tf
resource "aws_iam_user" "example_exp" {
  count         = "${length(var.aws_iam_user)}"
  name          = "${element(var.aws_iam_user, count.index)}"
  path          = "/"
  force_destroy = true
}

resource "aws_iam_user_login_profile" "example_exp_login_profile" {
  count                   = "${length(var.aws_iam_user)}"
  user                    = "${element(var.aws_iam_user, count.index)}"
  pgp_key                 = "keybase:exp_example"
  password_reset_required = true
  password_length         = "20"
}

resource "aws_iam_access_key" "example_exp_access_key" {
  count   = "${length(var.aws_iam_user)}"
  user    = "${element(var.aws_iam_user, count.index)}"
  pgp_key = "keybase:exp_example"
}

output "encrypted_secret" {
  value = "${join("\n", aws_iam_access_key.example_exp_access_key.*.encrypted_secret)}"
}

output "id" {
  value = "${join("\n", aws_iam_access_key.example_exp_access_key.*.id)}"
}

output "user" {
  value = "${join("\n", aws_iam_access_key.example_exp_access_key.*.user)}"
}
```
* iam_group_membership.tf

```tf
resource "aws_iam_group_membership" "example_exp_group_membership" {
  count = "${length(var.aws_iam_user)}"
  name  = "example_exp_group_membership"

  users = [
    "${element(var.aws_iam_user, count.index)}",
  ]

  group = "${element(var.aws_iam_group, 0)}"
}

resource "aws_iam_group" "example_exp_group" {
  count = "${length(var.aws_iam_group)}"
  name  = "${element(var.aws_iam_group, count.index)}"
}

resource "aws_iam_group_policy_attachment" "policy-attach" {
  group      = "${element(var.aws_iam_group, count.index)}"
  policy_arn = "arn:aws:iam::************:policy/administratoraccess"
}
```

* output

```sh
$ terraform output
encrypted_secret = wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
wcFMA615XJelCVO0ARAA(snip)
id = AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
AKIA****************
user =exp.example.yell
exp.example.ange
exp.example.etoile
exp.example.amor
exp.example.macherie
exp.example.whip
exp.example.custerd
exp.example.gelato
exp.example.macaron
exp.example.chocolat
exp.example.parfait
```

* 作成したユーザ分のシークレットキーをoutputすることはできるんですが、outputにcount処理が使えなく、どのキーがどのユーザに紐付いてるのかわからず...、また、ユーザ分のシークレットキーを復号化する際は、`terraform output encrypted_secret | base64 --decode | keybase pgp decrypt`で処理するんですが、複数のoutputをうまく処理できなく困ってました...
どう処理をぶん回すのが効率がいいか悩んでいたところ弊社が誇るRuby関西のFounderでもあるRubyistの[cuzic](https://github.com/cuzic?tab=repositories) （a.k.a Tomoya Kawanishi ）が助けてくれました。「ちょっとまっとき〜」と言って1時間後に下記ワンライナー付きでメンションがきました。

```sh
$ terraform output -json | ruby -rjson -ryaml -e 'json = JSON.load(ARGF); keys = %w(user id encrypted_secret); puts [keys, *keys.map{|key| v = json[key]["value"].split; key == "encrypted_secret" ? v.map{|s| `echo #{s} | base64 -d | keybase pgp decrypt -S exp_example`.chomp} : v}.transpose].map{|a| a.join(",")}'
```

素敵やん...!

* 実行結果

```sh
user,id,encrypted_secret
exp.example.yell,AKIA****************,Ya3R************************************
example.ange,AKIA****************,GYW7************************************
example.etoile,AKIA****************,bUpd************************************
exp.example.amor,AKIA****************,5lHm************************************
exp.example.macherie,AKIA****************,RyoM************************************
exp.example.whip,AKIA****************,r7EW************************************
exp.example.custerd,AKIA****************,CoBk************************************
exp.example.gelato,AKIA****************,FWYd************************************
exp.example.macaron,AKIA****************,4taL************************************
exp.example.chocolat,AKIA****************,4taL************************************
exp.example.parfait,AKIA****************,4taL************************************
```

最高やん...!

* これらをまとめてMFAを使うように手順も併せて配布すれば、きっと開発速度が向上し、技術的負債を熨斗付きで返済するようなイノベーションが発生するはず...!