🔑

GitHub Actions でAWS の複数 profile を切り替える

2024/01/30に公開

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 での設定は以下のようなイメージです。

main.tf
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 も簡単に設定できました。

https://github.com/aws-actions/configure-aws-credentials

以下のような Workflow を実行すると、 OIDC ログイン用に発行した IAM Role でログインできていることが確認できます。

workflow.yml
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
result
> 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_* を使って複数のプロファイルを設定しています。

.aws/config
[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 自体のログイン情報の指定が動的になってしまいます。

.aws/config
[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 の設定は以下のようにとてもシンプルになり、動的な要素もなくなります。

.github/workflows/aws_config/config
[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 が実行できました!
コマンドの実行結果を見ると、アカウントやロールも正しく切り替わっているようです。

workflow.yml
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    
result
> 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 を指定するだけで良いみたいです。

https://dev.classmethod.jp/articles/github-actions-oidc-configure-aws-credentials/

以下の Issue では env.AWS_ACCESS_KEY_ID などを動的に吐き出す方法が色々書かれていた。

https://github.com/aws-actions/configure-aws-credentials/issues/112

リーナーテックブログ

Discussion