terraformでIAM roleを作成しEC2にアタッチする時のtips

タイトルまんまですが、terraformでIAM role及びpolicyを作成してEC2インスタンスに当てようとした時に手こずったので記事にします。

まずはEC2本体を用意

```:ec2_sgw.tf
data "aws_ssm_parameter" "sgw_ami" {
  name = "/aws/service/storagegateway/ami/FILE_S3/latest"
}

resource "aws_instance" "sdw_test" {
  ami                    = data.aws_ssm_parameter.sgw_ami.value
  vpc_security_group_ids = ["sg-00000000"]
  subnet_id              = "subnet-00000000"
  iam_instance_profile   = aws_iam_instance_profile.instance_role.id
  key_name               = "hoge-key"
  instance_type          = "m4.xlarge"
  tags = {
    Name = "sgw_test"
  }
  root_block_device {
    volume_type           = "gp3"
    iops                  = 3000
    throughput            = 125
    volume_size           = 80
    delete_on_termination = true
    encrypted             = false
    tags = {
      Name = "sgw_root"
    }
  }
  ebs_block_device {
    device_name           = "/dev/sdf"
    volume_type           = "gp3"
    iops                  = 3000
    throughput            = 125
    volume_size           = 150
    delete_on_termination = true
    encrypted             = false
    tags = {
      Name = "sgw_cache"
    }
  }
}
```

storage gatewayをterraformで構築しようとしている途中での記事なので、AMIとかEBSが余分にぶら下がっているとか権限違うんじゃね？みたいな話は本題ではないのでスルーでお願いします（後日記事にする予定。つまり未定）。

# tips 1

順番的にポリシー→ロール→アタッチなのですが、先にEC2のファイルを載せたので先に書いておきます。
肝はこれ

```
  iam_instance_profile   = aws_iam_instance_profile.instance_role.id
```

EC2にroleを当てる時はインスタンスプロファイルとしてroleを当てる必要があるということです（まぁ当然のことなんですが。。。）。
ロール書いた！ロールを直接指定したのにエラー吐く！なんでや！！という初歩的なトラップに引っかかりました。

例えばですが以下のように

```
  iam_role   = aws_iam_role.ec2_role.id
```

こんな書き方でEC2.tfの中に書いてもダメですよ、ということです。
見た目的に行けそうな気がしますがちゃんとインスタンスプロファイルにしてから当ててましょう。

# tips 2

では順番にポリシーから行きましょう。

公式はこれ

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy

ここだとtfファイルに直接jsonファイルを書いている例が出ていますね。

```
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = [
          "ec2:Describe*",
        ]
        Effect   = "Allow"
        Resource = "*"
      },
    ]
  })
}
```

これでも勿論問題ないですが、IAMポリシーはterraformさんの書式に合わせて書くよりjsonをそのまま書いちゃったほうがなんだかんだ便利だったりします
(JSONシンタックスチェックが使えたりコピペでレッツゴーしたい時とか)。

ってことでポリシー本体部分を別途hoge.jsonみたいな感じで外出しして、ポリシーのtfファイルからjsonファイルを参照してあげることが出来ます。

./からポリシー用のディレクトリを掘ってjsonファイルを書いていきます(pathを指定すればjsonの置き場はお好みでおｋ)。
今回は特定のS3バケットにのみアクセスできるようにするポリシーを書いています。

```:policy/s3_sgw-hoge.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::sgw-hoge",
                "arn:aws:s3:::sgw-hoge/*"
            ]
        }
    ]
}
```

まぁこれはよくあるやつですね。内容については割愛します。
んで、このjsonファイルをtfファイルから参照してあげる書き方は以下

```
resource "aws_iam_role_policy" "s3_policy_01" {
  name   = "s3_sgw-hoge_role"
  role   = aws_iam_role.s3_role.id
  policy = file("./policy/s3_sgw-hoge.json")
}
```

ココですね

```
  policy = file("./policy/s3_sgw-hoge.json")
```

これでポリシー本体をファイル指定しています。
jsonファイルとして外出ししてあげたほうが個人的には見やすいので割とおすすめです、jsonで書けるし。

これに関しては後述(一番手こずった)

```
  role   = aws_iam_role.s3_role.id
```

# tips 3

roleについてです。
さてココが本番です。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role

最終的なファイルはこんな感じ

```
resource "aws_iam_role" "s3_role" {
  name               = "sgw_s3_role"
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
```

結論から書いてしまうと、GUIではポリシーを作成→ロールを作成してポリシーを指定ですがterraformだと逆です。
先入観というか、固定観念は非常によろしくないですね。

先にも書いたポリシー

```
resource "aws_iam_role_policy" "s3_policy_01" {
  name   = "s3_sgw-hoge_role"
  role   = aws_iam_role.s3_role.id
  policy = file("./policy/s3_sgw-hoge.json")
}
```

ここでポリシーを紐つけるroleを指定します。
role側で

```
  policy  =  aws_iam_role_policy. s3_policy_01
```

こんな感じに書いてもダメなわけです。
terraformではポリシー側からロールを指定、です。

次にこれ

```
  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
```

最初はこれを書かないでplan叩いて怒られました。
こんな感じ

```
resource "aws_iam_role" "s3_role" {
  name               = "sgw_s3_role"
}
```

これだとassume_role_policyが無いよと怒られます。
GUIから作成する時はあまり意識してなかった部分だったのですが(最悪)、これの正体はこれ

![スクリーンショット 2021-10-12 19.37.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1526421/a42bb790-d419-7c90-ae0d-032108a0a6e0.png)


これのことなんですねぇ
普段は脳死でEC2をクリックしていたのが最大の敗因。AWS認定の資格持っててもこういうところで躓いてちゃ型無しなわけです、反省。

ってことでEC2に当てたいロールなのでさっきのような書き方でOKということでした。


最後に上記で作成したroleを、インスタンスプロファイル作成時に指定してあげます。

```
resource "aws_iam_instance_profile" "instance_role" {
  name = "sgw_instance_role"
  role = aws_iam_role.s3_role.name
}
```

ここでのロールの指定は.nameで指定する必要があるようです、
んで、最終的にec2_sgw.tfのここで指定して完了です。

```
  iam_instance_profile   = aws_iam_instance_profile.instance_role.id
```


いかがだったでしょうか（アフィ並感(書いてみたかっただけ(これ書いてある記事ry))）Invalid syntax
何かと便利なterraform先生ですが、気を抜いてるとこういう罠に引っかかってしまうので気をつけましょう。

最後まで閲覧頂きありがとうございました。