「実際に手を動かす前に、SAM Pipelineの雰囲気をなんとなく掴みたい。。」
SAM Pipelineは対話形式で必要な情報を渡すと、サーバレスアプリケーションのデプロイパイプライン用のCloudFormation Templateを自動生成してくれます。
パイプライン作成をよしなにやってくれる機能ではありますが、中身もある程度知っておきたいものです。
勉強がてら作成してくれるリソースを眺めてみます。
準備: SAM Pipelineの作成
CodeCommitをソースにして、PipelineはCodePipelineを使用したものを作成します。
コマンドを実行して、CloudFormation Template等必要なファイルを用意します。
SAM Pipelineの使い方やデプロイの流れは以下の記事がわかりやすかったです。
% sam pipeline bootstrap
# 省略
[4] Summary
Below is the summary of the answers:
1 - Account: 0000000
2 - Stage configuration 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: [skipped]
% sam pipeline init
# 省略
SUMMARY
We will generate a pipeline config file based on the following information:
What is the Git provider?: CodeCommit
What is the CodeCommit repository name?: samp-app-test
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): sam-app-dev
What is the sam application stack name for stage 1?: sam-app-prd
What is the pipeline execution role ARN for stage 1?: arn:aws:iam::000000000:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-QO6TWPN0NBLJ
What is the CloudFormation execution role ARN for stage 1?: arn:aws:iam::000000000:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-7KYS28POR6G8
What is the S3 bucket name for artifacts for stage 1?: aws-sam-cli-managed-dev-pipeline-artifactsbucket-ywqrfhm83rhl
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): 1
What is the sam application stack name for stage 2?: sam-app
What is the pipeline execution role ARN for stage 2?: arn:aws:iam::000000000:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-QO6TWPN0NBLJ
What is the CloudFormation execution role ARN for stage 2?: arn:aws:iam::000000000:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-7KYS28POR6G8
What is the S3 bucket name for artifacts for stage 2?: aws-sam-cli-managed-dev-pipeline-artifact
What is the ECR repository URI for stage 2?:
What is the AWS region for stage 2?: ap-northeast-1
上記コマンドを実行すると、以下のようにファイルが作成されます。
├── ./assume-role.sh
├── ./codepipeline.yaml
└── ./pipeline
├── ./pipeline/buildspec_build_package.yml
├── ./pipeline/buildspec_deploy.yml
├── ./pipeline/buildspec_feature.yml
├── ./pipeline/buildspec_integration_test.yml
└── ./pipeline/buildspec_unit_test.yml
初期生成される雛形ファイルの中身は、Githubからも確認できます。
Github aws/aws-sam-cli-pipeline-init-templates
眺めてみる
CloudFormatoin Tempalte(codepipeline.yaml)
まずは、CloudFormation Templateであるcodepipeline.yamlです。
codepipeline.yaml
AWSTemplateFormatVersion : '2010-09-09'
Description: >
This template deploys a CodePipeline with its required resources.
The following stages are predefined in this template:
- Source
- UpdatePipeline
- BuildAndDeployFeatureStack (FeatureGitBranch only)
- BuildAndPackage (MainGitBranch only)
- DeployTest (MainGitBranch only)
- DeployProd (MainGitBranch only)
**WARNING** You will be billed for the AWS resources used if you create a stack from this template.
# To deploy this template and connect to the main git branch, run this against the leading account:
# `sam deploy -t codepipeline.yaml --stack-name <stack-name> --capabilities=CAPABILITY_IAM`.
# If later you need to deploy a new CodePipeline to connect to a non-main git branch, run
#
# sam deploy -t codepipeline.yaml --stack-name <stack-name> --capabilities=CAPABILITY_IAM \
# --parameter-overrides="FeatureGitBranch=<branch-name>"
#
Parameters:
GitProviderType:
Type: String
Default: "CodeCommit"
CodeCommitRepositoryName:
Type: String
Default: "samp-app-test"
MainGitBranch:
Type: String
Default: "main"
SamTemplate:
Type: String
Default: "template.yaml"
TestingRegion:
Type: String
Default: "ap-northeast-1"
TestingStackName:
Type: String
Default: "sam-app-prd"
TestingPipelineExecutionRole:
Type: String
Default: "arn:aws:iam::00000000000:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-QO6TWPN0NBLJ"
TestingCloudFormationExecutionRole:
Type: String
Default: "arn:aws:iam::00000000000:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-7KYS28POR6G8"
TestingArtifactBucket:
Type: String
Default: "bucket"
TestingImageRepository:
Type: String
# If there are functions with "Image" PackageType in your template,
# Update the line below with image repository URL and add "--image-repository ${TESTING_IMAGE_REPOSITORY}" to
# prod "sam package" and "sam deploy" commands in buildspec files (in pipeline/).
Default: ""
ProdRegion:
Type: String
Default: "ap-northeast-1"
ProdStackName:
Type: String
Default: "sam-app"
ProdPipelineExecutionRole:
Type: String
Default: "arn:aws:iam::00000000000:role/aws-sam-cli-managed-dev-pipe-PipelineExecutionRole-QO6TWPN0NBLJ"
ProdCloudFormationExecutionExeRole:
Type: String
Default: "arn:aws:iam::00000000000:role/aws-sam-cli-managed-dev-p-CloudFormationExecutionR-7KYS28POR6G8"
ProdArtifactBucket:
Type: String
Default: "bucket"
ProdImageRepository:
Type: String
# If there are functions with "Image" PackageType in your template,
# Update the line below with image repository URL and add "--image-repository ${PROD_IMAGE_REPOSITORY}" to
# prod "sam package" and "sam deploy" commands in buildspec files (in pipeline/).
Default: ""
# CodeStarConnectionArn and FeatureGitBranch are required for pipelines for feature branches
CodeStarConnectionArn:
Type: String
Default: ""
FeatureGitBranch:
Type: String
Default: ""
Conditions:
IsMainBranchPipeline: !Equals [!Ref FeatureGitBranch, ""]
IsFeatureBranchPipeline: !Not [Condition: IsMainBranchPipeline]
Resources:
# ____
# / ___| ___ _ _ _ __ ___ ___
# \___ \ / _ \| | | | '__/ __/ _ \
# ___) | (_) | |_| | | | (_| __/
# |____/ \___/ \__,_|_| \___\___|
CloudWatchEventRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action: sts:AssumeRole
Policies:
-
PolicyName: cwe-pipeline-execution
PolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Action: codepipeline:StartPipelineExecution
Resource: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}"
CloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
EventPattern:
source:
- aws.codecommit
detail-type:
- 'CodeCommit Repository State Change'
resources:
- !Sub "arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${CodeCommitRepositoryName}"
detail:
event:
- referenceCreated
- referenceUpdated
referenceType:
- branch
referenceName:
- !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, !Ref MainGitBranch]
Targets:
- Arn: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}"
RoleArn: !GetAtt CloudWatchEventRole.Arn
Id: codepipeline-AppPipeline
# ____ _ _ _
# | _ \(_)_ __ ___| (_)_ __ ___
# | |_) | | '_ \ / _ | | | '_ \ / _ \
# | __/| | |_) | __| | | | | | __/
# |_| |_| .__/ \___|_|_|_| |_|\___|
# |_|
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref PipelineArtifactsBucket
Type: S3
RoleArn: !GetAtt CodePipelineExecutionRole.Arn
RestartExecutionOnUpdate: true
Stages:
- Name: Source
Actions:
- Name: SourceCodeRepo
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: "1"
Configuration:
RepositoryName: !Ref CodeCommitRepositoryName
PollForSourceChanges: false
BranchName: !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, !Ref MainGitBranch]
OutputArtifacts:
- Name: SourceCodeAsZip
RunOrder: 1
- Name: UpdatePipeline
Actions:
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt PipelineStackCloudFormationExecutionRole.Arn
StackName: !Ref AWS::StackName
ChangeSetName: !Sub ${AWS::StackName}-ChangeSet
TemplatePath: SourceCodeAsZip::codepipeline.yaml
Capabilities: CAPABILITY_NAMED_IAM
ParameterOverrides: !Sub |
{
"FeatureGitBranch": "${FeatureGitBranch}",
"CodeStarConnectionArn": "${CodeStarConnectionArn}"
}
InputArtifacts:
- Name: SourceCodeAsZip
RunOrder: 1
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: "1"
Configuration:
ActionMode: CHANGE_SET_EXECUTE
RoleArn: !GetAtt PipelineStackCloudFormationExecutionRole.Arn
StackName: !Ref AWS::StackName
ChangeSetName: !Sub ${AWS::StackName}-ChangeSet
OutputArtifacts:
- Name: !Sub ${AWS::StackName}ChangeSet
RunOrder: 2
# Uncomment and modify the following step for running the unit-tests
# - Name: UnitTest
# Actions:
# - Name: UnitTest
# ActionTypeId:
# Category: Build
# Owner: AWS
# Provider: CodeBuild
# Version: "1"
# Configuration:
# ProjectName: !Ref CodeBuildProjectUnitTest
# InputArtifacts:
# - Name: SourceCodeAsZip
- !If
- IsFeatureBranchPipeline
- Name: BuildAndDeployFeatureStack
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildProjectBuildAndDeployFeature
InputArtifacts:
- Name: SourceCodeAsZip
- !Ref AWS::NoValue
- !If
- IsMainBranchPipeline
- Name: BuildAndPackage
Actions:
- Name: CodeBuild
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildProjectBuildAndPackage
InputArtifacts:
- Name: SourceCodeAsZip
OutputArtifacts:
- Name: BuildArtifactAsZip
- !Ref AWS::NoValue
- !If
- IsMainBranchPipeline
- Name: DeployTest
Actions:
- Name: DeployTest
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
Configuration:
ProjectName: !Ref CodeBuildProjectDeploy
EnvironmentVariables: !Sub |
[
{"name": "ENV_TEMPLATE", "value": "packaged-test.yaml"},
{"name": "ENV_REGION", "value": "${TestingRegion}"},
{"name": "ENV_STACK_NAME", "value": "${TestingStackName}"},
{"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${TestingPipelineExecutionRole}"},
{"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${TestingCloudFormationExecutionRole}"},
{"name": "ENV_BUCKET", "value": "${TestingArtifactBucket}"},
{"name": "ENV_IMAGE_REPOSITORY", "value": "${TestingImageRepository}"}
]
InputArtifacts:
- Name: BuildArtifactAsZip
RunOrder: 1
# Uncomment the following step for running the integration tests
# - Name: IntegrationTest
# ActionTypeId:
# Category: Build
# Owner: AWS
# Provider: CodeBuild
# Version: "1"
# Configuration:
# ProjectName: !Ref CodeBuildProjectIntegrationTest
# InputArtifacts:
# - Name: SourceCodeAsZip
# RunOrder: 2
- !Ref AWS::NoValue
- !If
- IsMainBranchPipeline
- Name: DeployProd
Actions:
# uncomment this to have a manual approval step before deployment to production
# - Name: ManualApproval
# ActionTypeId:
# Category: Approval
# Owner: AWS
# Provider: Manual
# Version: "1"
# RunOrder: 1
- Name: DeployProd
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: "1"
RunOrder: 2 # keeping run order as 2 in case manual approval is enabled
Configuration:
ProjectName: !Ref CodeBuildProjectDeploy
EnvironmentVariables: !Sub |
[
{"name": "ENV_TEMPLATE", "value": "packaged-prod.yaml"},
{"name": "ENV_REGION", "value": "${ProdRegion}"},
{"name": "ENV_STACK_NAME", "value": "${ProdStackName}"},
{"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${ProdPipelineExecutionRole}"},
{"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${ProdCloudFormationExecutionExeRole}"},
{"name": "ENV_BUCKET", "value": "${ProdArtifactBucket}"},
{"name": "ENV_IMAGE_REPOSITORY", "value": "${ProdImageRepository}"}
]
InputArtifacts:
- Name: BuildArtifactAsZip
- !Ref AWS::NoValue
PipelineArtifactsBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
VersioningConfiguration:
Status: Enabled
LoggingConfiguration:
DestinationBucketName:
!Ref PipelineArtifactsLoggingBucket
LogFilePrefix: "artifacts-logs"
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PipelineArtifactsBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PipelineArtifactsBucket
PolicyDocument:
Statement:
- Effect: "Deny"
Action: "s3:*"
Principal: "*"
Resource:
- !Sub "${PipelineArtifactsBucket.Arn}/*"
- !GetAtt PipelineArtifactsBucket.Arn
Condition:
Bool:
aws:SecureTransport: false
- Action:
- s3:*
Effect: Allow
Resource:
- !Sub arn:${AWS::Partition}:s3:::${PipelineArtifactsBucket}
- !Sub arn:${AWS::Partition}:s3:::${PipelineArtifactsBucket}/*
Principal:
AWS:
- !GetAtt CodePipelineExecutionRole.Arn
PipelineArtifactsLoggingBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
AccessControl: "LogDeliveryWrite"
VersioningConfiguration:
Status: Enabled
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PipelineArtifactsLoggingBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PipelineArtifactsLoggingBucket
PolicyDocument:
Statement:
- Effect: "Deny"
Action: "s3:*"
Principal: "*"
Resource:
- !Sub "${PipelineArtifactsLoggingBucket.Arn}/*"
- !GetAtt PipelineArtifactsLoggingBucket.Arn
Condition:
Bool:
aws:SecureTransport: false
CodePipelineExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Policies:
- PolicyName: CodePipelineAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "iam:PassRole"
Resource: "*"
- PolicyName: CodeCommitAccess
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'codecommit:CancelUploadArchive'
- 'codecommit:GetBranch'
- 'codecommit:GetCommit'
- 'codecommit:GetUploadArchiveStatus'
- 'codecommit:UploadArchive'
Resource:
- !Sub "arn:${AWS::Partition}:codecommit:${AWS::Region}:${AWS::AccountId}:${CodeCommitRepositoryName}"
- PolicyName: CodePipelineCodeAndS3Bucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- s3:GetBucketAcl
- s3:GetBucketLocation
Effect: Allow
Resource:
Fn::GetAtt:
- PipelineArtifactsBucket
- Arn
- Action:
- "s3:GetObject"
- "s3:GetObjectVersion"
- "s3:PutObject"
Effect: Allow
Resource:
Fn::Sub: ${PipelineArtifactsBucket.Arn}/*
- PolicyName: CodePipelineCodeBuildAndCloudformationAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "codebuild:StartBuild"
- "codebuild:BatchGetBuilds"
Resource:
# Uncomment the line below to enable the unit-tests
# - !GetAtt CodeBuildProjectUnitTest.Arn
- !If
- IsFeatureBranchPipeline
- !GetAtt CodeBuildProjectBuildAndDeployFeature.Arn
- !Ref AWS::NoValue
- !If
- IsMainBranchPipeline
- !GetAtt CodeBuildProjectBuildAndPackage.Arn
- !Ref AWS::NoValue
# Uncomment the following step for running the integration tests
# - !If
# - IsMainBranchPipeline
# - !GetAtt CodeBuildProjectIntegrationTest.Arn
# - !Ref AWS::NoValue
- !If
- IsMainBranchPipeline
- !GetAtt CodeBuildProjectDeploy.Arn
- !Ref AWS::NoValue
- Effect: Allow
Action:
- "cloudformation:CreateStack"
- "cloudformation:DescribeStacks"
- "cloudformation:DeleteStack"
- "cloudformation:UpdateStack"
- "cloudformation:CreateChangeSet"
- "cloudformation:ExecuteChangeSet"
- "cloudformation:DeleteChangeSet"
- "cloudformation:DescribeChangeSet"
- "cloudformation:SetStackPolicy"
- "cloudformation:SetStackPolicy"
- "cloudformation:ValidateTemplate"
Resource:
- !Sub "arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*"
# PipelineStackCloudFormationExecutionRole is used for the pipeline to self mutate
PipelineStackCloudFormationExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
Action: "sts:AssumeRole"
Effect: Allow
Principal:
Service: cloudformation.amazonaws.com
Policies:
- PolicyName: GrantCloudFormationFullAccess
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
# ____ _ ____ _ _ _
# / ___|___ __| | ___| __ ) _ _(_| | __| |
# | | / _ \ / _` |/ _ | _ \| | | | | |/ _` |
# | |__| (_) | (_| | __| |_) | |_| | | | (_| |
# \____\___/ \__,_|\___|____/ \__,_|_|_|\__,_|
CodeBuildServiceRole:
Type: AWS::IAM::Role
Properties:
Tags:
- Key: Role
Value: aws-sam-pipeline-codebuild-service-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: Allow
Principal:
Service:
- codebuild.amazonaws.com
Policies:
- PolicyName: CodeBuildLogs
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource:
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*"
- PolicyName: CodeBuildArtifactsBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetObject"
- "s3:GetObjectVersion"
- "s3:PutObject"
Resource:
- !Sub "arn:${AWS::Partition}:s3:::${PipelineArtifactsBucket}/*"
- PolicyName: AssumeStagePipExecutionRoles
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- sts:AssumeRole
Effect: Allow
Resource: "*"
Condition:
StringEquals:
aws:ResourceTag/Role: pipeline-execution-role
# Uncomment and modify the following step for running the unit-tests
# CodeBuildProjectUnitTest:
# Type: AWS::CodeBuild::Project
# Properties:
# Artifacts:
# Type: CODEPIPELINE
# Environment:
# Type: LINUX_CONTAINER
# ComputeType: BUILD_GENERAL1_SMALL
# Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
# ServiceRole: !GetAtt CodeBuildServiceRole.Arn
# Source:
# Type: CODEPIPELINE
# BuildSpec: pipeline/buildspec_unit_test.yml
CodeBuildProjectBuildAndDeployFeature:
Condition: IsFeatureBranchPipeline
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: SAM_TEMPLATE
Value: !Ref SamTemplate
- Name: TESTING_REGION
Value: !Ref TestingRegion
- Name: TESTING_PIPELINE_EXECUTION_ROLE
Value: !Ref TestingPipelineExecutionRole
- Name: TESTING_CLOUDFORMATION_EXECUTION_ROLE
Value: !Ref TestingCloudFormationExecutionRole
- Name: TESTING_ARTIFACT_BUCKET
Value: !Ref TestingArtifactBucket
- Name: TESTING_IMAGE_REPOSITORY
Value: !Ref TestingImageRepository
- Name: FEATURE_BRANCH_NAME
Value: !Ref FeatureGitBranch
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Source:
Type: CODEPIPELINE
BuildSpec: pipeline/buildspec_feature.yml
CodeBuildProjectBuildAndPackage:
Condition: IsMainBranchPipeline
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
PrivilegedMode: true
EnvironmentVariables:
- Name: SAM_TEMPLATE
Value: !Ref SamTemplate
- Name: TESTING_REGION
Value: !Ref TestingRegion
- Name: PROD_REGION
Value: !Ref ProdRegion
- Name: TESTING_PIPELINE_EXECUTION_ROLE
Value: !Ref TestingPipelineExecutionRole
- Name: PROD_PIPELINE_EXECUTION_ROLE
Value: !Ref ProdPipelineExecutionRole
- Name: TESTING_ARTIFACT_BUCKET
Value: !Ref TestingArtifactBucket
- Name: PROD_ARTIFACT_BUCKET
Value: !Ref ProdArtifactBucket
- Name: TESTING_IMAGE_REPOSITORY
Value: !Ref TestingImageRepository
- Name: PROD_IMAGE_REPOSITORY
Value: !Ref ProdImageRepository
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Source:
Type: CODEPIPELINE
BuildSpec: pipeline/buildspec_build_package.yml
# Uncomment and modify the following step for running the integration tests
# CodeBuildProjectIntegrationTest:
# Condition: IsMainBranchPipeline
# Type: AWS::CodeBuild::Project
# Properties:
# Artifacts:
# Type: CODEPIPELINE
# Environment:
# Type: LINUX_CONTAINER
# ComputeType: BUILD_GENERAL1_SMALL
# Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
# ServiceRole: !GetAtt CodeBuildServiceRole.Arn
# Source:
# Type: CODEPIPELINE
# BuildSpec: pipeline/buildspec_integration_test.yml
CodeBuildProjectDeploy:
Condition: IsMainBranchPipeline
Type: AWS::CodeBuild::Project
Properties:
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
ServiceRole: !GetAtt CodeBuildServiceRole.Arn
Source:
Type: CODEPIPELINE
BuildSpec: pipeline/buildspec_deploy.yml
AWS CloudFormation Designer では以下のように表示されました。
このCFnに含まれているリソースは以下になります。
- CodePipeline * 1
- Cloudwatch Event * 1
- S3 Bucket/BucketPolicy * 2
- CodeBuildProject * 3
- IAM Role * 4
デプロイパイプライン用のCodePipelineと、CodeCommitの変更をトリガーにCodePipelineを発火させるための CloudwatchEventが1つずつ作成されます。
S3 Bucketはsamのartifact保存用バケットと、そのバケットのロギング用のバケットが作成されています。
今回の設定では、CodeBuildは3つ作成されました。
- featureブランチのビルド・デプロイ
- mainブランチのビルド・パッケージ
- mainブランチのデプロイ
unit testやintegretion test用のbuildspecのファイルは作成されていますが、CloudFormation上ではコメントアウトされています。
IAMロールは、4つ作成されます。
- CloudwatchEvent
- CodePipelineのスタート - CodeCommitの変更を検知してCodePipelineを発火させる
- CodePipeline
- CodeCommitへのアクセス
- Artifact Bucketの読み書き
- CodeBuild実行
- CodeBuild
- Artifact Bucketの読み書き
- CloudwatchLogsへのログ出力
- sam deploy用のIAMロールの引き受け(assume-role)
- Cloudformation
- CloudFormationのフルアクセス
- sam deployでCloudformationを実行するIAMロールを指定
buildspec(pipeline配下)
pipeline
├── pipeline/buildspec_build_package.yml
├── pipeline/buildspec_deploy.yml
├── pipeline/buildspec_feature.yml # 今回は使用しない
├── pipeline/buildspec_integration_test.yml # デフォルトでは使用するCodeBuildがコメントアウトされている
└── pipeline/buildspec_unit_test.yml # デフォルトでは使用するCodeBuildがコメントアウトされている
今回は、デフォルトで無効化されている buildspec_integration_test.yml と buildspec_unit_test.yml の説明は省きます。 (testの部分は初期ではechoしているだけなので、ユーザー自身で編集する必要があります。)
同じく、buildspec_feature.yml も説明を省きます。 Featureブランチをトリガーにしたデプロイ設定する場合、今回はmainブランチだけをデプロイのトリガーにしています。
上記リンクのGithub上の雛形からも内容を確認できます。
ステージ「BuildAndPackage」では、sam buildとsam pacakgeコマンドを実行してデプロイに必要なパッケージの用意をしています。 本番とテストどちらも一つのビルドプロジェクトで作成しています。
buildspec_build_package.yml
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
commands:
- pip install --upgrade pip
- pip install --upgrade awscli aws-sam-cli
# Enable docker https://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker-custom-image.html
- nohup /usr/local/bin/dockerd --host=unix:///var/run/docker.sock --host=tcp://127.0.0.1:2375 --storage-driver=overlay2 &
- timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
build:
commands:
- sam build --use-container --template ${SAM_TEMPLATE}
- . ./assume-role.sh ${TESTING_PIPELINE_EXECUTION_ROLE} test-package
- sam package --s3-bucket ${TESTING_ARTIFACT_BUCKET}
--region ${TESTING_REGION}
--output-template-file packaged-test.yaml
- . ./assume-role.sh ${PROD_PIPELINE_EXECUTION_ROLE} prod-package
- sam package --s3-bucket ${PROD_ARTIFACT_BUCKET}
--region ${PROD_REGION}
--output-template-file packaged-prod.yaml
artifacts:
files:
- packaged-test.yaml
- packaged-prod.yaml
- assume-role.sh
- pipeline/*
ステージ「DeployTest」「DeployProd」では、前のステージで作成したパッケージを使用してsam deployを実行しています。 CodePipeline側で環境変数を渡して、本番とテストの環境差異(スタック名や、template)を表現しています。
buildspec_deploy.yml
version: 0.2
phases:
install:
runtime-versions:
python: 3.8
commands:
- pip install --upgrade pip
- pip install --upgrade awscli aws-sam-cli
build:
commands:
- . ./assume-role.sh ${ENV_PIPELINE_EXECUTION_ROLE} deploy
- sam deploy --stack-name ${ENV_STACK_NAME}
--template ${ENV_TEMPLATE}
--capabilities CAPABILITY_IAM
--region ${ENV_REGION}
--s3-bucket ${ENV_BUCKET}
--no-fail-on-empty-changeset
--role-arn ${ENV_CLOUDFORMATION_EXECUTION_ROLE}
assume-role.shとIAMロール
buildspec上でたびたび出てきた、assume-role.sh についてです。
その名の通り、ロールを引き受けて一時的に発行されるIAMアクセスキー等をセットするスクリプトです。
assume-role.sh
#!/bin/bash
ROLE=$1
SESSION_NAME=$2
# Unset AWS credentials stored in env so that every time this script runs,
# it will use the AWS CodeBuild service role to assume the target IAM roles.
unset AWS_SESSION_TOKEN
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
cred=$(aws sts assume-role --role-arn "$ROLE" \
--role-session-name "$SESSION_NAME" \
--query '[Credentials.AccessKeyId,Credentials.SecretAccessKey,Credentials.SessionToken]' \
--output text)
ACCESS_KEY_ID=$(echo "$cred" | awk '{ print $1 }')
export AWS_ACCESS_KEY_ID=$ACCESS_KEY_ID
SECRET_ACCESS_KEY=$(echo "$cred" | awk '{ print $2 }')
export AWS_SECRET_ACCESS_KEY=$SECRET_ACCESS_KEY
SESSION_TOKEN=$(echo "$cred" | awk '{ print $3 }')
export AWS_SESSION_TOKEN=$SESSION_TOKEN
作成されるCodeBuildには、共通のIAMロールがついています。 このIAMロール自体には、CloudFormationを更新することができる権限はついていません。
sam deployにはCloudFormationを更新する権限が必要です。
ステージ:BuildAndPackage/DeployTest/DeployProdのCodeBuildで、それぞれCloudFormationとS3を操作可能なIAMロールを引き受けています。
CodeBuildServiceRole ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Condition": {
"StringEquals": {
"aws:ResourceTag/Role": "pipeline-execution-role"
}
},
"Action": [
"sts:AssumeRole"
],
"Resource": "*",
"Effect": "Allow"
}
]
}
PipelineExecutionRole 信頼関係
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::00000000:user/aws-sam-cli-managed-dev-pipeline-reso-PipelineUser-1H2HRIQ8A3JGV"
},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::00000000:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Role": "aws-sam-pipeline-codebuild-service-role"
}
}
}
]
}
おわりに
SAM Pipelineで作成されるリソースについてでした。
ちなみに、sam pipeline init時のプロンプトをカスタマイズすることができるそうです。 利用頻度が多い場合は、この機能を使ってテンプレートを作成するのもいいかもしれません。
スターターパイプラインのカスタマイズ - AWS Serverless Application Model
この記事が、SAM Pipelineの雰囲気を掴むのに役立てば幸いです。
以上、AWS事業本部の佐藤(@chari7311)でした。