MFAやSwitch role(Assume role)使用環境下でTerraformを使うTips

はじめに

Terraformは、インフラ構成をCodeで管理(IaC)したり、同様の環境を複数構築するのに便利なツールである。
しかし、Terraform用のCredentialを対象の各AWSアカウントから払い出せればスムーズだが、Security要件によってはそれが難しいケースもある。例えば、AWS ConsoleにLog-inするIAM Userを用いなければならなかったり、そのIAM UserにはMFAが義務付けられていたり、Switch role(Assume role)を用いてTerraformで使用するCredentialが無いアカウントに環境を構築しなければならないケースなどである。
このような少し複雑な構成の場合、Terraformの設定やApplyにはちょっとしたコツが必要となる。本記事では、そのようなTipsをまとめておきたい。

前提条件

今回は、以下のような構成おいて、TerraformでAWS Account B下に構築を行うことを想定する。

  • AWS Account Aについて、Terraform実行するIAM UserのCredentialが払い出されている
  • AWS Account BにはSwitch role(Assume role)用のRoleが設定されており、上記のAWS Account AのIAM UserからSwitch role(Assume role)できるようになっている
  • 上記のAWS Account AのIAM UserのCredentialにはMFAが設定されている

事前準備

設定方法

MFAを使うための方法

AWC Cli

[profile Account-A]
region = ap-northeast-1
output = text
mfa_serial = arn:aws:iam::123456789012:mfa/aws_cli

ポイントは、mfa_serialの部分。
ここに、Credential情報に紐づくMFAのARNを記述する。
ちなみに、本記事執筆時点では AWS CliのMFAにPassskeyは使用できない1ので、必ず認証アプリケーションを割り当てておく必要がある。
以下にSampleの設定ファイルを置いておく。
https://github.com/unitedstar-tech/common/blob/main/terraform_mfa/aws_config.sample

MFA使用環境下でTerraformを使う

結論から言うと、Terraform自体はMFAトークンによる対話型認証に対応していない。
そのため、AWS Cliのようにshared_config_filesの設定を用いてTerraformを利用することはできない。
そこで、Wrapper scriptを作成した。
https://github.com/unitedstar-tech/common/blob/main/terraform_mfa/terraform_mfa

Source
#!/bin/bash
export LANG=C

# ARGS
MFA_ARN="arn:aws:iam::123456789012:mfa/aws_cli"
SRC_PROFILE="Account-A"
VALID_DURATION="14400"

check_opts=0
while getopts "m:t:h" opt
do
    case $opt in
        m)
            MFA_CODE=$OPTARG
            check_opts=`expr $check_opts + 1`;;
        t)
            TERRAFORM_MODE=$OPTARG
            check_opts=`expr $check_opts + 2`;;
        h)
            echo "Usage: $0 [-m MFA_CODE] [-t TERRAFORM_ARGS]"
            exit 0;;
        \?)
            echo "[WARNING] Usage: $0 [-m MFA_CODE] [-t TERRAFORM_ARGS]";;
    esac
done
if [ $check_opts -ne 3 ]
then
    echo -e "[ERROR] Missing required options\n\nUsage: $0 [-m MFA_CODE] [-t TERRAFORM_ARGS]\n-h to see Help"
    exit 1
fi

# Main
DATA=`aws sts get-session-token --serial-number $MFA_ARN --profile $SRC_PROFILE --duration-seconds $VALID_DURATION --token-code $MFA_CODE`
if [ $? -ne 0 ]
then
    echo "[ERROR] Invalid MFA code"
    exit 1
fi
ACCESS_KEY=`echo $DATA | awk '{print $2}'`
SECRET_ACCESS_KEY=`echo $DATA | awk '{print $4}'`
TOKEN=`echo $DATA | awk '{print $5}'`
export AWS_ACCESS_KEY_ID=$ACCESS_KEY
export AWS_SECRET_ACCESS_KEY=$SECRET_ACCESS_KEY
export AWS_SESSION_TOKEN=$TOKEN
terraform $TERRAFORM_MODE

具体的な方式としては、AWS Security Token Service(AWS STS)にMFA One-time codeとCredential情報を渡すことでIAMの一時的な認証情報(Token)を払い出し2、そのTokenを環境変数にSetして認証に用いることでTerraformがMFA One-time Codeの入力なしに実行することを可能としている。

Usage
e.g.
# terraform_mfa -t "plan -target=aws_vpc.vpc" -m <MFAのOne-time code>

Switch role(Assume role)を使うための方法

AWS Cli

[profile Account-A]
region = ap-northeast-1
output = text
mfa_serial = arn:aws:iam::123456789012:mfa/aws_cli
[profile Account-B]
role_arn = arn:aws:iam::987654321098:role/AWSAdministratorAccess
source_profile = Account-A
region = ap-northeast-1
output = text
mfa_serial = arn:aws:iam::123456789012:mfa/aws_cli

ポイントは、source_profileにSwitch role(Assume role)元のProfileを、role_arnにSwitch role(Assume role)先のRole arnを設定する。
source_profileにMFAが設定されている場合、Switch role(Assume role)先のEntryにも同様にmfa_serialを設定する必要がある。

Terraform

まずは、Terraformを使うための設定を記述したTFファイル全文を掲載しておく。
ポイントとなる部分の解説は後述。

Source
terraform {
    required_version = ">= 1.10.5"
    required_providers {
        aws = {
        source = "hashicorp/aws"
        version = "~> 5.33.0"
        }
    }
    backend "s3" {
      bucket = "terraform-state-dev-987654321098-ap-northeast-1"
      key = "dev/terraform.tfstate"
      region = "ap-northeast-1"
      assume_role = {
        role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
      }
  }
}

provider "aws" {
  region = "ap-northeast-1"
  assume_role {
    role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
  }
}

provider "aws" {
  alias = "tokyo"
  region = "ap-northeast-1"
  assume_role {
    role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
  }
}

provider "aws" {
  alias = "osaka"
  region = "ap-northeast-3"
  assume_role {
    role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
  }

ポイントは、providerBlock.
ここに、assume_roleでSwitch role(Assume role)先のRoleのARNを記述する。

provider "aws" {
  region = "ap-northeast-1"
  assume_role {
    role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
  }
}
tfstateをSwitch role(Assume role)先のS3に配置する
    backend "s3" {
      bucket = "terraform-state-dev-987654321098-ap-northeast-1"
      key = "dev/terraform.tfstate"
      region = "ap-northeast-1"
      assume_role = {
        role_arn = "arn:aws:iam::987654321098:role/AWSAdministratorAccess"
      }

ポイントは、backendBlockのassume_roleセクション。
ここに、assume_roleでSwitch role(Assume role)先のRoleのARNを記述する。
なお、providerBlockと違い、backendBlockでは “assume_role”の後ろに”=”が入ることに注意が必要。

終わりに

以上のように、TerraformでMFAやSwitch role(Assume role)を扱う方法や要点をまとめた。
Webで検索すると、aws-vaultを用いる方法なども頻繁に紹介されているが、passGnuPGと言った依存モジュールも導入する必要があり、やや煩雑である。
少し複雑な環境においてより手軽にTerraformで扱うために、本記事が参考になれば幸いである。

参考記事