SAMパイプラインでCI/CDパイプラインを構築
はじめに
最近マイクロサービス化したタスクの実装でAWS SAMを用いて構築しています。
実装優先で進めていたため、手動デプロイで凌いできましたが、
ある程度実装が落ち着いてきたこともあり、CI/CD周りを実装しようと思ったところ、
SAMにCI/CDパイプラインを構築できるツールが組み込まれていることを知り、
使い勝手を試してみようと思います。
SAMのアプリケーションは以前ブログで書いたものを使用します。
SAMパイプラインでできること
- SAMパイプラインを用いることで開発環境と本番環境それぞれにデプロイを分ける2ステージデプロイのパイプラインが簡単に作成できます。
- デプロイに必要なロールやリソースなどは対話形式で指定するか自動で作成してくれるため権限不足によるデプロイ失敗の手間を減らせます。
一言で言うと、上記のようなデプロイの準備作業とCI/CDを行うための定義ファイルを対話式に作成できるツールです。
SAMパイプラインが対応しているCI/CDサービス
- AWS CodePipeline
- Jenkins
- GitLab CI/CD
- GitHub Actions
- Bitbucket Pipelines
例えば、AWS CodePipelineであれば、CodePipeline や EventBridgeルールなどのSAMテンプレートファイルが作成されます。
GitLabであれば.gitlab-ci.yml
、
GitHubであれば.github/workflows
にpipeline.yaml
が作成されます。
※今回試してみるまでは、SAMパイプライン自体がパイプラインを生成から実行まですべてを行ってくれるものかと勘違いしておりましたが、実際のデプロイは各種CI/CDサービスで実行されます。
そのため、すでにSAMやCloudformationsで構築したデプロイ環境がある場合はあえてSAMパイプラインを用いずに流用するのも良いと思います。
CI/CDパイプライン構築用のSAMコマンド
以下2つのコマンドが提供されています。
-
sam pipeline bootstrap
CI/CDを実行するために必要なAWSリソースを作成 -
sam pipeline init
CI/CDパイプラインの構成ファイルを作成
sam pipeline --help
Usage: sam pipeline [OPTIONS] COMMAND [ARGS]...
Manage the continuous delivery of the application
Options:
-h, --help Show this message and exit.
Commands:
bootstrap Generates the required AWS resources to connect your CI/CD system.
init Generates a CI/CD pipeline configuration file.
bootstrap
コマンドでCI/CDに必要なリソースを作成
sam pipeline bootstrap
で開始します。
sam pipeline bootstrap
sam pipeline bootstrap generates the required AWS infrastructure resources to connect
to your CI/CD system. This step must be run for each deployment stage in your pipeline,
prior to running the sam pipeline init command.
We will ask for [1] stage definition, [2] account details, and
[3] references to existing resources in order to bootstrap these pipeline resources.
大きく3つのセクションで構成されているようです。
- ステージ定義(開発、検証、本番などデプロイする単位をステージと称しています。)
- アカウント詳細(この
- パイプラインに必要なリソースの指定や作成
[1] Stage definition
Enter a configuration name for this stage. This will be referenced later when you use the sam pipeline init command:
Stage configuration name: dev
ステージの名称を設定します。pipeline init
の際に指定することになります。
どの環境へデプロイするか、等でわかりやすい名前にするのが良いと思います。
ここでは、開発用としてdev
とします。
[2] Account details
The following AWS credential sources are available to use.
To know more about configuration AWS credentials, visit the link below:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html
1 - Environment variables (not available)
2 - default (named profile)
〜 中略 〜
9 - sdtoki (named profile)
10 - sam (named profile)
q - Quit and configure AWS credentials
Select a credential source to associate with this stage: 10
Associated account {AWSアカウントID} with configuration dev.
認証情報を指定します。ローカルのprofile情報から読み取っていると思われます。
この後作成するリソース作成が可能な権限を持っている必要があります。
適切なプロファイルを指定します。
Enter the region in which you want these resources to be created [ap-northeast-1]:
リージョンを指定します。デフォルトで。
Select a user permissions provider:
1 - IAM (default)
2 - OpenID Connect (OIDC)
Choice (1, 2): 2
認証方法を選択します。
IAMかOIDCが選べます。
OIDCにします。
Select an OIDC provider:
1 - GitHub Actions
2 - GitLab
3 - Bitbucket
Choice (1, 2, 3): 1
OIDCのプロバイダーを選択します。
今回はGitHub Actionsにしたいので 1 を選択
Enter the URL of the OIDC provider [https://token.actions.githubusercontent.com]:
OIDCプロバイダーのURLを指定します。
デフォルト値がすでに設定されているので、そのままエンターで良いと思います。
Enter the OIDC client ID (sometimes called audience) [sts.amazonaws.com]:
OIDCクライアントIDの指定です。
こちらもデフォルト値で。
Enter the GitHub organization that the code repository belongs to. If there is no organization enter your username instead: KaT0819
Githubの組織名かユーザ名を指定します。
Enter GitHub repository name: aws-step-function-sample
リポジトリ名を指定します。
Enter the name of the branch that deployments will occur from [main]: develop
このデプロイが実行される対象となるブランチを指定します。
[3] Reference application build resources
Enter the pipeline execution role ARN if you have previously created one, or we will create one for you []:
Enter the CloudFormation execution role ARN if you have previously created one, or we will create one for you []:
Please enter the artifact bucket ARN for your Lambda function. If you do not have a bucket, we will create one for you []:
Does your application contain any IMAGE type Lambda functions? [y/N]:
デプロイする際に必要となるリソースを作成していきます。
指定したい場合は入力します。何も入力せずエンターで自動作成してくれます。
- パイプラインを実行するためのロールのARN
- パイプライン実行時のCloudFormationの実行ロールのARN
- Lambda関数のためのバケットのARN
[4] Summary
Below is the summary of the answers:
1 - Account: {AWSアカウントID}
2 - Stage configuration name: dev
3 - Region: ap-northeast-1
4 - OIDC identity provider URL: https://token.actions.githubusercontent.com
5 - OIDC client ID: sts.amazonaws.com
6 - GitHub organization: KaT0819
7 - GitHub repository: aws-step-function-sample
8 - Deployment branch: develop
9 - Pipeline execution role: [to be created]
10 - CloudFormation execution role: [to be created]
11 - Artifacts bucket: [to be created]
12 - ECR image repository: [skipped]
Press enter to confirm the values above, or select an item to edit the value:
入力した内容の確認です。
問題なければエンター、修正したい箇所があれば修正したい項目の数値を入れてエンター
This will create the following required resources for the 'dev' configuration:
- IAM OIDC Identity Provider
- Pipeline execution role
- CloudFormation execution role
- Artifact bucket
Should we proceed with the creation? [y/N]: y
Creating the required resources...
最終確認です。ここで最後の実行確認です。
y
で実行 n
でキャンセル可能です。
デフォルトはキャンセルなので、まちがってエンター押してしまうと最初からやり直しになります。。
実行開始するとCloudformationが実行され、以下のようなリソースが作成されています。
SAMパイプラインは2ステージデプロイのため、同様の手順でもう一つステージを作成します。
今度のステージはprd
とします。
他は一緒なので内容は割愛します。
init
コマンドでCI/CDパイプライン構成ファイルを生成
sam pipeline init
で開始します。
sam pipeline init
sam pipeline init generates a pipeline configuration file that your CI/CD system
can use to deploy serverless applications using AWS SAM.
We will guide you through the process to bootstrap resources for each stage,
then walk through the details necessary for creating the pipeline config file.
Please ensure you are in the root folder of your SAM application before you begin.
Select a pipeline template to get started:
1 - AWS Quick Start Pipeline Templates
2 - Custom Pipeline Template Location
Choice: 1
パイプラインのテンプレートを選択します。
1を選択します。
Cloning from https://github.com/aws/aws-sam-cli-pipeline-init-templates.git (process may take a moment)
Select CI/CD system
1 - Jenkins
2 - GitLab CI/CD
3 - GitHub Actions
4 - Bitbucket Pipelines
5 - AWS CodePipeline
Choice: 3
CI/CDのサービスを選択します。
Githubなので3を選択します。
You are using the 2-stage pipeline template.
_________ _________
| | | |
| Stage 1 |->| Stage 2 |
|_________| |_________|
Checking for existing stages...
2 stage(s) were detected, matching the template requirements. If these are incorrect, delete .aws-sam/pipeline/pipelineconfig.toml and rerun
This template configures a pipeline that deploys a serverless application to a testing and a production stage.
2ステージデプロイのパイプラインを構築していきます。
先ほどbootstrapコマンドで作成したステージの存在を確認し、この先の設定に進みます。
1つしかステージを作成していない場合はここでエラーが発生します。
What is the git branch used for production deployments? [main]:
本番環境のブランチを指定します。mainなのでデフォルトで。
What is the template file path? [template.yaml]:
SAMテンプレートを指定します。こちらもデフォルトで。
We use the stage configuration name to automatically retrieve the bootstrapped resources created when you ran `sam pipeline bootstrap`.
Here are the stage configuration names detected in .aws-sam/pipeline/pipelineconfig.toml:
1 - dev
2 - prd
Select an index or enter the stage 1's configuration name (as provided during the bootstrapping): 1
1つ目のステージを選択します。最初は開発用にしたいので、1を選択
What is the sam application stack name for stage 1? [sam-app]: sam-step-function-sample-dev
SAMデプロイ時のスタック名を指定します。ステージごとに名前を分けておくのが良さそう。
Stage 1 configured successfully, configuring stage 2.
Here are the stage configuration names detected in .aws-sam/pipeline/pipelineconfig.toml:
1 - dev
2 - prd
Select an index or enter the stage 2's configuration name (as provided during the bootstrapping): 2
次に2つ目のステージを選択します。本番用なので、2を選択
What is the sam application stack name for stage 2? [sam-app]: sam-step-function-sample-prd
Stage 2 configured successfully.
SAMデプロイ時のスタック名を指定します。
SUMMARY
We will generate a pipeline config file based on the following information:
Select a user permissions provider.: OpenID Connect (OIDC)
What is the git branch used for production deployments?: main
What is the template file path?: template.yaml
Select an index or enter the stage 1's configuration name (as provided during the bootstrapping): 1
What is the sam application stack name for stage 1?: sam-step-function-sample-dev
What is the pipeline execution role ARN for stage 1?: arn:aws:iam::〜〜
What is the CloudFormation execution role ARN for stage 1?: arn:aws:iam::〜〜
What is the S3 bucket name for artifacts for stage 1?: aws-sam-cli-managed-dev-pipeline-r-artifactsbucket-dwojsqvhcloc
What is the ECR repository URI for stage 1?:
What is the AWS region for stage 1?: ap-northeast-1
Select an index or enter the stage 2's configuration name (as provided during the bootstrapping): 2
What is the sam application stack name for stage 2?: sam-step-function-sample-prd
What is the pipeline execution role ARN for stage 2?: arn:aws:iam::〜〜
What is the CloudFormation execution role ARN for stage 2?: arn:aws:iam::〜〜
What is the S3 bucket name for artifacts for stage 2?: aws-sam-cli-managed-prd-pipeline-r-artifactsbucket-vw7fgewbnmwl
What is the ECR repository URI for stage 2?:
What is the AWS region for stage 2?: ap-northeast-1
Successfully created the pipeline configuration file(s):
- .github/workflows/pipeline.yaml
サマリが表示され、パイプラインのファイルが作成されます。
ここでは最終確認はなく作成まで進みます。
生成されたファイルの確認
ディレクトリ構成
├── .aws-sam
│ ├── build
│ ├── build.toml
│ └── pipeline
│ └── pipelineconfig.toml
├── .github
│ └── workflows
│ └── pipeline.yaml
├── samconfig.toml
└── template.yaml
関連するファイルのみ抜粋
※samconfig.toml はこの後消えることになりますので不要です。
.aws-sam/pipeline/pipelineconfig.toml
bootstrap
コマンド実行時に作成されます。
作成したARNなどが保持されています。
version = 0.1
[default.pipeline_bootstrap.parameters]
oidc_provider_url = "https://token.actions.githubusercontent.com"
oidc_client_id = "sts.amazonaws.com"
github_org = "KaT0819"
github_repo = "aws-step-function-sample"
deployment_branch = "develop"
oidc_provider = "github-actions"
permissions_provider = "OpenID Connect (OIDC)"
[dev.pipeline_bootstrap.parameters]
pipeline_execution_role = "arn:aws:iam::〜〜"
cloudformation_execution_role = "arn:aws:iam::〜〜"
artifacts_bucket = "aws-sam-cli-managed-dev-pipeline-r-artifactsbucket-dwojsqvhcloc"
image_repository = ""
region = "ap-northeast-1"
.github/workflows/pipeline.yaml
init
コマンドで作成されたファイルです。
pipeline.toml
name: Pipeline
on:
push:
branches:
- 'main'
- 'feature**'
delete:
branches:
- 'feature**'
env:
SAM_TEMPLATE: template.yaml
TESTING_STACK_NAME: sam-step-function-sample-dev
TESTING_PIPELINE_EXECUTION_ROLE: arn:aws:iam::〜〜
TESTING_CLOUDFORMATION_EXECUTION_ROLE: arn:aws:iam::〜〜
TESTING_ARTIFACTS_BUCKET: aws-sam-cli-managed-dev-pipeline-r-artifactsbucket-dwojsqvhcloc
# If there are functions with "Image" PackageType in your template,
# uncomment the line below and add "--image-repository ${TESTING_IMAGE_REPOSITORY}" to
# testing "sam package" and "sam deploy" commands.
# TESTING_IMAGE_REPOSITORY = '0123456789.dkr.ecr.region.amazonaws.com/repository-name'
TESTING_REGION: ap-northeast-1
PROD_STACK_NAME: sam-step-function-sample-prd
PROD_PIPELINE_EXECUTION_ROLE: arn:aws:iam::〜〜
PROD_CLOUDFORMATION_EXECUTION_ROLE: arn:aws:iam::〜〜
PROD_ARTIFACTS_BUCKET: aws-sam-cli-managed-prd-pipeline-r-artifactsbucket-vw7fgewbnmwl
# If there are functions with "Image" PackageType in your template,
# uncomment the line below and add "--image-repository ${PROD_IMAGE_REPOSITORY}" to
# prod "sam package" and "sam deploy" commands.
# PROD_IMAGE_REPOSITORY = '0123456789.dkr.ecr.region.amazonaws.com/repository-name'
PROD_REGION: ap-northeast-1
permissions:
id-token: write
contents: read
jobs:
test:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
# trigger the tests here
delete-feature:
if: startsWith(github.event.ref, 'feature') && github.event_name == 'delete'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.TESTING_REGION }}
role-to-assume: ${{ env.TESTING_PIPELINE_EXECUTION_ROLE }}
role-session-name: testing-packaging
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Delete feature branch stack
env:
FEATURE_BRANCH_NAME: ${{ github.event.ref }}
run: |
sam delete \
--stack-name $(echo ${FEATURE_BRANCH_NAME##*/} | tr -cd '[a-zA-Z0-9-]') \
--region ${TESTING_REGION} \
--no-prompts
build-and-deploy-feature:
# this stage is triggered only for feature branches (feature*),
# which will build the stack and deploy to a stack named with branch name.
# https://github.com/actions/setup-python
# https://github.com/aws-actions/configure-aws-credentials#notice-node12-deprecation-warning
if: startsWith(github.ref, 'refs/heads/feature')
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
- run: sam build --template ${SAM_TEMPLATE} --use-container
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.TESTING_REGION }}
role-to-assume: ${{ env.TESTING_PIPELINE_EXECUTION_ROLE }}
role-session-name: feature-deployment
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Deploy to feature stack in the testing account
shell: bash
run: |
sam deploy --stack-name $(echo ${GITHUB_REF##*/} | tr -cd '[a-zA-Z0-9-]') \
--capabilities CAPABILITY_IAM \
--region ${TESTING_REGION} \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--no-fail-on-empty-changeset \
--role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}
build-and-package:
if: github.ref == 'refs/heads/main'
needs: [test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
- name: Build resources
run: sam build --template ${SAM_TEMPLATE} --use-container
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.TESTING_REGION }}
role-to-assume: ${{ env.TESTING_PIPELINE_EXECUTION_ROLE }}
role-session-name: testing-packaging
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Upload artifacts to testing artifact buckets
run: |
sam package \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--region ${TESTING_REGION} \
--output-template-file packaged-testing.yaml
- uses: actions/upload-artifact@v3
with:
name: packaged-testing.yaml
path: packaged-testing.yaml
- name: Assume the prod pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.PROD_REGION }}
role-to-assume: ${{ env.PROD_PIPELINE_EXECUTION_ROLE }}
role-session-name: prod-packaging
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Upload artifacts to production artifact buckets
run: |
sam package \
--s3-bucket ${PROD_ARTIFACTS_BUCKET} \
--region ${PROD_REGION} \
--output-template-file packaged-prod.yaml
- uses: actions/upload-artifact@v3
with:
name: packaged-prod.yaml
path: packaged-prod.yaml
deploy-testing:
if: github.ref == 'refs/heads/main'
needs: [build-and-package]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
- uses: actions/download-artifact@v3
with:
name: packaged-testing.yaml
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.TESTING_REGION }}
role-to-assume: ${{ env.TESTING_PIPELINE_EXECUTION_ROLE }}
role-session-name: testing-deployment
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Deploy to testing account
run: |
sam deploy --stack-name ${TESTING_STACK_NAME} \
--template packaged-testing.yaml \
--capabilities CAPABILITY_IAM \
--region ${TESTING_REGION} \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--no-fail-on-empty-changeset \
--role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}
integration-test:
if: github.ref == 'refs/heads/main'
needs: [deploy-testing]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: |
# trigger the integration tests here
deploy-prod:
if: github.ref == 'refs/heads/main'
needs: [integration-test]
runs-on: ubuntu-latest
# Configure GitHub Action Environment to have a manual approval step before deployment to production
# https://docs.github.com/en/actions/reference/environments
# environment: <configured-environment>
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
with:
use-installer: true
- uses: actions/download-artifact@v3
with:
name: packaged-prod.yaml
- name: Assume the prod pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.PROD_REGION }}
role-to-assume: ${{ env.PROD_PIPELINE_EXECUTION_ROLE }}
role-session-name: prod-deployment
role-duration-seconds: 3600
role-skip-session-tagging: true
- name: Deploy to production account
run: |
sam deploy --stack-name ${PROD_STACK_NAME} \
--template packaged-prod.yaml \
--capabilities CAPABILITY_IAM \
--region ${PROD_REGION} \
--s3-bucket ${PROD_ARTIFACTS_BUCKET} \
--no-fail-on-empty-changeset \
--role-arn ${PROD_CLOUDFORMATION_EXECUTION_ROLE}
作成されたpipeline.yaml
内容をざっくり確認します。
トリガー対象
-
main
ブランチのプッシュ -
feature
で始まるブランチのプッシュ -
feature
で始まるブランチの削除
develop
ブランチを指定したつもりでしたが、なぜかfeature**
になってしまっていました。
テスト用と本番用で環境変数がそれぞれ定義されており、
ステージ1がテスト用、ステージ2が本番用となっています。
定義されているジョブの確認
便宜上、feature**
をテスト用ブランチ、main
を本番用ブランチと表現します。
-
test
: テスト用、本番用ブランチにプッシュされた場合にテストを実行します。 -
delete-feature
: テスト用ブランチが削除された場合に、関連するスタックを削除します。sam delete
が実行されます。 -
build-and-deploy-feature
:
テスト用ブランチがプッシュされ、test
ジョブが完了した場合に実行されます。
ビルドしてデプロイします。
-
sam build
が実行されます。 -
sam deploy
が実行されます。
-
build-and-package
:
本番用ブランチがプッシュされ、test
ジョブが完了した場合に実行されます。
-
sam build
が実行されます。 - テスト用のバケットにビルドしたファイルをアップロードします。
- 本番用のバケットにビルドしたファイルをアップロードします。
-
deploy-testing
:
本番用ブランチがプッシュされ、build-and-package
ジョブが完了した場合に実行されます。
テスト環境にデプロイします。 -
integration-test
:
本番用ブランチがプッシュされ、deploy-testing
ジョブが完了した場合に実行されます。
本番デプロイ前のテストを実行します。 -
deploy-prod
:
本番用ブランチがプッシュされ、integration-test
ジョブが完了した場合に実行されます。
本番環境にデプロイします。
ARNなどをGitHubシークレット化
yaml内で環境変数として定義されていますが、ARNの名前などがベタ書きされているため、
それらの情報をGitHubのシークレットに保存して、その値を参照するように修正します。
リポジトリを開き、Settings > Secrets and variables の Actions を開きます。
「Repository secrets」の「New repositody secret」で追加していきます。
追加しました。
yamlファイルをシークレット参照に書き換えます。
以下抜粋
env:
SAM_TEMPLATE: template.yaml
TESTING_STACK_NAME: ${{ secrets.TESTING_STACK_NAME }}
TESTING_PIPELINE_EXECUTION_ROLE: ${{ secrets.TESTING_PIPELINE_EXECUTION_ROLE }}
TESTING_CLOUDFORMATION_EXECUTION_ROLE: ${{ secrets.TESTING_CLOUDFORMATION_EXECUTION_ROLE }}
TESTING_ARTIFACTS_BUCKET: ${{ secrets.TESTING_ARTIFACTS_BUCKET }}
TESTING_REGION: ap-northeast-1
PROD_STACK_NAME: ${{ secrets.PROD_STACK_NAME }}
PROD_PIPELINE_EXECUTION_ROLE: ${{ secrets.PROD_PIPELINE_EXECUTION_ROLE }}
PROD_CLOUDFORMATION_EXECUTION_ROLE: ${{ secrets.PROD_CLOUDFORMATION_EXECUTION_ROLE }}
PROD_ARTIFACTS_BUCKET: ${{ secrets.PROD_ARTIFACTS_BUCKET }}
PROD_REGION: ap-northeast-1
デプロイ確認
yamlファイルをプッシュします。
ブランチ名はfeature/sam-pipeline
としました。
GitHub Actionsのイメージ
※一発でデプロイできなかったので対処
むしろこちらの解消に時間がかかりました。。
pipeline.yamlのディレクトリ階層違い
複数のSAMを1つのリポジトリで管理している想定で試していたので、
SAMのリポジトリ構成がプロジェクト直下にSAMアプリケーションを配置していないことから、
作成されるyamlファイルがリポジトリ直下に作成されませんでした。
pipeline.yaml
の各アクションにworking-directory
を指定して参照ディレクトリ位置を調整して解決
Goのビルドでエラー
- name: Build resources
run: sam build --template ${SAM_TEMPLATE} --use-container
エラー内容
Error: GoModulesBuilder:Resolver - Path resolution for runtime: provided of binary: go was not successful
sam build
でエラーが発生
→ ローカルでも試してみましたが、--use-container
を指定したコンテナでのビルドがうまく行かないようです。
LambdaのRuntime問題なのか、いろいろ試しましたがうまく行きません。
AWS CLIの最新化、SAM CLIの最新化もしてみましたが結果は変わらず。
コンテナビルドするとうまくいかないので--use-container
オプションを外して解決
Assumeロール権限が不足
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
aws-region: ${{ env.TESTING_REGION }}
role-to-assume: ${{ env.TESTING_PIPELINE_EXECUTION_ROLE }}
role-session-name: feature-deployment
role-duration-seconds: 3600
role-skip-session-tagging: true
エラー内容
(node:5491) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023.
Please migrate your code to use AWS SDK for JavaScript (v3).
For more information, check the migration guide at https://a.co/7PzMCcy
(Use `node --trace-warnings ...` to show where the warning was created)
Error: Not authorized to perform sts:AssumeRoleWithWebIdentity
ここは2点修正ありました。
- 1点目
aws-actions/configure-aws-credentials@v1-node16
アクションのバージョンが古いため、上げないといけない旨の警告が出ているため、
公式見るとv4の例が出ていたので、v4にしました。
https://github.com/aws-actions/configure-aws-credentials
この修正でエラーが発生せずに実行はされるのですが、何回もリトライし、タイムアウトになってしまい、結果的にsts:AssumeRoleWithWebIdentity
の権限がないという同じエラーが発生。
bootstrapコマンドで作成されたIAMロールの信頼関係を確認したところ、GitHub ActionsとIAMロールの信頼関係の設定に差異があることが分かりました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::{アカウントID}:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"ForAllValues:StringLike": {
"token.actions.githubusercontent.com:sub": "repo:KaT0819/aws-step-function-sample:ref:refs/heads/develop",
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
}
]
}
Condition
で指定しているブランチの指定がdevelop
となっています。
token.actions.githubusercontent.com:sub": "repo:KaT0819/aws-step-function-sample:ref:refs/heads/develop
GitHub Actionsで動作しているのはfeatureブランチのため、これでは許可されません。
これはテンプレート作成バグではないだろうか・・・
対話形式で進めている最中にdevelop
を指定したので、GitHub Actionsのファイルもdevelop
であることを期待していたので、yamlファイルのfeature**
を追加します。
"Condition": {
"ForAllValues:StringLike": {
"token.actions.githubusercontent.com:sub": [
"repo:KaT0819/aws-step-function-sample:ref:refs/heads/develop",
"repo:KaT0819/aws-step-function-sample:ref:refs/heads/feature**"
],
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
}
}
テスト環境デプロイ時にエラー
- name: Deploy to feature stack in the testing account
shell: bash
run: |
sam deploy --stack-name $(echo ${GITHUB_REF##*/} | tr -cd '[a-zA-Z0-9-]') \
--capabilities CAPABILITY_IAM \
--region ${TESTING_REGION} \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--no-fail-on-empty-changeset \
--role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}
エラー内容
Error: Cannot use both --resolve-s3 and --s3-bucket parameters in non-guided deployments. Please use only one or use the --guided option for a guided deployment.
デプロイコマンドのs3-bucket
とresolve-s3
は同時に使えないとのこと。
resolve-s3
はコマンドに書いてないので何だろうと思いましたが、
ローカルでデプロイした際に作成されたsamconfig.toml
もプッシュしていたので、
こちらの設定ファイルの内容も見てしまってそうな雰囲気を感じたので、
samconfig.toml
を削除してプッシュ。
これが正解でした。
これでようやくビルドが通るようになりました。
本番環境デプロイ時にエラー
Assuming role with OIDC
Assuming role with OIDC
Assuming role with OIDC
Error: Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity
またもやAssumeRoleの不足でエラー。
mainブランチデプロイ時に、develop側にも何かしようとしているため、許可しているブランチの条件に一致せずエラーです。
やはりテンプレートが・・・
mainデプロイ時はテスト環境へのアクセスは不要なので、テスト関係のアクションを除去しようかと思いました。
が、出来上がったテンプレートの動作を一旦確認したいと思い、必要なブランチを許可しました。
修正内容はテスト環境デプロイ時の対策と同様、role-to-assume
に指定しているIAMロールの信頼関係へ必要なブランチを追加してあげて解決。
アーティファクトのダウンロードに失敗
actions/download-artifact@v3
によるダウンロードが失敗しました。
Run actions/download-artifact@v3
Starting download for packaged-testing.yaml
Error: Unable to find any artifacts for the associated workflow
遡ってみると、actions/download-artifact@v3
によるアップロードで警告が出ています。
Run actions/upload-artifact@v3
Warning: No files were found with the provided path: packaged-testing.yaml. No artifacts will be uploaded.
やろうとしてることは、sam package
コマンド実行時にtemplate.yaml
をpackaged-testing.yaml
という名前指定で出力し、
このpackaged-testing.yaml
をアップロード。
次のジョブでpackaged-testing.yaml
をダウンロードして、デプロイに使用しています。
色々試した結果、先のディレクトリ階層違いが影響しており、
アップロード時にパスが不適切な部分を修正し、アップロードの警告は消えました。
しかし、ダウンロードしようとするとファイルが取れずエラー。こちらもパスを修正します。
パス関係のみ抜粋すると下記のようになりました。
あまり関係ない部分はアクションのみに省略しています。
build-and-package:
if: github.ref == 'refs/heads/main'
needs: [test]
defaults:
run:
working-directory: ./sam-step-function-sample
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
- name: Build resources
run: sam build --template ${SAM_TEMPLATE}
- name: Assume the testing pipeline user role
uses: aws-actions/configure-aws-credentials@v4
- name: Upload artifacts to testing artifact buckets
run: |
sam package \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--region ${TESTING_REGION} \
--output-template-file packaged-testing.yaml
- uses: actions/upload-artifact@v3
with:
name: packaged-testing.yaml
path: ./sam-step-function-sample/packaged-testing.yaml
- name: Assume the prod pipeline user role
uses: aws-actions/configure-aws-credentials@v4
- name: Upload artifacts to production artifact buckets
run: |
sam package \
--s3-bucket ${PROD_ARTIFACTS_BUCKET} \
--region ${PROD_REGION} \
--output-template-file packaged-prod.yaml
- uses: actions/upload-artifact@v3
with:
name: packaged-prod.yaml
path: ./sam-step-function-sample/packaged-prod.yaml
deploy-testing:
if: github.ref == 'refs/heads/main'
needs: [build-and-package]
defaults:
run:
working-directory: ./sam-step-function-sample
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: aws-actions/setup-sam@v2
- uses: actions/download-artifact@v3
with:
name: packaged-testing.yaml
- name: Assume the testing pipeline user role
- name: Deploy to testing account
run: |
sam deploy --stack-name ${TESTING_STACK_NAME} \
--template ../packaged-testing.yaml \
--capabilities CAPABILITY_IAM \
--region ${TESTING_REGION} \
--s3-bucket ${TESTING_ARTIFACTS_BUCKET} \
--no-fail-on-empty-changeset \
--role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}
defaultsでworking-directoryを指定しています。
samのコマンドではworking-directoryの指定が効いてくれるのですが、
actions/upload-artifact
のアクションには効いてくれず、ファイルが存在しないエラーが発生していました。
actions/download-artifact
も同様です。
最終系がこちら
まとめ
- SAM Pipelineを使用することでCI/CDパイプラインのテンプレートが簡単に作成できます。
- OIDCの信頼関係構築やデプロイ時の権限周りが簡単につくれるのは良い。
- 作成されたテンプレートは若干構成が怪しい。
- ポリシーの信頼関係とGitHub Actionsの対象ブランチが一致していません。
- コンテナでのsam buildが失敗します。(※Go特有な要因ではあります)
- 本番用のデプロイフローのはずが、テスト用と本番用を同じフローで続けてデプロイしています。
まったくのゼロ知識でCI/CDが構築できるわけではなかったのが残念でした。
IAM認証であればすんなりいけるかもしれません。
OIDCの場合、AWS側のロールとActionsの定義のずれを見つけるのは何を作られるかを知っていないと解決は難しそうな気がしました。
この辺は今後のSAMパイプラインの進化に期待ですね。
今回のSAMパイプラインを検証したリポジトリはこちら。
Actionsの履歴がひどいことになっていますw
参考サイト
-
AWS CodePipelineでの構築手順です。
https://aws.amazon.com/jp/builders-flash/202110/new-lambda-container-development-6/?awsf.filter-name=*all -
GitLabでの構築手順です。
https://aws.amazon.com/jp/blogs/compute/introducing-aws-sam-pipelines-automatically-generate-deployment-pipelines-for-serverless-applications/ -
GitHubでの構築手順です。
https://zenn.dev/fusic/articles/aws-sam-pipeline-2023
Discussion