前提条件
以下の記事で手動構築していたCodePipelineのCI/CDパイプラインをCloudFormationで自動構築してみる記事。なので、構成やアプリケーションの内容は読んで把握しておくことが望ましい。
Amazon API Gateway/ALBのバックエンドで動くLambda関数をJava(Eclipse+maven)で実装する
ALBのバックエンドで動作するJava実装のLambda関数をBlue/GreenデプロイメントするCodePipelineを作る
最終的に目指すCI/CDパイプラインの構成イメージは以下。Lambda関数のフロントにALBがいる想定だが、このパイプラインでは触らない(厳密には、リスナーとターゲットグループは触る)ので割愛する。
記事中には、過去のCloudFormation関連の記事同様、プロパティに関する補足をしている部分がある。
備忘のためにリファレンスに書かれていないデフォルト値も整理しておくが、2020年4月時点の情報であり、後でAWSが仕様を変えたとしても追従する予定はないので、挙動が違ったらユーザーガイドのリファレンスを見直してほしい。あと、今回の構成以外の構成以外のデフォルト値まで調査はしていないのであしからず。
CodePipelineのデプロイステージ
過去のCloudFormationの記事と同様に作っていくことになるが、今回の主な違いはデプロイステージである。アクションプロバイダ(ActionTypeIdのProvider)が今回はCloudFormation
になる。
上記のケースでのアクション構造のリファレンスはここ。
プロパティ | デフォルト値 |
---|---|
ActionMode | 必須 ※リファレンス記載 |
StackName | 必須 ※リファレンス記載 |
Capabilities | 条件により必須 ※リファレンス記載 今回のように ActionMode: CREATE_UPDATE に設定する場合、CAPABILITY_NAMED_IAM, CAPABILITY_AUTO_EXPAND を設定する。 |
ChangeSetName | 条件により必須 ※リファレンス記載 今回のように ActionMode: CREATE_UPDATE に設定する場合、不要 |
RoleArn | 条件により必須 ※リファレンス記載 今回のように ActionMode: CREATE_UPDATE に設定する場合、必須 |
TemplatePath | 条件により必須 ※リファレンス記載 今回のように ActionMode: CREATE_UPDATE に設定する場合、必須 |
OutputFileName | なし |
ParameterOverrides | なし |
TemplateConfiguration | なし |
最小権限にするためのIAM設定
これまでの記事では、毎回「適当にS3にフルアクセスできるような権限でも用意しておいて」なノリで書いていて、最小権限の考え方と外れていたので、少し真面目に考えてみる。
今回、アーティファクトのやり取りのためにS3を新規に作ったり、CodeBuildのためにCloudWatch Logsのロググループを作ったりしているので、それだけを既存のCodeBuildのサービスロールに追加してあげることにしよう。
なお、${Prefix}
, ${CodeBuildPolicyNameSuffix}
, ${CodeBuildLogGroupNameSuffix}
, ${S3BucketName}
, ${CodeBuildRole}
はParametersで事前に設定しておけるようにしておこう。
CODEBUILDIAMPOLYCY:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub ${Prefix}${CodeBuildPolicyNameSuffix}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowS3Bucket
Effect: Allow
Action:
- "s3:GetBucketAcl"
- "s3:GetBucketLocation"
Resource: !GetAtt S3BUCKET.Arn
- Sid: AllowS3Object
Effect: Allow
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:GetObjectVersion"
Resource: !Join [ "/", [ !GetAtt S3BUCKET.Arn, "*" ] ]
- Sid: AllowCloudWatchLogs
Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: !GetAtt CODEBUILDLOGGROUP.Arn
Roles:
- !Sub ${CodeBuildRole}
S3BUCKET:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${S3BucketName}
CODEBUILDLOGGROUP:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${Prefix}${CodeBuildLogGroupNameSuffix}
気を付けなければいけないのが、IAMについては、CodePipelineとの依存関係をCloudFormationが自動で解決してくれないということ。
何も考えずに設定してしまうと、IAMとCodePipelineの作成順序が変わってしまう可能性があり、CodePipeline実行時にまだIAMポリシの設定が完了していなくてエラーになってしまう。
CodePipelineのリソース定義に
PIPELINE:
Type: AWS::CodePipeline::Pipeline
DependsOn: CODEBUILDIAMPOLYCY
Properties:
…
と、忘れず入れておくようにしよう。
CloudFormationテンプレート全体
ちょっと長いけど↓こんな感じになる。
今回は、ParametersのPrefixとS3BucketNameを修正すれば良いようにしたので、
AWSTemplateFormatVersion: "2010-09-09"
Description:
CI/CD Pipeline for Lambda Create
Parameters:
Prefix:
Description: "Project name prefix"
Type: "String"
Default: "LambdaTest"
S3BucketName:
Description: "S3 bucket name for Artifact"
Type: "String"
Default: "lambdatest-artifact-bucket"
PipelineNameSuffix:
Description: "PipelineName on CodePipeline"
Type: "String"
Default: "-Pipeline"
RepositoryNameSuffix:
Description: "RepositoryName on CodeCommit"
Type: "String"
Default: ""
BuildProjectNameSuffix:
Description: "BuildProjectName on CodeBuild"
Type: "String"
Default: "-Build-Project"
StackNameSuffix:
Description: "StackName on CloudFormation(SAM)"
Type: "String"
Default: "-CodePileline-Stack"
CodeBuildRole:
Description: "CodeBuild Service Role"
Type: "String"
Default: "codebuild-LambdaTestProject-service-role"
CodeBuildPolicyNameSuffix:
Description: "IAM Policy Name for CodeBuild"
Type: "String"
Default: "-CodeBuild-Polycy"
CodeBuildLogGroupNameSuffix:
Description: "LogGroup Name for CodeBuild"
Type: "String"
Default: "-CodeBuild-LogGroup"
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: "Project name prefix"
Parameters:
- Prefix
- Label:
default: "S3 Configuration"
Parameters:
- S3BucketName
- Label:
default: "Pipeline Configuration"
Parameters:
- PipelineNameSuffix
- RepositoryNameSuffix
- BuildProjectNameSuffix
- StackNameSuffix
- Label:
default: "IAM Role/Policy Configuration"
Parameters:
- CodeBuildRole
- CodeBuildPolicyNameSuffix
- Label:
default: "Log Configuration"
Parameters:
- CodeBuildLogGroupNameSuffix
Resources:
# ------------------------------------------------------------#
# IAM Policy
# ------------------------------------------------------------#
CODEBUILDIAMPOLYCY:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub ${Prefix}${CodeBuildPolicyNameSuffix}
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowS3Bucket
Effect: Allow
Action:
- "s3:GetBucketAcl"
- "s3:GetBucketLocation"
Resource: !GetAtt S3BUCKET.Arn
- Sid: AllowS3Object
Effect: Allow
Action:
- "s3:PutObject"
- "s3:GetObject"
- "s3:GetObjectVersion"
Resource: !Join [ "/", [ !GetAtt S3BUCKET.Arn, "*" ] ]
- Sid: AllowCloudWatchLogs
Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: !GetAtt CODEBUILDLOGGROUP.Arn
Roles:
- !Sub ${CodeBuildRole}
# ------------------------------------------------------------#
# S3 Bucket
# ------------------------------------------------------------#
S3BUCKET:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${S3BucketName}
# ------------------------------------------------------------#
# Cloud Watch Log Group
# ------------------------------------------------------------#
CODEBUILDLOGGROUP:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub ${Prefix}${CodeBuildLogGroupNameSuffix}
# ------------------------------------------------------------#
# CodeBuild
# ------------------------------------------------------------#
CODEBUILD:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub ${Prefix}${BuildProjectNameSuffix}
Source:
Type: CODEPIPELINE
BuildSpec: buildspec.yml
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
LogsConfig:
CloudWatchLogs:
GroupName: !Sub ${Prefix}${CodeBuildLogGroupNameSuffix}
Status: ENABLED
Cache:
Type: LOCAL
Modes:
- LOCAL_CUSTOM_CACHE
ServiceRole: !Sub arn:aws:iam::${AWS::AccountId}:role/service-role/codebuild-LambdaTestProject-service-role
# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
PIPELINE:
Type: AWS::CodePipeline::Pipeline
DependsOn: CODEBUILDIAMPOLYCY
Properties:
Name: !Sub ${Prefix}${PipelineNameSuffix}
ArtifactStore:
Location: !Ref S3BUCKET
Type: S3
RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/CodePipelineRole
Stages:
- Name: Source
Actions:
- RunOrder: 1
Name: Source
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !Sub ${Prefix}${RepositoryNameSuffix}
BranchName: master
OutputArtifacts:
- Name: SourceArtifact
- Name: Build
Actions:
- RunOrder: 2
Name: Build
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: 1
Configuration:
ProjectName: !Ref CODEBUILD
InputArtifacts:
- Name: SourceArtifact
OutputArtifacts:
- Name: BuildArtifact
- Name: Deploy
Actions:
- RunOrder: 3
Name: Deploy
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
StackName: !Sub ${Prefix}${StackNameSuffix}
ActionMode: CREATE_UPDATE
RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/CloudFormationLambdaPipeline
TemplatePath: BuildArtifact::output-template.yml
Capabilities: CAPABILITY_NAMED_IAM,CAPABILITY_AUTO_EXPAND
InputArtifacts:
- Name: BuildArtifact
↓の部分が、アプリのbuildspec.ymlと連動しなければいけないのがちょっとイケてない……。
また、このテンプレートで作るS3バケットの名前をあらかじめbuildspec.ymlの中で指定しなければいけないのもな……。この辺を、Outputと連動させられるようになると良い感じなのだが。
TemplatePath: BuildArtifact::output-template.yml