LoginSignup
7
6

More than 3 years have passed since last update.

iam_policy_attachment を移行するときの落とし穴

Last updated at Posted at 2019-04-04

以前こういう記事を書いたんですが、
terraform の aws_iam_policy_attachment は使わないほうが無難

iam_policy_attachment で定義してあるリソースを iam_*_policy_attachment に移行しようとするときに、場合によっては かなり致命的な落とし穴 があったので共有します。

なお、そもそもなぜ移行しないといけないか? については上記の記事を読んでください。

TL;DR

  • iam_policy_attachment で定義してあるリソースをソースコードから消して iam_*_policy_attachment を作り、 terraform apply する方法だと apply 後に Policy が剥がれた状態になることがある
    • terraform がリソースを delete するのと create する順番は不定
    • iam_poilicy_attachment の destroy が iam_*_policy_attachment の create よりも後に来る場合、 最終結果で policy が剥がれた状態になる
  • 代わりに、以下のような手順を踏む必要がある
    1. 既存で定義してある iam_policy_attachment*.tfstate の管理から外す(terraform state rm)
    2. iam_*_policy_attachment リソースを定義し、 terraform apply する

事故例

実際に私の所属しているチームで起こった事故を紹介します。

前提

ManagedPolicy である AmazonS3ReadOnlyAccess が各 IAM User にアタッチされているとします:

iam_policy.tf
data "aws_iam_policy" "s3_readonly_access" {
  arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

variable users {
  default = [
    "Taro",
    "Jiro",
    "Saburo",
  ]
}

resource "aws_iam_policy_attachment" "s3_readonly_access" {
  name       = "s3_readonly_access"
  users      = ["${var.users}"]
  policy_arn = "${data.aws_iam_policy.s3_readonly_access.arn}"
}

User に attach された Policy の状態を確認します:

<~>-1-> aws iam list-attached-user-policies --user-name Taro --profile peakystack
{
    "AttachedPolicies": [
  1 data "aws_iam_policy" "s3_readonly_access" {
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
        }
    ]
}
<~>-> aws iam list-attached-user-policies --user-name Jiro --profile peakystack
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
data "aws_iam_policy" "s3_readonly_access" {
        }
    ]
}
<~>-> aws iam list-attached-user-policies --user-name Saburo --profile peakystack
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
        }
    ]
}

aws_iam_policy_attachmentaws_iam_user_policy_attachment に移行する

まず、上記のコードを以下のようなコードに書き換えます。

iam_policy.tf
data "aws_iam_policy" "s3_readonly_access" {
  arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

variable users {
  default = [
    "Taro",
    "Jiro",
    "Saburo",
  ]
}

resource "aws_iam_user_policy_attachment" "s3_readonly_access" {
  count      = "${length(var.users)}"
  user       = "${element(var.users, count.index)}"
  policy_arn = "${data.aws_iam_policy.s3_readonly_access.arn}"
}

terraform plan

上記のコードで

terraform plan すると、以下のような差分が表示されます:

Terraform will perform the following actions:

  - aws_iam_policy_attachment.s3_readonly_access

  + aws_iam_user_policy_attachment.s3_readonly_access[0]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Taro"

  + aws_iam_user_policy_attachment.s3_readonly_access[1]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Jiro"

  + aws_iam_user_policy_attachment.s3_readonly_access[2]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Saburo"


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

delte と create のパターンですね。
plan の段階では、最終結果はそれぞれのユーザに AmazonS3ReadOnlyAccess ポリシーが付与されるように見えます

terraform apply

terraform apply を実施します

aws_iam_policy_attachment.s3_readonly_access: Destroying... (ID: s3_readonly_access)
aws_iam_user_policy_attachment.s3_readonly_access[1]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Jiro"
aws_iam_user_policy_attachment.s3_readonly_access[0]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Taro"
aws_iam_user_policy_attachment.s3_readonly_access[2]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Saburo"
aws_iam_user_policy_attachment.s3_readonly_access[1]: Creation complete after 3s
aws_iam_user_policy_attachment.s3_readonly_access[2]: Creation complete after 3s
aws_iam_user_policy_attachment.s3_readonly_access[0]: Creation complete after 3s (ID: Taro-20190404153229914700000002)
aws_iam_policy_attachment.s3_readonly_access: Destruction complete after 3s

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

destroy のほうが create よりも後に実行されている!!

IAM User にアタッチされたポリシーを見ると、もともとあった AWSS3ReadonlyPolicy が剥がれています。

<~/tmp>-> aws iam list-attached-user-policies --user-name Taro --profile peakystack
{
    "AttachedPolicies": []
}
<~/tmp>-> aws iam list-attached-user-policies --user-name Jiro --profile peakystack
{
    "AttachedPolicies": []
}
<~/tmp>-> aws iam list-attached-user-policies --user-name Saburo --profile peakystack
{
    "AttachedPolicies": []
}

このように、 aws_iam_policy_attachment の悪しき振る舞いによって、 applyしたときの最終状態がplanしたときの結果と同じにならないんですよね...!! (これは仕様じゃなくてバグなのかも)

正しい移行方法

上記のような問題を回避するために、以下のような手順で移行します。

  1. aws_iam_policy_attachment を teraform 管理下から外す

悪しき aws_iam_policy_attachment には退場してもらいます。

<~/tmp>-> terraform state rm aws_iam_policy_attachment.s3_readonly_access
1 items removed.
Item removal successful.
  1. ソースコードを修正する
```:iam_policy.tf
data "aws_iam_policy" "s3_readonly_access" {
  arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
}

variable users {
  default = [
    "Taro",
    "Jiro",
    "Saburo",
  ]
}

resource "aws_iam_user_policy_attachment" "s3_readonly_access" {
  count      = "${length(var.users)}"
  user       = "${element(var.users, count.index)}"
  policy_arn = "${data.aws_iam_policy.s3_readonly_access.arn}"
}
  1. terraform apply する

この場合は、 delete & create じゃなくて純粋に create だけになります。

<~/tmp>-> terraform apply
data.aws_iam_policy.s3_readonly_access: Refreshing state...

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_user_policy_attachment.s3_readonly_access[0]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Taro"

  + aws_iam_user_policy_attachment.s3_readonly_access[1]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Jiro"

  + aws_iam_user_policy_attachment.s3_readonly_access[2]
      id:         <computed>
      policy_arn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
      user:       "Saburo"


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

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_user_policy_attachment.s3_readonly_access[1]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Jiro"
aws_iam_user_policy_attachment.s3_readonly_access[2]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Saburo"
aws_iam_user_policy_attachment.s3_readonly_access[0]: Creating...
  policy_arn: "" => "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
  user:       "" => "Taro"
aws_iam_user_policy_attachment.s3_readonly_access[0]: Creation complete after 3s (ID: Taro-20190404154236127100000003)
aws_iam_user_policy_attachment.s3_readonly_access[2]: Creation complete after 3s (ID: Saburo-20190404154236122800000001)
aws_iam_user_policy_attachment.s3_readonly_access[1]: Creation complete after 3s (ID: Jiro-20190404154236127100000002)

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

ポリシーの紐づけが剥がれていないことがわかります。

<~/tmp>-> aws iam list-attached-user-policies --user-name Taro --profile peakystack
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
        }
    ]
}
<~/tmp>-> aws iam list-attached-user-policies --user-name Jiro --profile peakystack
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
        }
    ]
}
<~/tmp>-> aws iam list-attached-user-policies --user-name Saburo --profile peakystack
{
    "AttachedPolicies": [
        {
            "PolicyName": "AmazonS3ReadOnlyAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
        }
    ]
}

まとめ

私の所属するチームでは、 AdministratorAccess がすべての管理者ユーザから剥がされる という大惨事になりました。 root アカウントを使って権限を復活するという対応をするハメに...

terraform でリソースを delete & create する場合は delete と create の順番に注意しましょう。

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6