AWSで terraform apply
するにはAWSの操作権限の全てを与えておくのが現実的である。さもないと、未利用だったサービス・新しいサービスを使おうとする度にpolicy修正が必要になってしまう。
terraform 作業専用の instanceを用意する場合でも、強すぎる権限を常時全開にするわけにはいかないだろう。instance-profileとしては ReadOnlyAccess などもっと軽い権限だけにして、 terraform
コマンドのときだけ全権を与えたい。そうおもってやってみたらはまりポイントがあったのでここに記録する。
※terraformオペレータのIAMユーザをMFA必須にして、そのユーザからの assume-roleをするのが、たぶんベストバランスだろう。この記事はその一歩手前のところである。assume-roleするための方法としては共通のはず。
role/ReadOnly-Role
- Usecase: EC2
- instance-profile として使える role
- これを terraform作業用instance のIAM roleとして割り当てる
- ReadOnlyAccess AWS 管理ポリシー
- ProfileOnlyPolicy
- instance-profile:ReadOnly-Role の instanceでの利用でなければ何もさせない
{
"Version": "2012-10-17",
"Statement": [
{
"Resource": "*",
"Action": "*",
"Effect": "Deny",
"Condition": {
"ArnEquals": {
"ec2:InstanceProfile": "arn:aws:iam::<account-id>:role/ReadOnly-Role"
}
}
}
]
}
role/Terraform-Role
- AdministratorAccess AWS 管理ポリシー
- Terraform用に原則全部の操作を可能とする
- ProfileOnlyPolicy
- instance-profile:ReadOnly-Role の instanceでの利用でなければ何もさせない
- PolicyDocument
- role/ReadOnly-Role からの switch だけを許す
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account-id>:role/ReadOnly-Role"
},
"Action": "sts:AssumeRole"
}
]
}
provider.tf
provider "aws" {
region = "ap-northeast-1"
version = ">1.56.0"
profile = "terraform-role"
}
terraform {
backend "s3" {
bucket = "foo-terraform"
key = "bar/terraform.tfstate"
region = "ap-northeast-1"
skip_metadata_api_check = true
profile = "terraform-role"
}
}
~/.aws/credentials
[terraform-role]
role_arn = arn:aws:iam::<account-id>:role/Terraform-Role
credential_source = Ec2InstanceMetadata
policy/role/credentials の動作検証
terraform抜きで確認できる範囲をまずは検証する。
$ aws sts get-caller-identity
{
"Account": <account-id>,
"UserId": "xxxx:<instance-id>",
"Arn": "arn:aws:sts::<account-id>:assumed-role/ReadOnly-Role/<instance-id>"
}
$ aws sts get-caller-identity --profile terraform-role
{
"Account": <account-id>,
"UserId": "xxxx:botocore-session-1598506181",
"Arn": "arn:aws:sts::<account-id>:assumed-role/Terraform-Role/botocore-session-1598506181"
}
terraform apply
既存のリソースに新規Tagを追加するとか、注記用のTagの文字列を少し変えるとかで、terraform apply
はまりポイント
terraform state を S3保存するための backend "s3"
ブロックが難物だった。
terraformオペレータが複数になることや、terraform作業instanceが障害や誤操作で失われることを想定すると、S3保存は外せない。
しかし provider
ブロックで profile
指定しても export AWS_PROFILE=
の環境変数指定しても、エラーになる。
Failed to save state: failed to upload state: AccessDenied: Access Denied
検証用に追加したTagやTag文字列はリソースに反映されているので、assume-roleはできているはずなのに。
コードブロックにあるように、backend
ブロックにも profile
指定が要るのだった。 backend
保存処理は provider
ブロックとは独立の credential設定になっているようだ。おまけにこちらには、skip_metadata_api_check = true
オプションが必要だった。
参照した issue
terraform-providers/terraform-provider-aws#5018
The s3 backend does not use the provider block.
次にはまったときはこれも試そう
I run terraform init with TF_LOG=DEBUG