はじめに
今回は、CloudFormationスタックを更新するCodePipelineを作成していきます。
以前、作成した構成は、単純にCodeCommitリポジトリのmasterブランチにマージされたら自動でCloudFormationスタックが作成されるという内容でした。
ですが、今回は、この構成に承認プロセスとステージング環境にテストデプロイするという構成で構築していきたいと思います。
以下のドキュメントを参考にしていきます。
構成図
下記の内容を踏襲した構成で検証します。

さっそくCloudFormationで構築!
CloudFormationテンプレートは以下にございます。
ダウンロードしてデスクトップなど任意のフォルダに保存します。
※その他のファイルも後述の手順で使用するためダウンロードしておきます
CloudFormation:YAMLテンプレートのポイント
YAMLテンプレートのポイントを記述します。
- ArtifactStoreBucket: S3バケットを定義します。このバケットは、CodePipelineのアーティファクトを格納するために使用されます。バケット名は、タグ(TagOwner、TagApplication、TagEnv、TagUser)に基づいて自動的に生成されます。バージョニングとパブリックアクセスブロックの設定も含まれています。
- CodePipelineSNSTopic: AWS SNSトピックを定義します。このトピックは、CodePipelineの承認アクションで使用され、通知を送信するためのものです。トピック名も、同様にタグに基づいて自動的に生成されます。
- MyPipeline: AWS CodePipelineパイプラインを定義します。このパイプラインは、ソースステージ、ステージ、および本番ステージの3つのステージから構成されています。各ステージには、ソースの取得、テストステージ(StgStage)、本番デプロイステージ(PrdStage)が含まれます。各アクションには、スタックの作成、変更セットの作成、承認、およびスタックの削除などが含まれています。
Pipeline箇所の詳細は以下の通りです。
- Source Stage:
- ソースコードのリポジトリからソースコードを取得します。
- AWS CodeCommitがプロバイダーとして使用され、mainブランチからのソースコードの変更をトリガーとしてビルドが開始されます。
- Stg Stage:
- ステージング環境(Staging Environment)に対するデプロイメントを処理します。
- CloudFormationを使用して、ステージングスタックを作成、更新、または削除します。
- **
CreateStack
**アクションでは、指定されたCloudFormationテンプレートを使用してステージングスタックを作成します。 - **
ApproveStgStack
**アクションは、ステージングスタックの変更セットを承認するための人間の介入をトリガーします。このアクションには手動の承認が必要です。 - **
DeleteStgStack
**アクションは、ステージングスタックを削除します。このアクションは、本番環境に変更を反映する前にステージング環境をクリーンアップするために使用されます。
- Prd Stage:
- 本番環境(Production Environment)へのデプロイメントを処理します。
- ステージングスタックの変更が承認されると、本番環境に変更が適用されます。
- **
CreateChangeSet
**アクションでは、本番環境の変更セットを作成します。この変更セットは、CloudFormationテンプレートに基づいて本番環境を変更しますが、まだ適用されていません。 - **
ApproveChangeSet
**アクションは、変更セットを承認するための人間の介入をトリガーします。このアクションには手動の承認が必要です。 - **
ExecuteChangeSet
**アクションでは、承認された変更セットが本番環境に適用されます。つまり、本番環境が変更されます。
# ------------------------------ #
# ArtifactStore S3Bucket
# ------------------------------ #
ArtifactStoreBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'artifact-bucket']]
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- ExpirationInDays: !Ref ExpirationInDays
Id: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'retain-rule']]
Status: Enabled
Tags:
- Key: Name
Value: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'artifact-bucket']]
- Key: Application
Value: !Ref TagApplication
- Key: Env
Value: !Ref TagEnv
- Key: Owner
Value: !Ref TagOwner
- Key: User
Value: !Ref TagUser
# ------------------------------ #
# CodePipeline SNSTopic
# ------------------------------ #
CodePipelineSNSTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'approve-topic']]
Subscription:
- Endpoint: !Ref Email
Protocol: email
Tags:
- Key: Name
Value: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'approve-topic']]
- Key: Application
Value: !Ref TagApplication
- Key: Env
Value: !Ref TagEnv
- Key: Owner
Value: !Ref TagOwner
- Key: User
Value: !Ref TagUser
# ------------------------------ #
# CodePipeline
# ------------------------------ #
MyPipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Join ['-', [!Ref 'TagOwner', !Ref 'TagApplication', !Ref 'TagEnv', !Ref 'TagUser', 'cfn-pipeline']]
RoleArn: !GetAtt PipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactStoreBucket
Stages:
# ------------------------------ #
# Source Stage
# ------------------------------ #
- Name: Source
Actions:
- Name: TemplateSource
ActionTypeId:
Category: Source
Owner: AWS
Version: "1"
Provider: CodeCommit
Configuration:
RepositoryName: !Ref CodeCommitRepoName
BranchName: main
OutputArtifacts:
- Name: TemplateSource
Region: !Ref AWS::Region
RunOrder: 1
# ------------------------------ #
# Stg Stage
# ------------------------------ #
- Name: StgStage
Actions:
- Name: CreateStack
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: '1'
InputArtifacts:
- Name: TemplateSource
Configuration:
ActionMode: REPLACE_ON_FAILURE
RoleArn: !GetAtt CFNRole.Arn
StackName: !Ref StgStackName
TemplateConfiguration: !Sub "TemplateSource::${StgStackConfig}"
TemplatePath: !Sub "TemplateSource::${TemplateFileName}"
RunOrder: '1'
- Name: ApproveStgStack
ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: '1'
Configuration:
NotificationArn: !Ref CodePipelineSNSTopic
CustomData: !Sub 'Do you want to create a change set against the production stack and delete the ${StgStackName} stack?'
RunOrder: '2'
- Name: DeleteStgStack
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: '1'
Configuration:
ActionMode: DELETE_ONLY
RoleArn: !GetAtt CFNRole.Arn
StackName: !Ref StgStackName
RunOrder: '3'
# ------------------------------ #
# Production Stage
# ------------------------------ #
- Name: PrdStage
Actions:
- Name: CreateChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: '1'
InputArtifacts:
- Name: TemplateSource
Configuration:
ActionMode: CHANGE_SET_REPLACE
RoleArn: !GetAtt CFNRole.Arn
StackName: !Ref PrdStackName
ChangeSetName: !Ref ChangeSetName
TemplateConfiguration: !Sub "TemplateSource::${PrdStackConfig}"
TemplatePath: !Sub "TemplateSource::${TemplateFileName}"
RunOrder: '1'
- Name: ApproveChangeSet
ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: '1'
Configuration:
NotificationArn: !Ref CodePipelineSNSTopic
CustomData: !Sub 'A new change set was created for the ${PrdStackName} stack. Do you want to implement the changes?'
RunOrder: '2'
- Name: ExecuteChangeSet
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: '1'
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: !Ref ChangeSetName
RoleArn: !GetAtt CFNRole.Arn
StackName: !Ref PrdStackName
RunOrder: '3'
スタックの作成_CLI
- 任意のターミナルソフトを開き、保存しておいたYAMLテンプレートをカレントディレクトリに配置します。 teststackフォルダ内のファイルは、後ほど
approvetest-codecommit.yml
で作成したリポジトリ内にpushします。
$ ls -R
approvetest-codecommit.yml test-stack
approvetest-codepipeline.yml
./test-stack:
prd-stack-conf.json test-s3.yml
stg-stack-conf.json
- CodeCommitリポジトリの作成:以下のコマンドを実行します。
aws cloudformation create-stack --region ap-northeast-1 --stack-name approvetest-codecommit --template-body file://approvetest-codecommit.yml
- CodePipelineの作成:以下のコマンドを実行します。
aws cloudformation create-stack --region ap-northeast-1 --stack-name approvetest-codepipeline --template-body file://approvetest-codepipeline.yml --capabilities CAPABILITY_NAMED_IAM
スタックの作成_GUI
マネジメントコンソールでスタックを作成する手順は、以下の通りです。リンクにも詳細がございます。
- CloudFormation画面を開きスタックの作成>新しいソースを使用(標準)を選択
- テンプレートの準備完了>テンプレートファイルのアップロードを選択して、保存したymlを選択
- スタックに任意の名称を入力して、パラメータは環境に合わせて変更>次へを選択。※後述の手順に従う場合、デフォルト値でOK
- スタックオプションは特に変更せず、次へを選択
- レビュー画面を確認して送信を選択
動作検証
実際に、ソースコードをpushして検証していきます。
コードをmainブランチにpushする
下記の順で、コマンドを実行して、以下3つのファイルをmainブランチにpushしました。
./test-stack: prd-stack-conf.json test-s3.yml stg-stack-conf.json
$ git clone {リポジトリ名}
$ cd {リポジトリ名}
〜カレントディレクトリに必要なファイル3つを入れる〜
$ git status
$ git add .
$ git commit -m "1st commit"
$ git push origin main
ソースステージが成功しました。

続いて、TestStageでテスト用スタックが作成されました。

SNSトピックのサブスクリプションに登録したメールの宛先に下記のようなメールが届くので、リンクに飛んで承認します。

レビューを選択します。

よい感じのコメントをつけて、送信を選択します。
(レビュアーのIAMユーザー名もパイプラインの実績に残るため、実務では、適切な文言をご使用ください。。!!)

変更セットが作成されて同様にメールが届くため、同様にレビューして送信します。
今回は、作成されたS3バケットのタグをstg→prdに変更していきます。

ステージングテスト用バケットが削除され、本番用バケットが作成されました。

リソースのタグも本番用のjsonでパラメータを受け取り、正しく変更されていることを確認しました。

まとめ
今回は、承認プロセスを追加したCloudFormationスタック用のCodePipelineを作成しました。
今回のステージングテストは単純作成でしたが、Lambdaを作成してテストコードを書くなど汎用的に使用していきたいと思います。
最後までご覧いただきありがとうございました。
コメント