GitHub Actions でAWS の複数 profile を切り替える
Leaner 開発チームの黒曜(@kokuyouwind)です。
最近 Terraform の整備を始めたのですが、 AWS provider と S3 backend で別の AWS profile を使いたいケースがあり、 GitHub Actions での terraform plan にどう認証情報を渡すか困って調べたところうまくいったので記事に残しておきます。
TL;DR
configure-aws-credentials の OIDC を使うと、アクセスキーを使わず安全に認証できる。
ログインしたロールから他のロールに切り替える場合、 credential_source = Environment
を使うことでソースプロファイルを指定せずに Assume Role できる。
前提: 複数の AWS profile を使いたくなった局面
Leaner ではベストプラクティスに沿って、プロダクトの環境ごとに AWS アカウントを分割しています。
この状況で Terraform を使い始めるにあたり、各プロダクト用の AWS アカウントとは別に用意した AWS アカウントに S3 バケットを作り Terraform State を管理することにしました。
この理由は以下の 2 つです。
- S3 バケットを各 AWS アカウントに配置すると、数が多くなって管理が大変になりそう
- remote state で相互参照する際に、共通のバケットを見るほうが認証情報の管理が楽そう
Terraform での設定は以下のようなイメージです。
terraform {
backend "s3" {
bucket = "leaner-terraform-state"
region = "ap-northeast-1"
key = "develop.tfstate"
encrypt = true
profile = "ops" # 運用アカウントの AWS profile
}
}
provider "aws" {
region = "ap-northeast-1"
profile = "develop" # プロダクト開発アカウントの AWS profile
}
このとき、ローカル開発時は AWS IAM Identity Center によるシングルサインオンで AWS CLI を設定しているため問題ないのですが、 GitHub Actions で terraform plan を実行したい場合困ることに気づきました。
GitHub Actions での AWS 認証
GitHub Actions から AWS にアクセスする必要がある場合、 OIDC を使って認証情報を受け渡すとアクセスキーを環境変数に設定する必要がなく、安全に認証を行えます。
configure-aws-credentials の README にある通り設定するだけで利用でき、 CloudFormation テンプレートを使えば AWS 側の OIDC Provider も簡単に設定できました。
以下のような Workflow を実行すると、 OIDC ログイン用に発行した IAM Role でログインできていることが確認できます。
name: AWS Login
on: push
env:
AWS_ROLE_ARN: arn:aws:iam::xxxxxxxx28:role/GitHubActions-OIDCProvider-Role-xxxxxxxx8gt
permissions:
id-token: write
contents: read
jobs:
aws-login:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- run: aws sts get-caller-identity
> Run aws sts get-caller-identity
{
"UserId": "xxxxxxxxSUL:GitHubActions",
"Account": "xxxxxxxx28",
"Arn": "arn:aws:sts::xxxxxxxx28:assumed-role/GitHubActions-OIDCProvider-Role-xxxxxxxx8gt/GitHubActions"
}
複数の AWS profile 設定における課題
ローカル環境での AWS CLI 設定では、以下のように sso_*
を使って複数のプロファイルを設定しています。
[profile ops]
sso_start_url = https://xxxxx.awsapps.com/start#/
sso_account_id = xxxxx28
sso_role_name = DeveloperAccess
[profile develop]
sso_start_url = https://xxxxx.awsapps.com/start#/
sso_account_id = xxxxx02
sso_role_name = DeveloperAccess
この形だと aws sso
しておくだけで各プロファイルを利用できるのですが、残念ながら GitHub Actions で利用する OIDC は IAM Role としてのログインなので SSO は利用できません。
一方、 Switch Role で通常用いる source_profile
を指定する形では、元になる profile 自体のログイン情報の指定が動的になってしまいます。
[profile default]
# 以下 2 つを動的に生成する必要が出てしまう
aws_access_key_id = ?????
aws_secret_access_key = ?????
[profile ops]
source_profile = default
role_arn = arn:aws:iam::xxxxx28:role/GitHubActions-LeanerTerraform-SwitchRole
[profile develop]
source_profile = default
role_arn = arn:aws:iam::xxxxx02:role/GitHubActions-LeanerTerraform-SwitchRole
認証後に env.AWS_ACCESS_KEY_ID
などを取得すれば可能なのですが、せっかく環境変数に閉じている値をわざわざファイルに書き出すというのはできれば避けたいところです。
credential_source = Environment を利用する
そこで 公式ドキュメント を読み返したところ、 source_profile
の代わりに credential_source
が利用できるという記述を見つけました。
credential_source 属性では、以下の値がサポートされます。
- Environment - 環境変数からソース認証情報の取得。
- Ec2InstanceMetadata - Amazon EC2 インスタンスプロファイルにアタッチされた IAM ロールの使用。
- EcsContainer - Amazon ECS コンテナにアタッチされた IAM ロールの使用。
これを使うと、 AWS CLI の設定は以下のようにとてもシンプルになり、動的な要素もなくなります。
[profile ops]
credential_source = Environment
role_arn = arn:aws:iam::xxxxx28:role/GitHubActions-LeanerTerraform-SwitchRole
[profile develop]
credential_source = Environment
role_arn = arn:aws:iam::xxxxx02:role/GitHubActions-LeanerTerraform-SwitchRole
以下のワークフローを実行すると、 ops
および develop
の各プロファイルを指定して aws sts get-caller-identity
が実行できました!
コマンドの実行結果を見ると、アカウントやロールも正しく切り替わっているようです。
name: AWS Login
on: push
env:
AWS_ROLE_ARN: arn:aws:iam::xxxxxxxx28:role/GitHubActions-OIDCProvider-Role-xxxxxxxx8gt
permissions:
id-token: write
contents: read
jobs:
aws-login:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ env.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- run: aws sts get-caller-identity
- name: Configure AWS Profiles
run: |
mkdir ~/.aws
cp .github/workflows/aws_config/config ~/.aws/config
- run: aws --profile=ops sts get-caller-identity
- run: aws --profile=develop sts get-caller-identity
> Run aws sts get-caller-identity
{
"UserId": "xxxxxxxxSUL:GitHubActions",
"Account": "xxxxxxxx28",
"Arn": "arn:aws:sts::xxxxxxxx28:assumed-role/GitHubActions-OIDCProvider-Role-xxxxxxxx8gt/GitHubActions"
}
> Run aws --profile=ops sts get-caller-identity
{
"UserId": "xxxxxxxxYVH:botocore-session-xxxxxxxx999",
"Account": "xxxxxxxx28",
"Arn": "arn:aws:sts::xxxxxxxx28:assumed-role/GitHubActions-LeanerTerraform-SwitchRole/botocore-session-xxxxxxxx999"
}
> Run aws --profile=lp sts get-caller-identity
{
"UserId": "xxxxxxxxHCD:botocore-session-xxxxxxxx001",
"Account": "xxxxxxxx02",
"Arn": "arn:aws:sts::xxxxxxxx02:assumed-role/GitHubActions-LeanerTerraform-SwitchRole/botocore-session-xxxxxxxx001"
}
Terraform では複数の AWS アカウントを横断して操作したい局面がままあるため、 credential_source = Environment
を利用した方法はかなり便利に使えるはずです。
ぜひ使ってみてください!
参考資料
1 つのアクション中で複数ロールを切り替える必要がない場合は、この記事にある通り role-to-assume
を指定するだけで良いみたいです。
以下の Issue では env.AWS_ACCESS_KEY_ID
などを動的に吐き出す方法が色々書かれていた。
Discussion