ブランチ毎に作成したGitHub Actions workflowを1つにまとめる

2021.10.13

はじめに

GitHub Actions,公式アナウンスされていませんが、AssumeRoleができるようになって積極的に利用しています。今回は、GitHubリポジトリのブランチ毎にAWSアカウントのデプロイ先を変更するworkflowを1つにまとめてみました。

構成図

前回の記事をベースとしています。dev,stg,prd環境毎にAWSアカウントを分離したワークロードでpush先のブランチ毎にworkflowを作成していました。

.github/workflows/dev-deploy.yml
name: cfn deploy

on:
  push:
    branches: 
      - dev

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      id-token: write
      contents: read
    env:
      ENV: dev
      SYSNAME: example
      BILLINGTAG: example
      CIDR: 10.255.0.0/16
      DOMAIN_NAME: dev.example.com
      HOSTED_ZONE_ID: Z10116024XXXXXXXXXXX
      INSTANCE_TYPE: t3.micro
      INSTANCE_CLASS: db.t3.small
      MASTERPASSWORD: ${{ secrets.MASTERPASSWORD }}

    steps:
      - name: setup
        run: |
          brew install rain
          echo "rain -v"
          rain -v

      - name: there's still a race condition for now
        run: sleep 5

      - name: Configure AWS
        run: |
          export AWS_ROLE_ARN=arn:aws:iam::123456789012:role/GithubActionRole
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          export AWS_DEFAULT_REGION=ap-northeast-1

          echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
          echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV
          echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV

          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - uses: actions/checkout@v2
        with:
          ref: dev

      - name: Check the Format of the CloudFormation template.
        run: |
          echo Returns an error if not formatted.
          rain fmt -v ./template/*yml

      - name: Template deploy.
        run: |
          rain deploy -y ./system.yml $ENV-stack --params \
          env=$ENV,\
          sysName=$SYSNAME,\
          billingTag=$BILLINGTAG,\
          vpcCidr=$CIDR,\
          domainName=$DOMAIN_NAME,\
          hostedZoneId=$HOSTED_ZONE_ID,\
          instanceType=$INSTANCE_TYPE,\
          instanceClass=$INSTANCE_CLASS,\
          masterPassword=$MASTERPASSWORD,\
.github/workflows/stg-deploy.yml
name: cfn deploy

on:
  push:
    branches: 
      - stg

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      id-token: write
      contents: read
    env:
      ENV: stg
      SYSNAME: example
      BILLINGTAG: example
      CIDR: 10.254.0.0/16
      DOMAIN_NAME: stg.example.com
      HOSTED_ZONE_ID: Z10116024XXXXXXXXXXX
      INSTANCE_TYPE: t3.micro
      INSTANCE_CLASS: db.t3.small
      MASTERPASSWORD: ${{ secrets.MASTERPASSWORD }}

    steps:
      - name: setup
        run: |
          brew install rain
          echo "rain -v"
          rain -v

      - name: there's still a race condition for now
        run: sleep 5

      - name: Configure AWS
        run: |
          export AWS_ROLE_ARN=arn:aws:iam::123456789012:role/GithubActionRole
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          export AWS_DEFAULT_REGION=ap-northeast-1

          echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
          echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV
          echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV

          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - uses: actions/checkout@v2
        with:
          ref: stg

      - name: Check the Format of the CloudFormation template.
        run: |
          echo Returns an error if not formatted.
          rain fmt -v ./template/*yml

      - name: Template deploy.
        run: |
          rain deploy -y ./system.yml $ENV-stack --params \
          env=$ENV,\
          sysName=$SYSNAME,\
          billingTag=$BILLINGTAG,\
          vpcCidr=$CIDR,\
          domainName=$DOMAIN_NAME,\
          hostedZoneId=$HOSTED_ZONE_ID,\
          instanceType=$INSTANCE_TYPE,\
          instanceClass=$INSTANCE_CLASS,\
          masterPassword=$MASTERPASSWORD,\
.github/workflows/prd-deploy.yml
name: cfn deploy

on:
  push:
    branches: 
      - main

  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      id-token: write
      contents: read
    env:
      ENV: prd
      SYSNAME: example
      BILLINGTAG: example
      CIDR: 10.0.0.0/16
      DOMAIN_NAME: example.com
      HOSTED_ZONE_ID: Z10116024ZZZZZZZZZZ
      INSTANCE_TYPE: m5.large
      INSTANCE_CLASS: db.m5.large
      MASTERPASSWORD: ${{ secrets.MASTERPASSWORD }}

    steps:
      - name: setup
        run: |
          brew install rain
          echo "rain -v"
          rain -v

      - name: there's still a race condition for now
        run: sleep 5

      - name: Configure AWS
        run: |
          export AWS_ROLE_ARN=arn:aws:iam::123456789012:role/GithubActionRole
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          export AWS_DEFAULT_REGION=ap-northeast-1

          echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
          echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV
          echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV

          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - uses: actions/checkout@v2
        with:
          ref: main

      - name: Check the Format of the CloudFormation template.
        run: |
          echo Returns an error if not formatted.
          rain fmt -v ./template/*yml

      - name: Template deploy.
        run: |
          rain deploy -y ./system.yml $ENV-stack --params \
          env=$ENV,\
          sysName=$SYSNAME,\
          billingTag=$BILLINGTAG,\
          vpcCidr=$CIDR,\
          domainName=$DOMAIN_NAME,\
          hostedZoneId=$HOSTED_ZONE_ID,\
          instanceType=$INSTANCE_TYPE,\
          instanceClass=$INSTANCE_CLASS,\
          masterPassword=$MASTERPASSWORD,\

それぞれの差異は、push先ブランチ(dev or stg or prd)、env、AWS_ROLE_ARNが異なりますがそれ以外は重複しています。例えばCloudFormationスタックをデプロイする rain deploy のパラメータの修正があるとすべてのworkflowを修正する必要があります。重複する処理を共通action、push先ブランチで環境変数を変更する1つのworkflowを作成しました。

1つのworkflow

.github/workflows/deploy.yml
name: cfn deploy

on:
  push:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions: 
      id-token: write 
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: IAM Role for dev account
        if: contains(toJSON(github.ref), 'dev')
        run: |
          echo ENV=dev >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.255.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=dev.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMXXXXXXXXXX >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.DEV_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789010:role/githubrole >> $GITHUB_ENV

      - name: env exportIAM Role for stg account
        if: contains(toJSON(github.ref), 'stg')
        run: |
          echo ENV=stg >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.254.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=stg.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMYYYYYYYYYY >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.STG_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789011:role/githubrole >> $GITHUB_ENV

      - name: IAM Role for prd account
        if: contains(toJSON(github.ref), 'main')
        run: |
          echo ENV=prd >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.0.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=stg.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMYYYYYYYYYY >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.PRD_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789012:role/githubrole >> $GITHUB_ENV

      - name: Configure AWS
        run: |
          export AWS_ROLE_ARN=$AWS_ROLE_ARN
          export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds
          export AWS_DEFAULT_REGION=ap-northeast-1

          echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV
          echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV
          echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV

          curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE

      - name: setup common
        run: |
          brew install rain
          echo "rain -v"
          rain -v

      - name: Check the Format of the CloudFormation template
        run: |
          echo Returns an error if not formatted
          rain fmt -v ./template/*yml

      - name: Template deploy.
        run: |
          rain deploy -y ./system.yml $ENV-stack --params \
          env=$ENV,\
          sysName=$SYSNAME,\
          billingTag=$BILLINGTAG,\
          vpcCidr=$CIDR,\
          domainName=$DOMAIN_NAME,\
          hostedZoneId=$HOSTED_ZONE_ID,\
          instanceType=$INSTANCE_TYPE,\
          instanceClass=$INSTANCE_CLASS,\
          masterPassword=$MASTERPASSWORD

変更ポイント1

すべてのpushをトリガーとするように修正しました。これまで特定ブランチで制御していましたが、dev,stg,mainブランチのpushをトリガーとしてGitHub Actionsが起動します。

on:
  push:

変更ポイント2

if: contains(toJSON(github.ref), 'ブランチ名') でpushブランチを特定してactionを実行します。併せて環境毎に異なるenv,AWS_ROLE_ARNを環境変数に設定します。ここで環境変数を設定する意図は、steps内のenvが次のアクションに引き継がれませんが、$GITHUB_ENV 環境変数に格納しておくと他のアクションでも利用できるようになるので設定してます。

      - name: IAM Role for dev account
        if: contains(toJSON(github.ref), 'dev')
        run: |
          echo ENV=dev >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.255.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=dev.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMXXXXXXXXXX >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.DEV_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789010:role/githubrole >> $GITHUB_ENV

      - name: env exportIAM Role for stg account
        if: contains(toJSON(github.ref), 'stg')
        run: |
          echo ENV=stg >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.254.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=stg.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMYYYYYYYYYY >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.STG_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789011:role/githubrole >> $GITHUB_ENV

      - name: IAM Role for prd account
        if: contains(toJSON(github.ref), 'main')
        run: |
          echo ENV=prd >> $GITHUB_ENV
          echo SYSNAME=example >> $GITHUB_ENV
          echo BILLINGTAG=example >> $GITHUB_ENV
          echo CIDR=10.0.0.0/16 >> $GITHUB_ENV
          echo DOMAIN_NAME=stg.example.com >> $GITHUB_ENV
          echo HOSTED_ZONE_ID=Z01774222TMYYYYYYYYYY >> $GITHUB_ENV
          echo INSTANCE_TYPE=t3.micro >> $GITHUB_ENV
          echo INSTANCEINSTANCE_CLASS_TYPE=db.t3.small >> $GITHUB_ENV
          echo MASTERPASSWORD=${{ secrets.PRD_MASTERPASSWORD }} >> $GITHUB_ENV
          echo AWS_ROLE_ARN=arn:aws:iam::123456789012:role/githubrole >> $GITHUB_ENV

変更ポイント3

name: Configure AWSname: Check the Format of the CloudFormation templatename: Template deploy.は共通アクションです。環境変数AWS_ROLE_ARNからIAMロールのARNを参照してexportするように修正しています。rain deployの--paramsも同様に環境変数から呼び出しています。

まとめ

1つのworkflowで重複する処理とブランチ毎のデプロイ先を変更することができました。環境毎(例えば本番だけ)に処理が異なる場合は無理してworkflowを統合する必要は無いと考えています。その他、様々な実装方法があるのでドキュメント見ながら試してみてください。

参考にしたサイト

GitHub Actionsのワークフロー構文 環境変数