CodePipelineでCloudFormation用CI/CD環境を構築する
以下のAWS公式ページで、CodePipelineを使用したCloudFormation用のCI/CD環境を構築する方法が取り上げられています。
本ページでは、上記のページを参考にして構築を行い、動作を確認します。
構築する環境
CodePipelineでパイプラインを構成します。
パイプラインは以下の流れで動作します。
まずCodeCommitです。
CodeCommitはCodePipelineのソースステージを担当します。
Gitリポジトリとして使用します。
次にCloudFormationです。
このCloudFormationの働きは、テスト用スタックを構築することです。
スタック作成後、承認アクションとして、SNSでメール通知します。
メール通知を受けたユーザは、パイプラインのページにアクセスし、承認ボタンを押すとパイプラインが再開されます。
最後に再びCloudFormationです。
このCloudFormationの働きは、本番環境用スタックおよび変更セットを作成することです。
変更セット作成後、承認アクションとして、再びSNSでメール通知します。
承認ボタンが押されますと、パイプラインが再開され、変更セットから本番スタックが作成・更新されます。
CodePipelineが開始されるきっかけですが、CodeCommitへのプッシュを条件とします。
具体的には、EventBridgeに上記を満たすルールを用意します。
テストスタック・本番スタックで作成するリソースはLambda関数とします。
合わせて関数用のIAMロールも作成します。
CloudFormationテンプレートファイル
上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。
https://github.com/awstut-an-r/awstut-fa/tree/main/117
テンプレートファイルのポイント解説
本ページは、CodePipelineを使って、CloudFormation用のCI/CD環境を構築することを目的としています。
CodePipelineに関する基本的な事項については、以下のページをご確認ください。
CodePipeline
Resources:
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
ArtifactStore:
Location: !Ref BucketName
Type: S3
Name: !Ref Prefix
RoleArn: !GetAtt CodePipelineRole.Arn
Stages:
- Actions:
- ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
BranchName: !Ref BranchName
OutputArtifactFormat: CODE_ZIP
PollForSourceChanges: false
RepositoryName: !GetAtt CodeCommitRepository.Name
Name: SourceAction
OutputArtifacts:
- Name: !Ref PipelineSourceArtifact
Region: !Ref AWS::Region
RunOrder: 1
Name: CodeCommitSource
- Actions:
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: REPLACE_ON_FAILURE
Capabilities: CAPABILITY_IAM
RoleArn: !GetAtt CloudFormationRole.Arn
StackName: !Ref TestStackName
TemplateConfiguration: !Sub "${PipelineSourceArtifact}::${TestStackConfig}"
TemplatePath: !Sub "${PipelineSourceArtifact}::${TemplateFileName}"
InputArtifacts:
- Name: !Ref PipelineSourceArtifact
Name: CreateTestStack
RunOrder: 1
- ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: 1
Configuration:
CustomData: !Sub "Do you want to create a change set against the production stack and delete the ${TestStackName} stack?"
NotificationArn: !Ref TopicArn
Name: ApproveTestStack
RunOrder: 2
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: DELETE_ONLY
RoleArn: !GetAtt CloudFormationRole.Arn
StackName: !Ref TestStackName
Name: DeleteTestStack
RunOrder: 3
Name: DeployTestStack
- Actions:
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_REPLACE
Capabilities: CAPABILITY_IAM
ChangeSetName: !Ref ChangeSetName
RoleArn: !GetAtt CloudFormationRole.Arn
StackName: !Ref ProdStackName
TemplateConfiguration: !Sub "${PipelineSourceArtifact}::${ProdStackConfig}"
TemplatePath: !Sub "${PipelineSourceArtifact}::${TemplateFileName}"
InputArtifacts:
- Name: !Ref PipelineSourceArtifact
Name: CreateProdStack
RunOrder: 1
- ActionTypeId:
Category: Approval
Owner: AWS
Provider: Manual
Version: 1
Configuration:
CustomData: !Sub "A new change set was created for the ${ProdStackName} stack. Do you want to implement the changes?"
NotificationArn: !Ref TopicArn
Name: ApproveChangeSet
RunOrder: 2
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: CloudFormation
Version: 1
Configuration:
ActionMode: CHANGE_SET_EXECUTE
ChangeSetName: !Ref ChangeSetName
RoleArn: !GetAtt CloudFormationRole.Arn
StackName: !Ref ProdStackName
Name: ExecuteChangeSet
RunOrder: 3
Name: DeployProdStack
Code language: YAML (yaml)
基本的な構成は以下のページを参考にしています。
https://s3.amazonaws.com/cloudformation-examples/user-guide/continuous-deployment/basic-pipeline.yml
パイプラインを構成する各ステージを確認します。
ステージ1
1つ目はソースステージです。
上記のページで紹介されているコードでは、ソースにS3バケットが設定されています。
本ページでは、ソースにCodeCommitを指定します。
ステージ2
2つ目はテストスタックを構築するステージで、3つのアクションから構成されます。
1つ目のアクションは、CloudFormationでテストスタックを構築するアクションです。
設定方法については、AWS公式サイトで以下の通り説明されています。
ステージのアクションで CloudFormation をプロバイダーとして指定する場合は、Configuration プロパティ内で次のプロパティを定義します。AWS CLI、CodePipeline API、または AWS CloudFormation テンプレートの場合は、JSON オブジェクトを使用します。
設定プロパティ (JSON オブジェクト)
ポイントとなるパラメータを取り上げます。
スタックを更新・新規作成する場合、ActionModeプロパティに「REPLACE_ON_FAILURE」を指定します。
このオプションについては、以下の通りに説明されています。
REPLACE_ON_FAILURE は、指定されたスタックが存在しない場合、スタックを作成します。スタックが存在しており、失敗状態の場合 (ROLLBACK_COMPLETE、ROLLBACK_FAILED、CREATE_FAILED、DELETE_FAILED または UPDATE_ROLLBACK_FAILED として報告されている場合)、AWS CloudFormation はそのスタックを削除して新しいスタックを作成します。スタックが失敗状態ではない場合は、AWS CloudFormation はそれを更新します。失敗したスタックをリカバリーまたはトラブルシューティングせずに自動的に置き換えるには、このアクションを使用します。通常、このモードはテスト用に選択されます。
設定プロパティ (JSON オブジェクト)
TemplatePathプロパティで、スタックの素ととなるテンプレートファイルを指定します。
記法は以下の通りです。
Artifactname::TemplateFileName
設定プロパティ (JSON オブジェクト)
上記に従い、テストスタック用のテンプレートファイルを指定します。
TemplateConfigurationプロパティで、テストスタック作成用のテンプレート設定ファイルを指定することができます。
記法は以下の通りです。
Artifactname::TemplateConfigurationFileName
設定プロパティ (JSON オブジェクト)
上記に従い、テストスタック用のコンフィギュレーションファイルを指定します。
2つ目のアクションは承認アクションです。
SNSトピックで指定したメールアドレス宛に、承認メールを送付します。
メールの受信者はテストスタックの内容を確認し、問題なければ承認します。
3つ目のアクションはテストスタックを削除するアクションです。
ActionModeプロパティで、「DELETE_ONLY」を指定します。
こちらのオプションについては、以下の通りに説明されています。
DELETE_ONLY は、スタックを削除します。存在しないスタックを指定した場合は、アクションはスタックを削除せずに正常に終了します。
設定プロパティ (JSON オブジェクト)
ステージ3
3つ目は本番スタックを構築するステージで、3つのアクションから構成されます。
1つ目のアクションは、CloudFormationで本番スタック用の変更セットを構築するアクションです。
ポイントはActionModeプロパティでして、「CHANGE_SET_REPLACE」を指定します。
こちらのオプションについては、以下の通りに説明されています。
CHANGE_SET_REPLACE は、変更セットが存在しない場合、指定されたスタック名とテンプレートに基づいて変更セットを作成します。変更セットが存在する場合は、AWS CloudFormation はそれを削除して新しいものを作成します。
設定プロパティ (JSON オブジェクト)
変更セットに関する詳細については、以下のページをご確認ください。
TemplatePath・TemplateConfigurationプロパティに、それぞれ本番スタック用のテンプレートファイル、本番スタック作成用のテンプレート設定ファイルを指定します。
2つ目のアクションは承認アクションです。
SNSトピックで指定したメールアドレス宛に、承認メールを送付します。
メールの受信者は本番スタック用の変更セットの内容を確認し、問題なければ承認します。
3つ目のアクションは、CloudFormationを使用して、本番スタックを作成するアクションです。
先ほど作成した変更セットから本番スタックを作成します。
ActionModeプロパティに「CHANGE_SET_EXECUTE」を指定します。
こちらのオプションについては、以下の通りに説明されています。
CHANGE_SET_EXECUTE は変更セットを実行します。
設定プロパティ (JSON オブジェクト)
IAMロール
以下の2つのIAMロールを作成します。
- CodePipeline実行用IAMロール
- CloudFormation実行用IAMロール
CodePipeline実行用のIAMロールを確認します。
Resources:
CodePipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: PipelinePolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- codecommit:CancelUploadArchive
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:GetRepository
- codecommit:GetUploadArchiveStatus
- codecommit:UploadArchive
Resource:
- !GetAtt CodeCommitRepository.Arn
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
- s3:GetBucketAcl
- s3:GetBucketLocation
Resource:
- !Sub "arn:aws:s3:::${BucketName}"
- !Sub "arn:aws:s3:::${BucketName}/*"
- Effect: Allow
Action:
- sns:Publish
Resource:
- !Ref TopicArn
- Effect: Allow
Action:
- cloudformation:CreateStack
- cloudformation:DescribeStacks
- cloudformation:DeleteStack
- cloudformation:UpdateStack
- cloudformation:CreateChangeSet
- cloudformation:ExecuteChangeSet
- cloudformation:DeleteChangeSet
- cloudformation:DescribeChangeSet
- cloudformation:SetStackPolicy
- iam:PassRole
Resource: "*"
Code language: YAML (yaml)
パイプラインを構成するリソース(CodeCommit、S3、SNS、CloudFormation)に関する権限を与えます。
次にCloudFormation実行用IAMロールを確認します。
Resources:
CloudFormationRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- cloudformation.amazonaws.com
Action:
- sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: CloudFormationDeployPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- iam:*
- lambda:*
Resource: "*"
Code language: YAML (yaml)
CloudFormationを通じて作成するリソース(Lambda関数、関数用IAMロール、CloudWatch Logs)に関する権限を与えます。
CloudFormation関係のファイル
CodePipeline内のCloudFormationを実行するためのファイルを確認します。
テンプレートファイル
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Prefix:
Type: String
Default: fa-117-sample-lambda
Environment:
Type: String
Default: test
Handler:
Type: String
Default: index.lambda_handler
MemorySize:
Type: Number
Default: 128
Runtime:
Type: String
Default: python3.8
Timeout:
Type: Number
Default: 5
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
def lambda_handler(event, context):
print('sample lambda')
FunctionName: !Sub "${Prefix}-${Environment}-Function"
Handler: !Ref Handler
MemorySize: !Ref MemorySize
Runtime: !Ref Runtime
Role: !GetAtt LambdaRole.Arn
LambdaRole:
Type: AWS::IAM::Role
DeletionPolicy: Delete
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Code language: YAML (yaml)
Lambda関数およびIAMロールを定義します。
関数名(FunctionName)とメモリサイズ(MemorySize)がポイントです。
それぞれパラメータを埋め込む形で指定しますが、これらの値は後述のテンプレート設定ファイルによって、テストスタック・本番スタックで異なる値を設定します。
テストスタック用テンプレート設定ファイル
{
"Parameters" : {
"Environment": "test",
"MemorySize": "128"
}
}
Code language: JSON / JSON with Comments (json)
2つのパラメータを設定します。
特にメモリサイズがポイントです。
128(MB)を指定します。
本番スタック用テンプレートファイル
{
"Parameters" : {
"Environment": "prod",
"MemorySize": "512"
}
}
Code language: JSON / JSON with Comments (json)
こちらも2つのパラメータを設定します。
メモリサイズは512(MB)を指定します。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成する
CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- SNSトピック:fa-117
- CodeCommit:fa-117
- CodePipeline:fa-117
メールアドレスの認証
SNSトピックのサブスクライバーとしてメールアドレスを指定した場合、そのメールアドレスを認証する必要があります。
指定したメールアドレスに、以下のような認証メールが送られてきます。
「Confirm subscription」を押下して、認証を進めます。
上記のページが表示されて、認証が完了したことがわかります。
リソース確認
AWS Management Consoleから各リソースを確認します。
まずSNSトピックを確認します。
SNSトピックのサブスクライバーとして、メールアドレスが指定されています。
CodePipelineを確認します。
パイプラインの実行に失敗しています。
これはCloudFormationスタック作成時に、CodeCommitが作成されたことがきっかけとして、パイプラインが実行されたためです。
現時点ではCodeCommitにコードをプッシュしていないため、パイプライン実行の過程でエラーが発生しました。
作成されているステージに注目します。
DeployTestStackとDeployTestStackという名前で、CloudFormationスタックを作成するステージが用意されています。
各ステージの内容を見ます。
前者はテストスタック作成後、承認を得ると、そのテストスタックを削除します。
後者は変更セット作成後、承認を得ると、本番スタックを作成・更新します。
動作確認
準備が整いましたので、CodeCommitにコードをプッシュします。
まずCodeCommitリポジトリをプルします。
$ git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/fa-117
Cloning into 'fa-117'...
warning: You appear to have cloned an empty repository.
Code language: Bash (bash)
空のリポジトリがプルされました。
リポジトリに3つのファイル(sample-lambda.yaml、test-stack-configuration.json、prod-stack-configuration.json)を加えます。
$ ls -al
total 12
drwxrwxr-x 3 ec2-user ec2-user 118 Jan 18 12:33 .
drwxrwxr-x 4 ec2-user ec2-user 131 Jan 18 12:32 ..
drwxrwxr-x 7 ec2-user ec2-user 119 Jan 18 12:32 .git
-rw-rw-r-- 1 ec2-user ec2-user 94 Jan 18 11:50 prod-stack-configuration.json
-rw-rw-r-- 1 ec2-user ec2-user 1667 Jan 18 11:40 sample-lambda.yaml
-rw-rw-r-- 1 ec2-user ec2-user 93 Jan 18 11:48 test-stack-configuration.json
Code language: Bash (bash)
3ファイルをプッシュします。
$ git add .
$ git commit -m 'first commit'
...
3 files changed, 92 insertions(+)
create mode 100644 prod-stack-configuration.json
create mode 100644 sample-lambda.yaml
create mode 100644 test-stack-configuration.json
$ git push
...
* [new branch] master -> master
Code language: Bash (bash)
正常にプッシュできました。
これでパイプラインが開始されるはずです。
しばらく待つと、以下のメールが届きました。
テストスタックの内容を承認に関する内容です。
パイプラインを確認します。
テストスタック作成が成功し、認証アクションで一時停止しています。
作成されたテストスタックを確認します。
確かにテストスタックが作成されています。
スタック内のLambda関数を確認します。
確かにLambda関数が作成されています。
関数名が「fa-117-sample-lambda-test-Function」、メモリサイズが「128 MB」とありますので、テストスタック用のテンプレート設定ファイルが読み込まれた上で、スタックが作成されたことがわかります。
正常にテストスタックが作成されたことが確認できましたので、パイプラインの承認アクションを行います。
「Approve」を押下すると、一時停止していたパイプラインが再開されます。
改めてテストスタックを確認します。
パイプラインが再開されたことによって、自動的にテストスタックが削除されました。
再びしばらく待機すると、以下のメールが届きました。
メールの内容は本番スタックの変更セットの承認に関するものです。
パイプラインを確認します。
本番スタック用の変更セットの作成が成功し、認証アクションで一時停止しています。
作成された変更セットを確認します。
変更セットの内容を見ると、Lambda関数と関数用のIAMロールが新規作成されることが読み取れます。
正常に変更セットが作成されたことが確認できましたので、パイプラインの承認アクションを行います。
「Approve」を押下すると、一時停止していたパイプラインが再開されます。
パイプラインが最後まで到達し、正常に終了しました。
変更セットから作成された本番スタックを確認します。
確かに本番スタックが作成されています。
スタック内に2つのリソース(Lambda関数、IAMロール)が作成されていることもわかります。
最後にLambda関数を確認します。
確かにLambda関数が作成されています。
関数名が「fa-117-sample-lambda-prod-Function」、メモリサイズが「512 MB」とありますので、本番スタック用のテンプレート設定ファイルが読み込まれた上で、スタックが作成されたことがわかります。
以上の通り、CodePipelineを使用したCloudFormation用のCI/CD環境が構築できました。
まとめ
CodePipelineを使用したCloudFormation用のCI/CD環境を構築する方法をご紹介しました。