terraform: AWS provider や s3 backend の設定に role の arn をハードコードせずに switch role や MFA 環境で実行する方法

# 概要

terraform を MFA 認証有の IAM ユーザ権限で switch role しつつ実行するのに手間どったので、対応方法を記載しておきます。
実行時に使用する switch role 先の arn を aws provider や s3 backend 設定にハードコードすると簡単にできるらしいのですが、role は環境（開発/テスト/本番）依存するものなので、ハードコードしたくないですよね。

同じようなことで困っている人は世の中にそこそこいて、関連する issue がいくつか挙がっています。
その中で、terraform の開発者っぽい人が、"terraform は自動実行を意識しているので、MFA の one time password を interactive に入力するとか対応しねーんだよ！" と言っているコメントがあったので、それをヒントに workaround を調査しました。

# MFA および switch role 環境の場合

## 事前の設定

~/.aws/config, credentials ファイルは通常通り設定済みとします。# mfa の記述をしないのがポイント

ポイントは以下です。

* source profile のアカウントへのログインには MFA が必要
* target profile のとこに MFA の設定は記述しない

``` ~/.aws/config
[profile source]
output = json
region = ap-northeast-1

[profile target]
role_arn = <arn of role to switch>
source_profile = source
region = ap-northeast-1
# mfa の設定は記述しない
```

``` ~/.aws/credentials 
[source]
aws_access_key_id = <your access key id>
aws_secret_access_key = <your secret key>
```

jq コマンドをダウンロードして、path を通しておきます。

## terraform 実行前に実行すること

ポイントは IAM 認証時に発行される session token を事前に取得して設定しておくことです。

``` sh:コマンド 
# 環境変数をクリアする。環境変数に認証情報が残っていると aws cli, sdk に（意図せずに）参照されてしまう。
unset AWS_SHARED_CREDENTIALS_FILE
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
# token を取得する
export AWS_PROFILE=source # changeme
aws sts get-session-token --serial-number <your mfa device serial number> --token-code <one time code> > tmp.token
aws_access_key_id=`cat tmp.token| jq -r ".Credentials.AccessKeyId"`
aws_secret_access_key=`cat tmp.token| jq -r ".Credentials.SecretAccessKey"`
aws_session_token=`cat tmp.token| jq -r ".Credentials.SessionToken"`
# .aws/credentials ファイルとは別に credentials ファイルを作成し、terraform 実行時に参照する。
export AWS_SHARED_CREDENTIALS_FILE=tmp.credentials # 参照する credentials ファイルを変更する。
cat << EOF > $AWS_SHARED_CREDENTIALS_FILE
[source]
aws_session_token=$aws_session_token
aws_secret_access_key=$aws_secret_access_key
aws_access_key_id=$aws_access_key_id
EOF
export AWS_SDK_LOAD_CONFIG=1 # terraform AWS provider v3.0.0 以降は default 設定なので指定不要っぽい
export AWS_PROFILE=target # changeme
```

後は、通常通り terraform を実行できます。
AWS_SHARED_CREDENTIALS_FILE=tmp.credentials にて、session token が記載された credential ファイルを参照するようにしています。

# MFA のない switch role 環境の場合

## 事前の設定

~/.aws/config, credentials ファイルは通常通り設定済みとします。

## terraform 実行前に実行すること

``` sh:コマンド 
export AWS_SDK_LOAD_CONFIG=1 # terraform AWS provider v3.0.0 以降は default 設定なので指定不要っぽい
export AWS_PROFILE=target # changeme
```

後は、通常通り terraform を実行できます。

# 参考: 私が上記設定を試した terraform の構成

backend には s3 を用いています。

``` terraform:terraform.tf
terraform {
  required_version = ">= 0.12.26"
  backend "s3" {}
}

# Configure the AWS Provider
provider "aws" {
  version = ">= 2.62.0"
  region  = "ap-northeast-1"
}
```

backend.hcl は環境別に用意しています。

``` :env/dev/backend.hcl
bucket   = <dev 用の bucket 名>
key      = "sample_terraform.tfstate"
region   = "ap-northeast-1"
encrypt  = true
```

terraform 実行コマンドは以下です。

``` bash:terraform実行コマンド
ENV="dev"
export TF_DATA_DIR=./.terraform/$ENV
terraform init -backend-config=env/${ENV}/backend.hcl
terraform apply -var="env=${ENV}"
```

# まとめ

こういうどーでもいいことで時間を使いたくないものですよね。
誰かのお役に立てばと思います。

もっと elegant な方法をご存知の方は教えてください。