Terraform実行時のMFA認証を使ったAssume Roleを楽にできる方法がないか調べていたら、以下のコメントを見つけました。
Doesn't ask MFA token code when using assume_role with MFA required #2420
どうやらツールや長いコマンドの実行なしで、MFA認証ありでも簡単にAssume Roleができそうです。
便利だったのでブログにしてみました。
TerraformのMFA認証事情は以下のブログをご確認ください。
結論
この方法では、aws-vaultやaws-mfaなどのツールは不要です。
以下のようにProfileを用意して、terraformのコマンドを打つだけです。
~/.aws/config
[profile myprofile]
output=json
region=ap-northeast-1
role_arn=arn:aws:iam::01234567890:role/sato.masaki
mfa_serial=arn:aws:iam::12345678901:mfa/sato.masaki
[profile myprofile-tf]
credential_process = aws configure export-credentials --profile myprofile
main.tf
provider "aws" {
region = "ap-northeast-1"
profile = "myprofile-tf" # tfファイル側で記載せずに、環境変数AWS_PROFILEで渡してもOK
}
aws configure export-credentials
上記の./aws/config
内のaws configure export-credentials
あまり見慣れない方も多いのではないでしょうか。(私自身も使ったことがありませんでした)
credential_process
は認証情報を取得するためのコマンドを指定するオプションです。例えばaws-vaultのような外部ツールで取得した認証情報を渡したりできます。
.aws/config
[profile common]
credential_process=aws-vault --prompt terminal export classmethod --duration 12h --format=json
上記は、AWS Vaultで端末内のAWSアクセスキー平文保存をやめてみた | DevelopersIOから引用。
aws configure export-credentials
は既存のprofileから認証情報を取得し、出力するコマンドです。
(デフォルトは、credential_process
で予期されるスキーマで出力されます。)
$ aws configure export-credentials --profile "myprofile"
{
"Version": 1,
"AccessKeyId": "<アクセスキーID>",
"SecretAccessKey": "<シークレットアクセスキー>",
"SessionToken": "<セッショントークン>",
"Expiration": "2024-04-18T03:31:52+00:00"
}
profilemyprofile
から認証情報を取得して、profilemyprofile-tf
に認証情報をセットしています。
- 外部プロセスを使用して認証情報を作成する - AWS Command Line Interface
- export-credentials — AWS CLI 2.15.39 Command Reference
やってみた
Assume Roleして、Terraformでリソースを作成してみます。
AWSCLI Profileの準備
IAMユーザー管理アカウント(スイッチ元アカウント)でIAMアクセスキーを発行して、AWS CLIにアクセスキーを設定します。(既に実施済みの場合は、不要)
$ aws configure
AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [ap-northeast-1]:
Default output format :
.aws/config
に以下のように記述を追加します。
ARNやリージョン等は適宜置き換えてください。
.aws/config
[profile default]
output=json
region=ap-northeast-1
[profile sandbox]
output=json
region=ap-northeast-1
role_arn=arn:aws:iam::1234567890:role/test.taro # Terraformデプロイ先アカウントのIAMロール(要置き換え)
mfa_serial=arn:aws:iam::2345678901:mfa/test.taro # IAMユーザーのMFAの識別子(要置き換え)
source_profile=default
[profile sandbox-tf]
credential_process = aws configure export-credentials --profile "sandbox"
設定したprofileを使って、AWS CLIを実行してみます。
コマンド実行時にMFA Codeが求められます。入力して、以下のように出力が得られたらOKです。
$ aws sts get-caller-identity --profile sandbox-tf
Enter MFA code for arn:aws:iam::2345678901:mfa/test.taro:
{
"UserId": "AROAS52FR6JWMTMPR7PHT:botocore-session-1713404114",
"Account": "1234567890",
"Arn": "arn:aws:sts::1234567890:assumed-role/test.taro/botocore-session-1713404114"
}
Terraformの実行
以下のコードを用意して、terraform init
やterraform [plan/apply]
を試していきます。
今回はコード中でprofileを指定していますが、コード中で指定せずに環境変数AWS_PROFILE
で指定しても問題ありません。
(export AWS_PROFILE="sandbox-tf"
)
main.tf
terraform {
backend "s3" {
bucket = "<State管理バケット名>"
key = "profile-test/terraform.state"
region = "ap-northeast-1"
profile = "sandbox-tf"
}
}
provider "aws" {
region = "ap-northeast-1"
profile = "sandbox-tf"
}
resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "my-vpc"
}
}
Terraform CLIを試してみます。それぞれ正常に動作します。
セッションが切れていたら、コマンド実行時にMFA Codeが求められます。
$ terraform init
# 省略
Terraform has been successfully initialized!
# 省略
$ terraform plan
# 省略
Plan: 1 to add, 0 to change, 0 to destroy.
# 省略
$ terraform apply
# 省略
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
おまけ: 通常のスイッチロール用Profileでやってみる(失敗)
credential_process
を使わないProfileで実行 したケースを、おまけとして書いておきます。
今回の環境では、sandbox
が通常のProfileになります。
.aws/config
[profile sandbox]
output=json
region=ap-northeast-1
role_arn=arn:aws:iam::1234567890:role/test.taro # Terraformデプロイ先アカウントのIAMロール(要置き換え)
mfa_serial=arn:aws:iam::2345678901:mfa/test.taro # IAMユーザーのMFAの識別子(要置き換え)
source_profile=default
main.tf
terraform {
backend "s3" {
bucket = "<State管理バケット名>"
key = "profile-test/terraform.state"
region = "ap-northeast-1"
profile = "sandbox"
}
}
provider "aws" {
region = "ap-northeast-1"
profile = "sandbox"
}
resource "aws_vpc" "my_vpc" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "my-vpc"
}
}
Terraform CLIを実行してみます。以下のエラーメッセージが出力されます。
TerraformはMFAトークンを要求しないので、エラーになってしまいます。
$ terraform init
Initializing the backend...
╷
│ Error: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
│
│
╵
$ terraform plan # backendのprofileを変更してinitを成功させた後
Planning failed. Terraform encountered an error while generating this plan.
╷
│ Error: assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.
│
│ with provider["registry.terraform.io/hashicorp/aws"],
│ on main.tf line 10, in provider "aws":
│ 10: provider "aws" {
│
おわりに
私は普段aws sts assume-role
の結果(AWSアクセスキーID、シークレットキー、セッショントークン)を環境変数に設定するスクリプトを使って、Terraformを実行していました。
しかし、今回のサンプルコードのようにProviderの部分でprofileを指定されていると、この方法は少し面倒です。
.aws/config
にprofileの設定と、.aws/credentials
に都度認証情報をいれる必要もあります。(他にも色々方法ありそうですが、いい方法が思いつかない)
今回紹介した方法は、tfファイル上でprofile指定がある場合でも使えますし、セットアップも容易なので今後は使っていきたいと思います。
以上、AWS事業本部の佐藤(@chari7311)でした。