GitHub Actions で Environmentsを使いマルチアカウントでのCICDコード量を減らす書き方
概要
マルチアカウントのAWS環境に対してS3にファイルアップロードするCICDをGitHubActionsで作成します
記事を書こうと思ったきっかけ
AWSマルチアカウント化の作業を進めているときに工夫しないとCICDのコードが複雑になると思い、この書き方ができることが知られていないと感じたため
この記事でのポイント
- CICDのコードでIf文を使わずに環境毎にデプロイできる設定を行う
-
Environments
,Repository rulesets
機能を使用 - PullRequestの作成, Merge をトリガーにしたCICDの例を紹介
動作環境
-
GitHub
-
Environments
,Repository rulesets
機能を使えるのは パブリックリポジトリ or Team Plan 以上のプライベートリポジトリ
-
-
GitHubActions
-
AWS
-
DevContainers(Github ActionsのVSCode拡張機能など入れてます)
-
- 今回使用するCICDのコードはこのリポジトリにあります。S3などAWSリソースをTerraform で作成するコードも含まれています
今回作成するCICDの構成図
PushをトリガーにしてS3にファイルをアップロードするCICDを作成します。
今回はマルチアカウント構成のためPROD
とSTG
の2つの環境を作成しています
Before
普通に実装するとこのような冗長なコードになります
name: S3 upload file to Multi Account non Environments
on:
push:
branches:
- production
- staging
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: "set variables"
id: variables
run: |
# Pushされたブランチ名を取得してIf文の条件分岐で環境名を定義している
BRANCH_NAME="$(echo ${GITHUB_REF#refs/heads/})"
if [ "$BRANCH_NAME" = "staging" ]; then \
_env="stg"; \
echo "AWS_IAM_ROLE_ARN=${{ secrets.AWS_IAM_ROLE_ARN_STG }}" >> $GITHUB_ENV; \
elif [ "$BRANCH_NAME" = "production" ]; then \
_env="prod"; \
echo "AWS_IAM_ROLE_ARN=${{ secrets.AWS_IAM_ROLE_ARN_PRD }}" >> $GITHUB_ENV; \
else \
exit 1; \
fi
# 環境名をActions内部の環境変数として定義
echo "ENV=${_env}" >> $GITHUB_ENV
- uses: actions/checkout@v4.1.1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ env.AWS_IAM_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Check Bucket Name
run: |
aws s3 ls
- name: Upload file to S3
env:
S3_UPLOAD_BUCKET: ${{ env.ENV }}-${{ secrets.BUCKET_NAME }}
run: |
aws s3 cp upload-test.txt s3://$S3_UPLOAD_BUCKET/
set variables
で定義しているIf文の条件分岐の部分が冗長に感じますね。
マルチアカウントで環境毎に stg-bucket-name
, prod-bucket-name
のような名称のバケットにデプロイします。
またこの部分はAWSの認証を伴うworkflowで繰り返し定義する必要があるので、コードの可読性はどんどん下がります
After
今回紹介するコードがこちらです。先ほどのコードとやっていることは同じですが短く書けていることがわかります
name: S3 upload file to Multi Account
on:
push:
branches: # production, stagingブランチにPush, Mergeされた時のみ実行
- production
- staging
jobs:
build:
runs-on: ubuntu-latest
permissions: # OIDCで使用するTokenの権限
id-token: write
contents: read
environment: ${{ github.ref_name }} # Pushされたブランチ名を取得してenvironmentに設定
steps:
- uses: actions/checkout@v4.1.1
- name: Configure AWS credentials # AWSの認証をOIDCで行う設定
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Check Bucket Name # S3のバケット名を確認
run: |
aws s3 ls
- name: Copy files to S3 # S3にファイルをアップロード
run: |
aws s3 cp upload-test.txt s3://${{vars.AWS_S3_BUCKET}}/
Environments
, Repository rulesets
を使うことでこのように短く書くことができます。
${{ github.ref_name }}
はGithub Actionsに標準で用意されている環境変数で、Pushされたブランチ名を取得できます。
- サンプルリポジトリを見て頂ければ同じコードで別々の環境にデプロイされていることがわかると思います
-
prodcutionへのPullRequest
-
PROD環境へのCICD実行結果
- デプロイされたバケット名
- デプロイされたバケット名
-
PROD環境へのCICD実行結果
-
stagingへのPullRequest
-
STG環境へのCICD実行結果
- デプロイされたバケット名
- デプロイされたバケット名
-
STG環境へのCICD実行結果
-
prodcutionへのPullRequest
Environments
EnvironmentsはGithub の機能で、 パブリックリポジトリ or Team Plan 以上のプライベートリポジトリ で使用できます
Environmentsを使わない場合のAWSの認証情報管理
AWS_IAM_ROLE_ARN_PRD
と AWS_IAM_ROLE_ARN_STG
がGithubのSecretsに保存されています。
なのでCICDでこれらの値を取得するには secrets.AWS_IAM_ROLE_ARN_PRD
,secrets.AWS_IAM_ROLE_ARN_STG
のように別々の名称で取得する必要があるため、どうしてもコード内部に条件分岐が必要になります。
Beforeのコードではブランチ名によってIf文で分岐させて環境のコードを取得していました
Environmentsを使った場合
PROD, STGのどちらの環境も AWS_IAM_ROLE_ARN
という名称で定義されていることがわかると思います。
CICDでこれらの値を取得する際も secrets.AWS_IAM_ROLE_ARN
という名称で取得できるため、コード内部に条件分岐を書かなくても環境毎の設定を取得できます。
ではどの部分で環境を判断しているかというと、environment: ${{ github.ref_name }}
の部分です。
Afterのコードでは environment: ${{ github.ref_name }}
となっていますが、この部分は environment: production
や environment: staging
のように環境名をベタ書きするのが一般的です
ブランチ戦略
の部分で詳細を書きますが、今回のCICDのポイントはenvironmentsの名前とデプロイ先のブランチ名を一致させることです
Environmentsの何がいいのか?
サンプルのようにSecretやVariablesが3個だと実感がわきにくいですが、実際のプロジェクトではSecretやVariablesが20個, 30個と増えていくことが多いです
その時、Environmentsを使わないと secrets.AWS_IAM_ROLE_ARN_PRD
, secrets.AWS_IAM_ROLE_ARN_STG
のような条件分岐で取得する部分も20個, 30個書く必要がでてきます。
そこでEnvironmentsを使えばSecretやVariablesがいくら増えてもCICDの最初に environment: production
や environment: staging
のように一回書くだけで済むのでとても簡潔なコードになります
Repository Rules
Repository RulesもGithubの機能で、 パブリックリポジトリ or Team Plan 以上のプライベートリポジトリ で使用できます。
今回のCICDでの役割は、PullRequestのMerge時にHEADブランチが削除されないように保護する設定や、PROD環境のブランチにPullRequestなしの直接Pushを禁止する設定で使用しています
以前からGithub に存在する Branch Protection Rules
でも同じことが実現できます。Github 的に Repository Rules
は Branch Protection Rules
の進化系らしく Repository Rules
の方が Github Organization
でのルール使い回しや柔軟な制御設定などが可能なようです
今回はこのように production
, staging
ブランチに対して PullRequestのレビュー必須化、ブランチの直Push禁止、ブランチ削除保護設定を有効にしました
ブランチ戦略
- 今回のCICDは基本的にこのフローでしか動作しません
- デフォルトブランチを
production
としています- Environmentsの
production
にはPROD
の環境変数が設定されているためブランチ名も同じ方がわかりやすいため(mainなどでも運用は可能です)
- Environmentsの
- topicブランチ以外はGithub の
Repository Rules
を使用してMerge時にブランチが削除されないように保護を行っております - サンプルコードに
development
ブランチ, AWS環境がないのは予算の都合なので実際はDEV, STG, PRODの3つの環境を作成する想定です
- デフォルトブランチを
おまけ
PullRequest作成時に環境毎のCICDを実行
PullRequestのMerge時だけでなくPullRequest作成時にCICDを実行したい場合はこのように書きます
name: S3 upload file to Multi Account
on:
pull_request:
branches:
- production
- staging
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
environment: ${{ github.base_ref }} # PullRequest作成時のベースブランチ名を取得
steps:
- uses: actions/checkout@v4.1.1
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4.0.2
with:
role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
aws-region: ${{ vars.AWS_REGION }}
- name: Check Bucket Name
run: |
aws s3 ls
- name: Copy files to S3
run: |
aws s3 cp upload-test.txt s3://${{vars.AWS_S3_BUCKET}}/
Merge時トリガーとの違いは ${{ github.base_ref }}
の部分でPullRequestのBaseブランチ名を取得するようになっております。
用途としてはその環境に対してCIによるCheckなどを行いたい場合を想定しています
このようにstagingに対するPullRequest作成時にActionsが実行されています
この部分でstagingのenvironmentsの設定でCICDが成功したことが確認できます
まとめ
-
Environments
,Repository rulesets
を使うことでマルチアカウントのCICDのコードがシンプルになる - ブランチ名と環境名を一致させることで更にコードがシンプルになる
参考資料
- 【GitHub Actions】ブランチ名をもとに環境(Environments)を切り替える
- 【GitHubActions】単一ワークフローファイルでトリガー元に応じてデプロイ先のAWSアカウントを切り替えてみる | DevelopersIO
- GitHub Actionsでブランチ名やコミットSHA等のプロパティーを取得、表示する方法 #GitHub - Qiita
- GitHub Actionsのワークフローをブランチ名を元に環境定義を切り替える方法 #GitHubActions - Qiita
- 小規模開発チームのブランチ戦略。GitHub Flowの導入 - Insight Edge Tech Blog
- Git ブランチの表現も Mermaid がよい感じに表示してくれました
- Check! GitHub Repository rulesetsとは?
- terraform-aws-modules/s3-bucket/aws | Terraform Registry
- TerraformでS3のオブジェクトACLを無効にする | DevelopersIO
- 【React】GitHubActionsでS3へのデプロイを自動化する | Hisuiblog
Discussion