terraformでiam roleにpolicyをアタッチする時のmanaged_policy_arnsとは

#はじめに
terraformを使ってAWSのIAMを管理する際、ロールに管理ポリシーをアタッチする際にはaws_iam_role_policy_attachmentを使用していましたが、aws_iam_roleのmanaged_policy_arns引数を利用してもアタッチができるため、両者の違いは何か調べてみました。

#aws_iam_role_policy_attachmentを用いたポリシーのアタッチ

aws_iam_role_policy_attachmentを用いてロールにポリシーをアタッチする方法は以下の通り。

```hcl
resource "aws_iam_role" "role" {
  name = "test-role"

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

resource "aws_iam_policy" "policy" {
  name        = "test-policy"
  description = "A test policy"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy_attachment" "test-attach" {
  role       = aws_iam_role.role.name
  policy_arn = aws_iam_policy.policy.arn
}
```
aws_iam_role、aws_iam_policyをそれぞれ定義した後、aws_iam_role_policy_attachmentにてそれらを紐づけるといった流れです。

#managed_policy_arnsを用いたpolicyのアタッチ

aws_iam_roleのmanaged_policy_arns引数を用いてロールにポリシーをアタッチする方法は以下の通り。


```hcl
resource "aws_iam_role" "role" {
  name = "test-role"
  managed_policy_arns = [aws_iam_policy.policy.arn]

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

resource "aws_iam_policy" "policy" {
  name        = "test-policy"
  description = "A test policy"

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}
EOF
}

```
aws_iam_roleの引数にmanaged_policy_arnsを追加し、ポリシーを指定することでアタッチができます。

#何が違うか

どちらの方法もロールに管理ポリシー(AWS管理ポリシー、カスタマー管理ポリシー)をアタッチすることが可能ですが、これらには互換性がなくterraform内では明確に異なるものとして認識されるようです。

>For a given role, this resource is incompatible with using the aws_iam_role resource managed_policy_arns argument. When using that argument and this resource, both will attempt to manage the role's managed policy attachments and Terraform will show a permanent difference.

しかしながら、マネジメントコンソールでのポリシーアタッチなどterraform管理外でリソースを変更した際の挙動に違いがありました。

##マネジメントコンソールにて新たにポリシーをアタッチした際の挙動

マネジメントコンソールにてterraformでは管理していないポリシー(ここではpolicy_bとします)を手動で新たにアタッチした後、terraform applyを実行すると以下のような挙動となりました。

| アタッチ方法                            | terraform apply   |
|:-------------------------------------|:------------      |
| aws_iam_role_policy_attachment       | no change         |
| managed_policy_arns                  | policy_bをデタッチ  |

managed_policy_arnsで定義した場合のみ外部での変更を検知した際に、`事前に定義した状態`に戻りました。


##マネジメントコンソールにて既存ポリシーをデタッチした際の挙動

マネジメントコンソールにてterraformで事前に定義済みのポリシー(ここではpolicy_aとします)を手動でデタッチ後、terraform applyを実行すると以下のような挙動となりました。

| アタッチ方法                            | terraform apply   |
|:-------------------------------------|:------------      |
| aws_iam_role_policy_attachment       | policy_aを再アタッチ   |
| managed_policy_arns                  | policy_aを再アタッチ   |

デタッチの場合は両者ともに事前に定義した状態に戻りました。

上記２つの結果から、managed_policy_arnsは外部での変更があった場合terraformの定義状態に戻るのに対し、aws_iam_role_policy_attachmentでは外部での追加は許容する挙動となることがわかります。

##両方で管理した場合
上記方法の両方を利用してロールにポリシーをアタッチした場合の挙動を調べました。
**この2つの管理方法を併用することは推奨されておりませんので、本番環境等では利用しないでください。**

###両方で同一のポリシーをアタッチした場合
エラーなく、ポリシーがアタッチされます。


###両方で同一のポリシーをデタッチした場合
エラーなく、ポリシーがアタッチされます。

###両方で同一のポリシーをアタッチ後、managed_policy_arnsのみ当該ポリシーをデタッチした場合
apply1回目:当該ポリシーをデタッチ
apply2回目:当該ポリシーをアタッチ
apply3回目:当該ポリシーをデタッチ
apply4回目:当該ポリシーをアタッチ
・・・・・

アタッチとデタッチを繰り返します。

###両方で同一のポリシーをアタッチ後、aws_iam_role_policy_attachmentのみ当該ポリシーをデタッチした場合
apply1回目:当該ポリシーをデタッチ
apply2回目:当該ポリシーをアタッチ
apply3回目:no change

2回applyを実施した段階で定常状態へと遷移します。

#おわりに
terraformにてロールに管理ポリシーをアタッチする方法としてmanaged_policy_arnsとaws_iam_role_policy_attachmentの２種類の方法があり、両者の挙動には明確な違いがあることがわかりました。terraformでの定義状態を遵守したい場合にはmanaged_policy_arnsを、定義状態+αな変更を許容する場合にはaws_iam_role_policy_attachmentでよいのかなと思います。
また、両方の管理方法を利用してリソースを定義するとterraform applyの結果が循環してしまう等想定外の結果を招いてしまうことがわかりました。

# 参考文献

この記事は以下の情報を参考にして執筆しました。

- [aws_iam_role | Resources | hashicorp/aws](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role)
- [aws_iam_role_policy_attachment | Resources | hashicorp/aws](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment)