🦥

sam pipelineで生成したpipeline.yamlを使用して、GitHub ActionsでCI/CDをやってみた

2022/04/28に公開

https://zenn.dev/marumarumeruru/articles/6aeb25bd27063a
の続きです

構成

下記のCI/CDを実装します
featureブランチをpushしたタイミング

  • テスト実施
  • dev環境にdeploy

featureブランチをmasterにマージしたタイミング

  • テスト実施
  • prod環境にdeploy
  • featureブランチ削除
構成
 _________    _________ 
| feature |  | master  |
|   dev   |  |  prod   |
| Stage 1 |->| Stage 2 |
|_________|  |_________|

dev環境を作成する

最初にprodを作ると順序が逆になり、失敗しました

~/environment/sam-app $ 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.

[1] Stage definition
Enter a name for this stage. This will be referenced later when you use the sam pipeline init command:
Stage name: 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)
        q - Quit and configure AWS credentials
Select a credential source to associate with this stage: 2
Associated account 123456789012 with stage dev.

Enter the region in which you want these resources to be created [ap-northeast-1]: 
Enter the pipeline IAM user ARN if you have previously created one, or we will create one for you []: 

[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]: y
Please enter the ECR image repository ARN(s) for your Image type function(s).If you do not yet have a repository, we will create one for you []: 

[4] Summary
Below is the summary of the answers:
        1 - Account: 123456789012
        2 - Stage name: dev
        3 - Region: ap-northeast-1
        4 - Pipeline user: [to be created]
        5 - Pipeline execution role: [to be created]
        6 - CloudFormation execution role: [to be created]
        7 - Artifacts bucket: [to be created]
        8 - ECR image repository: [to be created]
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' environment: 
        - Pipeline IAM user
        - Pipeline execution role
        - CloudFormation execution role
        - Artifact bucket
        - ECR image repository
Should we proceed with the creation? [y/N]: y
        Creating the required resources...
        Successfully created!
The following resources were created in your account:
        - Pipeline IAM user
        - Pipeline execution role
        - CloudFormation execution role
        - Artifact bucket
        - ECR image repository
Pipeline IAM user credential:
        AWS_ACCESS_KEY_ID: AKIA****************
        AWS_SECRET_ACCESS_KEY: *****************************
View the definition in .aws-sam/pipeline/pipelineconfig.toml,
run sam pipeline bootstrap to generate another set of resources, or proceed to
sam pipeline init to create your pipeline configuration file.

Before running sam pipeline init, we recommend first setting up AWS credentials
in your CI/CD account. Read more about how to do so with your provider in
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-generating-example-ci-cd-others.html.

prodを作成する

~/environment/sam-app $ sam pipeline init --bootstrap

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 structure template to get started:
Select template
        1 - AWS Quick Start Pipeline Templates
        2 - Custom Pipeline Template Location
Choice: 1

Cloning from https://github.com/aws/aws-sam-cli-pipeline-init-templates.git
CI/CD system
        1 - Jenkins
        2 - GitLab CI/CD
        3 - GitHub Actions
        4 - Bitbucket Pipelines
        5 - AWS CodePipeline
Choice: 3
You are using the 2-stage pipeline template.
 _________    _________ 
|         |  |         |
| Stage 1 |->| Stage 2 |
|_________|  |_________|

Checking for existing stages...

Only 1 stage(s) were detected, fewer than what the template requires: 2.

Do you want to go through stage setup process now? If you choose no, you can still reference other bootstrapped resources. [y/N]: y

For each stage, we will ask for [1] stage definition, [2] account details, and [3]
reference application build resources in order to bootstrap these pipeline
resources.

We recommend using an individual AWS account profiles for each stage in your
pipeline. You can set these profiles up using [little bit of info on how to do
this/docs].


Stage 2 Setup

[1] Stage definition
Enter a name for this stage. This will be referenced later when you use the sam pipeline init command:
Stage name: prod

[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)
        q - Quit and configure AWS credentials
Select a credential source to associate with this stage: 2
Associated account 123456789012 with stage prod.

Enter the region in which you want these resources to be created [ap-northeast-1]: 
Pipeline IAM user ARN: arn:aws:iam::123456789012:user/aws-sam-cli-managed-dev-pipeline-reso-PipelineUser-***********

[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]: y
Please enter the ECR image repository ARN(s) for your Image type function(s).If you do not yet have a repository, we will create one for you []: arn:aws:ecr:ap-northeast-1:123456789012:repository/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********

[4] Summary
Below is the summary of the answers:
        1 - Account: 123456789012
        2 - Stage name: prod
        3 - Region: ap-northeast-1
        4 - Pipeline user ARN: arn:aws:iam::123456789012:user/aws-sam-cli-managed-dev-pipeline-reso-PipelineUser-***********
        5 - Pipeline execution role: [to be created]
        6 - CloudFormation execution role: [to be created]
        7 - Artifacts bucket: [to be created]
        8 - ECR image repository ARN: arn:aws:ecr:ap-northeast-1:123456789012:repository/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********
Press enter to confirm the values above, or select an item to edit the value: 

This will create the following required resources for the 'prod' environment: 
        - Pipeline execution role
        - CloudFormation execution role
        - Artifact bucket
Should we proceed with the creation? [y/N]: y
        Creating the required resources...
        Successfully created!
The following resources were created in your account:
        - Pipeline execution role
        - CloudFormation execution role
        - Artifact bucket
View the definition in .aws-sam/pipeline/pipelineconfig.toml,
run sam pipeline bootstrap to generate another set of resources, or proceed to
sam pipeline init to create your pipeline configuration file.

Checking for existing stages...


This template configures a pipeline that deploys a serverless application to a testing and a production stage.

What is the GitHub secret name for pipeline user account access key ID? [AWS_ACCESS_KEY_ID]: 
What is the GitHub Secret name for pipeline user account access key secret? [AWS_SECRET_ACCESS_KEY]: 
What is the git branch used for production deployments? [main]: master
What is the template file path? [template.yaml]: 
We use the stage configuration name to automatically retrieve the bootstrapped resources created when you ran `sam pipeline bootstrap`.

Here are the stage names detected in .aws-sam/pipeline/pipelineconfig.toml:
        1 - dev
        2 - prod
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-app]: sam-app-dev
Stage 1 configured successfully, configuring stage 2.

Here are the stage names detected in .aws-sam/pipeline/pipelineconfig.toml:
        1 - dev
        2 - prod
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-app]: sam-app-prod
Stage 2 configured successfully.

SUMMARY
We will generate a pipeline config file based on the following information:
        What is the GitHub secret name for pipeline user account access key ID?: AKIA****************
        What is the GitHub Secret name for pipeline user account access key secret?: *****************************
        What is the git branch used for production deployments?: master
        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-app-dev
        What is the pipeline execution role ARN for stage 1?: arn:aws:iam::123456789012:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-***********
        What is the CloudFormation execution role ARN for stage 1?: arn:aws:iam::123456789012:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-***********
        What is the S3 bucket name for artifacts for stage 1?: aws-sam-cli-managed-dev-pipeline-artifactsbucket-***********
        What is the ECR repository URI for stage 1?: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********
        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-app-prod
        What is the pipeline execution role ARN for stage 2?: arn:aws:iam::123456789012:role/aws-sam-cli-managed-prod-pip-PipelineExecutionRole-***********
        What is the CloudFormation execution role ARN for stage 2?: arn:aws:iam::123456789012:role/aws-sam-cli-managed-prod-CloudFormationExecutionR-***********
        What is the S3 bucket name for artifacts for stage 2?: aws-sam-cli-managed-prod-pipeline-artifactsbucket-***********
        What is the ECR repository URI for stage 2?: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********
        What is the AWS region for stage 2?: ap-northeast-1
Successfully created the pipeline configuration file(s):
        - .github/workflows/pipeline.yaml

.github/workflows/pipeline.yamlのコミット

生成されたものを修正して、このパスのままコミットする

pipeline.yaml 生成されたもの
name: Pipeline

on:
  push:
    branches:
      - 'master'
      - 'feature**'
  delete:
    branches:
      - 'feature**'

env:
  PIPELINE_USER_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  PIPELINE_USER_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  SAM_TEMPLATE: template.yaml
  TESTING_STACK_NAME: sam-app-dev
  TESTING_PIPELINE_EXECUTION_ROLE: arn:aws:iam::123456789012:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-***********
  TESTING_CLOUDFORMATION_EXECUTION_ROLE: arn:aws:iam::123456789012:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-***********
  TESTING_ARTIFACTS_BUCKET: aws-sam-cli-managed-dev-pipeline-artifactsbucket-***********
  TESTING_IMAGE_REPOSITORY: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********
  TESTING_REGION: ap-northeast-1
  PROD_STACK_NAME: sam-app-prod
  PROD_PIPELINE_EXECUTION_ROLE: arn:aws:iam::123456789012:role/aws-sam-cli-managed-prod-pip-PipelineExecutionRole-***********
  PROD_CLOUDFORMATION_EXECUTION_ROLE: arn:aws:iam::123456789012:role/aws-sam-cli-managed-prod-CloudFormationExecutionR-***********
  PROD_ARTIFACTS_BUCKET: aws-sam-cli-managed-prod-pipeline-artifactsbucket-***********
  PROD_IMAGE_REPOSITORY: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/aws-sam-cli-managed-dev-pipeline-resources-imagerepository-***********
  PROD_REGION: ap-northeast-1

jobs:
  test:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - 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@v2
      - uses: actions/setup-python@v2
      - uses: aws-actions/setup-sam@v1

      - name: Assume the testing pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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.
    if: startsWith(github.ref, 'refs/heads/feature')
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - uses: aws-actions/setup-sam@v1
      - run: sam build --template ${SAM_TEMPLATE} --use-container

      - name: Assume the testing pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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} \
            --image-repository ${TESTING_IMAGE_REPOSITORY} \
            --no-fail-on-empty-changeset \
            --role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}

  build-and-package:
    if: github.ref == 'refs/heads/master'
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - uses: aws-actions/setup-sam@v1

      - 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
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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} \
            --image-repository ${TESTING_IMAGE_REPOSITORY} \
            --region ${TESTING_REGION} \
            --output-template-file packaged-testing.yaml

      - uses: actions/upload-artifact@v2
        with:
          name: packaged-testing.yaml
          path: packaged-testing.yaml

      - name: Assume the prod pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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} \
            --image-repository ${PROD_IMAGE_REPOSITORY} \
            --region ${PROD_REGION} \
            --output-template-file packaged-prod.yaml

      - uses: actions/upload-artifact@v2
        with:
          name: packaged-prod.yaml
          path: packaged-prod.yaml

  deploy-testing:
    if: github.ref == 'refs/heads/master'
    needs: [build-and-package]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
      - uses: aws-actions/setup-sam@v1
      - uses: actions/download-artifact@v2
        with:
          name: packaged-testing.yaml

      - name: Assume the testing pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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} \
            --image-repository ${TESTING_IMAGE_REPOSITORY} \
            --no-fail-on-empty-changeset \
            --role-arn ${TESTING_CLOUDFORMATION_EXECUTION_ROLE}

  integration-test:
    if: github.ref == 'refs/heads/master'
    needs: [deploy-testing]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: |
          # trigger the integration tests here

  deploy-prod:
    if: github.ref == 'refs/heads/master'
    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@v2
      - uses: actions/setup-python@v2
      - uses: aws-actions/setup-sam@v1
      - uses: actions/download-artifact@v2
        with:
          name: packaged-prod.yaml

      - name: Assume the prod pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          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} \
            --image-repository ${PROD_IMAGE_REPOSITORY} \
            --no-fail-on-empty-changeset \
            --role-arn ${PROD_CLOUDFORMATION_EXECUTION_ROLE}

secretの登録

自動生成されたPipelineUserのACCESS_KEY_ID、SECRET_ACCESS_KEYをGITHUBのsecretとして登録する
Settings > Secrets > Actions > New repository secretで登録できる

env:
  PIPELINE_USER_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  PIPELINE_USER_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

samconfig.tomlの変更

trueだと確認操作待ちになり失敗する

変更後
confirm_changeset = false

PipelineExecutionRolePermissionsの権限補正

自動生成されたままだと、delete-featureで権限が足りずにエラーになる

GetTemplate権限不足
Error: Failed to fetch the template for the stack: cicd, An error occurred (AccessDenied) when calling the GetTemplate operation: User: arn:aws:sts::***:assumed-role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-1CQHYJJ1HT9ZN/testing-packaging is not authorized to perform: cloudformation:GetTemplate on resource: arn:aws:cloudformation:ap-northeast-1:***:stack/cicd/bb8844c0-c661-11ec-aa45-0a395be04137 because no identity-based policy allows the cloudformation:GetTemplate action
Error: Process completed with exit code 1.
DeleteObject権限不足
botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the DeleteObject operation: Access Denied
Error: Process completed with exit code 1.
DeleteStack権限不足
Error: Failed to delete the stack: cicd, An error occurred (AccessDenied) when calling the DeleteStack operation: User: arn:aws:sts::***:assumed-role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-1CQHYJJ1HT9ZN/testing-packaging is not authorized to perform: cloudformation:DeleteStack on resource: arn:aws:cloudformation:ap-northeast-1:***:stack/cicd/bb8844c0-c661-11ec-aa45-0a395be04137 because no identity-based policy allows the cloudformation:DeleteStack action
Error: Process completed with exit code 1.

下記の権限をマネージドコンソールで追加する

PipelineExecutionRolePermissionsの修正
cloudformation:GetTemplate
cloudformation:DeleteStack
s3:DeleteObject

testの実装

pipeline.yaml test部分の変更後
jobs:
  test:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Install Dependencies
        run: pip install pytest pytest-mock boto3
      - name: Assume the testing pipeline user role
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.PIPELINE_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.PIPELINE_USER_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.TESTING_REGION }}
      - name: docker-compose up
        run: |
          docker-compose up -d
      - name: pytest
        run: |
          python -m pytest -s tests/ -v

integration-test

未実装のままです
https://zenn.dev/marumarumeruru/articles/05e899e043f4f0
で実装しました

Discussion