以前こういう記事を書いたんですが、
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 が剥がれた状態になる
- 代わりに、以下のような手順を踏む必要がある
- 既存で定義してある
iam_policy_attachment
を*.tfstate
の管理から外す(terraform state rm
) -
iam_*_policy_attachment
リソースを定義し、terraform apply
する
- 既存で定義してある
事故例
実際に私の所属しているチームで起こった事故を紹介します。
前提
ManagedPolicy である AmazonS3ReadOnlyAccess
が各 IAM User にアタッチされているとします:
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_attachment
を aws_iam_user_policy_attachment
に移行する
まず、上記のコードを以下のようなコードに書き換えます。
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したときの結果と同じにならないんですよね...!! (これは仕様じゃなくてバグなのかも)
正しい移行方法
上記のような問題を回避するために、以下のような手順で移行します。
-
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.
- ソースコードを修正する
```: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 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 の順番に注意しましょう。