1. はじめに

Terraformを使って複数アカウント(管理用アカウントとリソース作成用アカウントなど)をまたぐクロスアカウント構成を構築する際、セキュアな認証ツールである aws-vault を併用すると、謎の認証エラー(AccessDeniedexit status 1)に悩まされることがあります。

その最大の原因は、「AWSプロファイル(~/.aws/config)」と「aws-vault」の役割を混同してしまうこと にあります。本記事では、この2つの違いを明確にし、Terraformで安全かつ確実に認証を使い分けるベストプラクティスを解説します。

2. 概念の比較:プロファイルとaws-vaultの違い

この2つは似たような名前(プロファイル名)で管理されるため混同しがちですが、役割が全く異なります。ここを意識することがトラブル解決の第一歩です。

比較項目 AWSプロファイル aws-vault
役割のイメージ 「手順書」 「受付窓口」
主な役割 「どのリージョンを使うか」「MFAのシリアルは何か」「どのコマンドで鍵を取得するか」といったルールの定義 実際のアクセスキーをOSのセキュア領域に暗号化して保管し、一時的な認証情報(STSトークン)を発行すること。
Terraformから見た扱い Terraformがコード内の profile = "xxx" を見て、自ら読みに行く設定ファイル。 Terraformを外側から包み込み、環境変数として一時キーを強制的に注入するラッパー。

3. 混同すると起きる2つの罠

この2つの役割を分けず、「1つのプロファイル名にすべてを詰め込んで、aws-vaultでラップして実行する」というアプローチをとると、以下の罠にハマります。

罠①:クロスアカウント時の「板挟み」エラー

Terraformの状態管理ファイル(tfstate)を「管理アカウント(Account A)」のS3に置き、リソースを「ターゲットアカウント(Account B)」に作成する場合を考えます。

  • aws-vault exec app-target -- terraform plan と実行すると、S3(管理側)へのアクセス権がないためエラー(Access Denied)になります。
  • 逆に aws-vault exec mgmt-admin で実行すると、今度はターゲット側にリソースを作成できずエラーになります。

罠②:aws-vaultの「二重起動(無限ループ)」エラー

Terraformのコード(Providerブロック)に profile = "app-target" と書き、さらにコマンドラインで aws-vault exec app-target -- terraform plan を実行した場合。
外側の aws-vault がすでに環境変数をセットしているのに、内側のTerraformが再び設定ファイルを読み込んで aws-vault を呼び出そうとし、環境変数がコンフリクトを起こしてクラッシュ(exit status 1 など)します。

4. 解決策:正しい「棲み分け」と設計パターン

この問題を解決するためのベストプラクティスは、「S3(状態管理)用は外側からaws-vaultでラップし、リソース作成用はTerraformの裏側でaws-vaultを呼び出す」 というハイブリッド構成にすることです。

そして、そのために AWSプロファイルを「鍵の保管用(source)」と「Terraformからの呼び出し用(窓口)」の2つに分割 します。

Step 1: プロファイルの分割(~/.aws/config

ターゲットアカウント(リソース作成先)の設定を、1つの役割に依存させず以下のように親子関係を作ります。

# 1. 鍵の保管用(実体):aws-vaultにはこの名前でアクセスキーを登録し、MFA等もここに集約する
[profile app-target-source]
region = ap-northeast-1
mfa_serial = arn:aws:iam::111122223333:mfa/your-username

# 2. Terraformからの呼び出し用(窓口):Terraformのコードにはこちらを指定する
[profile app-target]
region = ap-northeast-1
# credential_processを使って、裏側で自動的にaws-vaultを呼び出す
# env -u ... をつけることで、外側のaws-vault環境変数を隠蔽し、二重起動エラーを防ぐ
credential_process = env -u AWS_VAULT -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY -u AWS_SESSION_TOKEN -u AWS_SECURITY_TOKEN aws-vault exec app-target-source --json

Step 2: Terraformコードの記述

Terraformコード内では、以下のように記述して役割を明確にします。

  • Backend(S3用): profile の指定は書かない(またはコメントアウト)。外側から注入される aws-vault の権限に任せます。
# backend.tf
terraform {
  backend "s3" {
    bucket = "my-terraform-state-bucket"
    region = "ap-northeast-1"
    # profile = "mgmt-admin" # ← 書かない!環境変数から読み取らせる
  }
}
  • Provider(リソース用): profile = "app-target" と書く。これによりStep1で作った credential_process の窓口が呼ばれます。
# provider.tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "app-target" # ← 窓口プロファイルを指定する
}

Step 3: 実行方法

実行時は、S3(状態管理)へのアクセス権を持つ管理用プロファイルでラップして実行します。

# 1. 事前にターゲット側のMFA認証を済ませてキャッシュしておく(裏側での対話プロンプト失敗を防ぐため)
aws-vault exec app-target-source -- echo "Target Auth OK"

# 2. 管理アカウントの権限でTerraformを起動する
aws-vault exec mgmt-admin -- terraform plan

この手順を踏むことで、「S3への読み書きは mgmt-admin 権限で行い、AWSリソースの操作は裏で自動取得した app-target 権限で行う」という理想的なクロスアカウント実行環境が完成します。

5. まとめ

  • AWSプロファイルは「設定の定義」、aws-vaultは「セキュアな鍵の発行機」
  • クロスアカウント環境では、単に aws-vault exec でラップするだけでは限界が来る。
  • credential_processenv -u を活用し、「aws-vault」と「プロファイル」の親子関係を作ることで、安全かつシームレスな自動認証フローを構築