aws_iam_policy_attachment はIAMポリシーを排他的にアタッチする

3月 29, 2022AWS,IAM_Policy関連,terraform

概要

  • Terraform の “aws_iam_policy_attachment" を使用した失敗談を記事にします。
  • 先ず、“aws_iam_policy_attachment" は、IAMポリシーをIAM のuser(s), role(s), group(s) にアタッチするためのリソースです。この “aws_iam_policy_attachment" は、使用方法を理解しないと、想定外の結果を引き起こします。本記事の内容をお読みください。

 

“aws_iam_policy_attachment" の注意事項

  • aws_iam_policy_attachment はIAMポリシーを排他的にアタッチします。
  • aws_iam_policy_attachment に定義されていない user(s), role(s), group(s) から、対象のIAMポリシーがデタッチされます。これは、aws_iam_policy_attachment を使用する際は、対象のIAMポリシーを使用中のすべてのuser(s), role(s), group(s) についても定義する必要があるためです。
  • 同一 AWSアカウント内の user(s), role(s), group(s) が影響を受けます。
  • Terraform にて作成されたリソース、コンソール等Terraform 以外で作成されたリソースの区別に関わらず、すべてのIAM リソースが影響を受ける可能性があります。

 

  • 以下のドキュメントにも記載があります。

 

 

“aws_iam_policy_attachment" の解説

事象の詳細

  • こちらの記事を元に、"aws_iam_policy_attachment" リソースを作成します。以下の様に、IAMロールに、カスタマー管理のポリシーと、AWS管理ポリシー(AmazonEC2ReadOnlyAccess)の2つをアタッチします。
resource "aws_iam_policy_attachment" "policy-attachment_s3-role-for-ec2-attach1" {
  name       = "policy-attachment_s3-role-for-ec2-attachment1"
  policy_arn = aws_iam_policy.policy_allow_rw_access_s3_bucket.arn
  groups     = []
  users      = []
  roles      = [
    aws_iam_role.s3-role-for-ec2.name
  ]
}

resource "aws_iam_policy_attachment" "policy-attachment_s3-role-for-ec2-attach2" {
  name       = "policy-attachment_s3-role-for-ec2-attachment2"
  policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
  groups     = []
  users      = []
  roles      = [
    aws_iam_role.s3-role-for-ec2.name
  ]
}

 

  • terraform apply を実行します。2つのリソースが追加されました。
$ terraform apply --var-file=terraform.tfvars
aws_iam_policy.policy_allow_rw_access_s3_bucket: Refreshing state... [id=arn:aws:iam::111111111111:policy/s3-policy-example]
aws_iam_role.s3-role-for-ec2: Refreshing state... [id=s3-role-example]
aws_iam_instance_profile.instance-profile_s3-role-for-ec2: Refreshing state... [id=s3-role-example]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach1 will be created
  + resource "aws_iam_policy_attachment" "policy-attachment_s3-role-for-ec2-attach1" {
      + id         = (known after apply)
      + name       = "policy-attachment_s3-role-for-ec2-attachment1"
      + policy_arn = "arn:aws:iam::111111111111:policy/s3-policy-example"
      + roles      = [
          + "s3-role-example",
        ]
    }

  # aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2 will be created
  + resource "aws_iam_policy_attachment" "policy-attachment_s3-role-for-ec2-attach2" {
      + id         = (known after apply)
      + name       = "policy-attachment_s3-role-for-ec2-attachment2"
      + policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
      + roles      = [
          + "s3-role-example",
        ]
    }

Plan: 2 to add, 0 to change, 0 to destroy.


Warning: Argument is deprecated

Use shared_credentials_files instead.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach1: Creating...
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2: Creating...
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach1: Creation complete after 1s [id=policy-attachment_s3-role-for-ec2-attachment1]
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2: Creation complete after 1s [id=policy-attachment_s3-role-for-ec2-attachment2]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: ./terraform.tfstate

 

  • この状態で、コンソールからIAM リソースを確認します。こちらの期待通り、IAMロールに、2つのポリシーがアタッチされていることが分かります。

 

  • AWS管理ポリシー(AmazonEC2ReadOnlyAccess)には、今回新規にアタッチを行ったIAMロールと、その他に既存のリソースとしてIAMユーザー、IAMロールがあり、計3つのリソースが関連付けされていることが分かります。

 

  • 次に、Terraform のコードは一切変更せず、再度terraform apply を実行します。出力結果に「No changes. Infrastructure is up-to-date.」と表示されることを期待しましたが、「1 to change」と表示されました。今回は、yes と入力して変更を適用します。 ※実際には、出力結果が期待と異なる場合にyes を入力してはいけません。
$ terraform apply --var-file=terraform.tfvars
aws_iam_policy.policy_allow_rw_access_s3_bucket: Refreshing state... [id=arn:aws:iam::111111111111:policy/s3-policy-example]
aws_iam_role.s3-role-for-ec2: Refreshing state... [id=s3-role-example]
aws_iam_instance_profile.instance-profile_s3-role-for-ec2: Refreshing state... [id=s3-role-example]
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2: Refreshing state... [id=policy-attachment_s3-role-for-ec2-attachment2]
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach1: Refreshing state... [id=policy-attachment_s3-role-for-ec2-attachment1]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2 will be updated in-place
  ~ resource "aws_iam_policy_attachment" "policy-attachment_s3-role-for-ec2-attach2" {
        groups     = []
        id         = "policy-attachment_s3-role-for-ec2-attachment2"
        name       = "policy-attachment_s3-role-for-ec2-attachment2"
        policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
      ~ roles      = [
          - "niikawa-test-role-ec2",
            "s3-role-example",
        ]
      ~ users      = [
          - "test-niikawa",
        ]
    }

Plan: 0 to add, 1 to change, 0 to destroy.


Warning: Argument is deprecated

Use shared_credentials_files instead.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2: Modifying... [id=policy-attachment_s3-role-for-ec2-attachment2]
aws_iam_policy_attachment.policy-attachment_s3-role-for-ec2-attach2: Modifications complete after 2s [id=policy-attachment_s3-role-for-ec2-attachment2]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: ./terraform.tfstate

 

  • この状態で、コンソールからIAM リソースを確認します。AWS管理ポリシー(AmazonEC2ReadOnlyAccess)は、今回新規にアタッチを行ったIAMロールだけに変わり、既存のリソースであったIAMユーザー、IAMロールがデタッチされていることが分かります。

 

  • terraform apply の出力から「1 to change」の内容として、既存のリソースであるIAMユーザー、IAMロールに “" がマークされており、二回目の apply実行によって既存のリソースから対象のIAMポリシーがデタッチされることが読み取れます。
  • Infrastructure as Code であれば、べき等性が担保されていると思いがちですが、初回の実行でTerraform のstateファイルに対象のIAMロールと、AWS管理ポリシー(AmazonEC2ReadOnlyAccess)の関連付けが 1:1 であると管理されました。初回の apply実行で済めば良いのですが、二回目の実行で既存のリソースからポリシーをデタッチする動作が起きていることが分かります。

 

対策

  • “aws_iam_policy_attachment" リソースを使用する場合は、対象のIAMポリシーを使用中のすべてのuser(s), role(s), group(s) についても定義するか、あるいは"aws_iam_role_policy_attachment" リソースを代わりに使用します。
  • 今回の場合では、下記の様にIAMロールとIAMポリシーの関連付けを定義します。

 

resource "aws_iam_role_policy_attachment" "role-policy-attachment_s3-role-for-ec2-attach1" {
    role = aws_iam_role.s3-role-for-ec2.name
    policy_arn = aws_iam_policy.policy_allow_rw_access_s3_bucket.arn
}

resource "aws_iam_role_policy_attachment" "role-policy-attachment_s3-role-for-ec2-attach2" {
    role = aws_iam_role.s3-role-for-ec2.name
    policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"
}

 

 

参考資料