はじめに
CDKアプリケーションの開発を行っていたプロジェクトにて、GitHub Actionsによりデプロイ自動化の仕組みを構築しました。
その際、エンタープライズでのチーム開発に最低限必要そうな機能を含めたのですが、その辺りがひと通りまとまっている記事があまり見当たらなかったため、簡単にまとめてみました。
-
想定読者
- チーム開発向けにデプロイ自動化の仕組みを構築したい。
- GitHub Actionsでワークフローを組んだことがある。
- AWSのCDK、CloudFormation(以降CFNと記載)、IAMなどについて基礎的な知識がある。
-
主な技術要素
この記事のポイント
-
デプロイ自動化の実現に至るための要点に絞っています。(基礎的な説明は省略してます)
-
ブランチ毎に異なるAWS環境(アカウント)へデプロイします。
-
OICDプロバイダーを使用し、GitHub ActionsからAWSへのセキュアで効率的なアクセスを行います。
前提事項
構築時の環境情報は下記のとおりです。
OS・MW | Version |
---|---|
Ubuntu | 20.04 LTS |
Node.js | 16.14.2 |
AWS CDK | 2.17.0 |
AWS CLI | 2.4.25 |
この記事でデプロイしているCDKアプリケーションは、cdk init
コマンドを実行しただけの簡単なサンプルアプリケーションです。
# サンプルアプリケーション作成時のコマンド例
$ mkdir sample-cdk-app
$ cd sample-cdk-app/
$ cdk init sample-app --language=typescript
デプロイ先のAWS環境にはcdk bootstrap
コマンドが実行済みで、CDKを扱うために必要なCDKToolkit
スタックが作成済みであるものとします。
$ npm install -g aws-cdk
$ cdk bootstrap
本題
AWSへのOIDCプロバイダーとIAMロールの作成
デプロイ先のAWS環境へ、OIDCプロバイダーとIAMロール(GitHub Actionsが使用)を作成します。OIDCプロバイダーによる認証を採用することで、下記の様なメリットがあります。
- GitHub ActionsがAWSへアクセスする際の専用IAMユーザーの作成が不要。
- AWSのクレデンシャル情報(アクセスキーやシークレット)をGitHub側に持たせる必要がない。
- 上記により、シークレットのローテーションといった運用の考慮が必要ない。
ここでいうAWS側のOIDCプロバイダーは、厳密には「IDプロバイダーを信頼するための設定」です。実際にOpenID Connect認証用のIDトークンを発行するプロバイダは、GitHub側に存在します。そのあたりの説明は、下記の記事が分かりやすく、参考にさせて頂きました。
下記CFNテンプレートにより、前述のリソースを含むスタックを作成できます。
Parameters:
RepoOwner:
Description: Owner of repository.
Type: String
RepoName:
Description: Name of repository where the GitHub Actions will be running.
Type: String
OIDCProviderArn: # 2回目以降に作成済みOIDC ProviderのARNを指定します
Description: Arn for the GitHub OIDC Provider.
Default: ''
Type: String
Conditions:
CreateOIDCProvider: !Equals
- !Ref OIDCProviderArn
- ''
Resources:
Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${RepoName}-actions-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Action: sts:AssumeRoleWithWebIdentity
Principal:
Federated: !If
- CreateOIDCProvider
- !Ref GithubOidc
- !Ref OIDCProviderArn
Condition:
StringLike:
token.actions.githubusercontent.com:sub: !Sub repo:${RepoOwner}/${RepoName}:*
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess # 権限の限定を推奨
GithubOidc:
Type: AWS::IAM::OIDCProvider
Condition: CreateOIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 6938fd4d98bab03faadb97b34396831e3780aea1 # サムプリントの更新箇所
Outputs:
Role:
Value: !GetAtt Role.Arn
OIDC Provider
はAWSアカウントにひとつのみしか存在できません。そのため、このCFNテンプレートで別スタックを作成する場合は、下記Parameters
に作成済みOIDC Provider
のArn
を渡してください。
Parameters:
OIDCProviderArn: # 2回目以降に作成済みOIDC ProviderのARNを指定します
下記のポリシーを指定している箇所は、デプロイするCDKアプリケーションに応じて、必要最低限の権限とすることを推奨します。今回はサンプルのため、強い権限を与えています。
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess # 権限の限定を推奨
また、GitHubのIDプロバイダーでサムプリントが更新されていた場合、最新のものをCFNテンプレートに反映する必要があります。スタック作成前に、後述の更新手順を実施しておくのが確実と思います。
GithubOidc:
Type: AWS::IAM::OIDCProvider
Condition: CreateOIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 6938fd4d98bab03faadb97b34396831e3780aea1 # サムプリントの更新箇所
下記がAWSの公式ガイドで説明されているサムプリント取得方法です。
公式ガイドに記載の手順は少々複雑であるため、私はIAMコンソールを使用する下記の方法で取得しました。(公式ではガイドされていない方法のため、こちらは自己責任で実施されてください)
- 「プロバイダのタイプ」で
OpenID Connect
を選択します。
- 「プロバイダのURL」に
https://token.actions.githubusercontent.com
を指定することで取得できます。
下記のようなAWS CLIコマンドで、前述のCFNテンプレートでスタックを作成します。
aws cloudformation create-stack \
--stack-name actions-base \
--template-body file://template.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=RepoOwner,ParameterValue=<Owner Name> ParameterKey=RepoName,ParameterValue=sample-cdk-app
# 名前指定のIAMロール作成があるため、`CAPABILITY_NAMED_IAM`オプションの指定が必要。
GitHubのSecretsへAWSアカウント情報を格納
GitHub Actionsがデプロイ先を判断するために必要なAWSアカウント情報(アカウントIDとリージョン)を、リポジトリのSecretsへ格納します。これは後述のワークフロー定義内で作り込みました仕様によるもので、Secretsへ格納することでアカウント情報が漏出するリスクを低減することが目的です。
- 開発環境(dev)用のSecret名
AWS_ACCOUNT_DEV
AWS_REGION_DEV
- 本番環境(prd)用のSecret名
AWS_ACCOUNT_PRD
AWS_REGION_PRD
GitHubリポジトリのコンソールSettings > Secrets > Actions
から追加できます。
GitHub Actionsワークフロー定義の配置
デプロイ対象のCDKアプリケーションに、.github/workflows/
というディレクトリを作成し、ワークフロー定義ファイル(yaml)を配置します。ファイル名は任意です。
今回作成したワークフローの仕様はざっくり下記の通りです。
-
merge先/push先ブランチによってデプロイ先のAWS環境を切り替えます。
-
develop
ブランチ → 開発環境(dev) -
main
ブランチ → 本番環境(prd)
-
-
プルリクエストの作成時、または、プルリクエストの更新時
-
cdk diff
を実行します。ワークフローの実行結果から、デプロイ済みスタックとの差分が確認できます。
-
-
プルリクエストのmerge時、または、ブランチへの直接push時
-
cdk diff
とcdk deploy
を実行します。
-
name: CDK Deploy
on:
push:
branches:
- develop
- main
pull_request:
branches:
- develop
- main
permissions:
id-token: write
contents: read
jobs:
cdk-deploy:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16
registry-url: https://npm.pkg.github.com/
- name: Install AWS CDK
run: npm ci
- name: Destination settings for dev enviroment
if: (github.ref == 'refs/heads/develop') || (github.event.pull_request.base.ref == 'develop')
run: |
echo "Using dev environment settings."
echo "AWS_ACCOUNT=${{secrets.AWS_ACCOUNT_DEV}}" >> $GITHUB_ENV
echo "AWS_REGION=${{secrets.AWS_REGION_DEV}}" >> $GITHUB_ENV
- name: Destination settings for prd enviroment
if: (github.ref == 'refs/heads/main') || (github.event.pull_request.base.ref == 'main')
run: |
echo "Using prd environment settings."
echo "AWS_ACCOUNT=${{secrets.AWS_ACCOUNT_PRD}}" >> $GITHUB_ENV
echo "AWS_REGION=${{secrets.AWS_REGION_PRD}}" >> $GITHUB_ENV
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::${{env.AWS_ACCOUNT}}:role/${{github.event.repository.name}}-actions-role
aws-region: ${{env.AWS_REGION}}
- name: CDK Diff
if: contains(github.event_name, 'pull_request') || contains(github.event_name, 'push')
run: npm run cdk -- diff
- name: CDK Deploy
if: contains(github.event_name, 'push')
run: npm run cdk -- deploy --require-approval never
ワークフロー定義は今回のキモとなる部分のため、工夫点など含め少し詳しく説明していきます。
下記ステップは、package.json
に記載のaws-cdk
などをインストールすることが目的ですが、npm ci
とすることで、package-lock.json
の構成管理を強制しています。npm install
を実行してしまうと、依存パッケージのバージョンが安定しないため、意図せぬ変更を防いでいます。
- name: Install AWS CDK
run: npm ci
下記ステップで、どのブランチへのイベントであるかによってデプロイ先を判断しています。リダイレクトの記述>> $GITHUB_ENV
によって、ステップを跨る環境変数の引継ぎができます。
- name: Destination settings for dev enviroment
if: (github.ref == 'refs/heads/develop') || (github.event.pull_request.base.ref == 'develop')
run: |
echo "Using dev environment settings."
echo "AWS_ACCOUNT=${{secrets.AWS_ACCOUNT_DEV}}" >> $GITHUB_ENV
echo "AWS_REGION=${{secrets.AWS_REGION_DEV}}" >> $GITHUB_ENV
- name: Destination settings for prd enviroment
if: (github.ref == 'refs/heads/main') || (github.event.pull_request.base.ref == 'main')
run: |
echo "Using prd environment settings."
echo "AWS_ACCOUNT=${{secrets.AWS_ACCOUNT_PRD}}" >> $GITHUB_ENV
echo "AWS_REGION=${{secrets.AWS_REGION_PRD}}" >> $GITHUB_ENV
[参考] Workflow commands for GitHub Actions > Setting an environment variable
下記ステップで、aws-actions/configure-aws-credentials
を使用し、AWS環境へ作成したOIDCプロバイダーとIAMロールを使用したクレデンシャルの取得を実施しています。冒頭のpermissions
で指定しているid-token: write
もセットで必要です。
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::${{env.AWS_ACCOUNT}}:role/${{github.event.repository.name}}-actions-role
aws-region: ${{env.AWS_REGION}}
permissions:
id-token: write
contents: read
[参考] aws-actions/configure-aws-credentials: "Configure AWS Credentials" Action For GitHub Actions
下記ステップで、プルリクエスト作成かmerge(or 直接push)かによって、実行するCDKコマンドを切り替えています。実行コマンドはnpm run cdk -- diff
というようにすることで、package.json
で依存しているCDKを使用するようにしています。ワークフロー定義内でCDKのインストールを記述してしまうと、CDKのバージョン指定箇所が分散してしまうため、このようにしています。
- name: CDK Diff
if: contains(github.event_name, 'pull_request') || contains(github.event_name, 'push')
run: npm run cdk -- diff
- name: CDK Deploy
if: contains(github.event_name, 'push')
run: npm run cdk -- deploy --require-approval never
"scripts": {
"cdk": "cdk"
},
"devDependencies": {
"aws-cdk": "2.17.0",
このワークフロー定義を格納した状態でGitHubへpushしますと、さっそくGitHub Actionsが動きだします。GitHub Actionsのコンソールから下記状況が確認できれば、ワークフローが正しく動いています。
※GitHub Event Context Info
というステップは、前述のワークフロー定義からは削除しているため無視してください。
デプロイ先AWS環境のCFNコンソールからも、スタック作成に成功していることが確認できると思います。(この記事通りの場合、SampleCdkAppStack
というスタック名)
改善したいポイント
-
本番環境向けデプロイへの承認プロセスの実装
- 実際のプロダクトでは上長への申請→承認といったプロセスを経てデプロイ、が多いのではと思います。そのあたりもGitHubに存在する機能の中で実現できるといいなぁと思ってます。
-
環境差異の切り替え → Environmentsの使用
- Environmentsという機能を使うと、環境に応じた切り替えをもっとスマートに実現できそうです。ただし、Free版ではパブリックリポジトリでしか使えず、プライベートリポジトリで使う場合はEnterprise版の契約が必要なようです。(Free版プライベートリポジトリユーザーであるため、手が出せませんでした・・・)
-
GitHub Actionsコンソール上での
cdk diff
の結果確認- 正直かなり見づらく実用的でないです。ローカル(VSCode)での見え方を再現できる方法があればいいのですが・・・
-
OIDCプロバイダーとIAMロールを作成するCFNテンプレートのCDK化
- 実害はない&あまり変更が入らない定義と思われますが、CDKに揃えたい気持ちがちょっとあります。
まとめ
エンタープライズやチーム開発での自動化の手始めとして、参考にして頂けましたら幸いです。最後まで読んでいただきありがとうございました。