LoginSignup
0
0

【AWS CDK (Python)】 LambdaのCI/CD環境をIaCで作る(Layer対応版)

Posted at

はじめに

本記事は、私自身の備忘録を兼ねてAWS CDKをこれから始める方の一助になればと思い、AWS CDKの使い方等をまとめたものです。
前回、AWS CDKでLambdaのCI/CD環境を作成しましたが、今回は、その構成を少し変更してLambda LayerのCI/CDにも対応するようにしたいと思います。
前回の記事は、こちら(【AWS CDK (Python)】 LambdaのCI/CD環境をIaCで作る)を参照してください。
なお、本記事は私自身の経験を基に記載していますが、間違いがあったらすみません。

環境

本記事は以下の環境を使用して記載しています。

  • AWS Cloud9
  • AWS CDK:2.80.0
  • Python: 3.10.11
  • Node.js: 16.20.0

また、以下の記事に基づいてAWS CDKの環境を作成しています。

LambdaのCI/CD環境をIaCで作る(Layer対応版)

Layerに対応しますが、構成としてはほぼ変わりありません。Lambdaと同様にCodeCommitにソースコードとbuildspecファイルを用意し、CDKでSAM Templateファイルを作成、パイプラインでBuild、Deployします。

Layer対応版のCDKのソースコードは以下のようになりました。

CDKコード全体
app.py
#!/usr/bin/env python3
import os
import aws_cdk as cdk
from cdk_app.cdk_lambda_cicd_stack import CdkAppStack

app = cdk.App()

CdkAppStack = CdkAppStack(app, "CdkAppStack",
    )

app.synth()
cdk_app/cdk_lambda_cicd_stack.py
from aws_cdk import (
    Stack,
    aws_s3 as s3,
    RemovalPolicy,
    aws_s3_deployment as s3deploy,
    aws_iam as iam,
    aws_codebuild as codebuild,
    aws_codepipeline as codepipeline,
    aws_events as events
)
from constructs import Construct
import json
import os
import zipfile

# S3バケットを作成する共通関数
def CreateBucket(scope, bucket_id):

    cfn_bucket = s3.CfnBucket(
        scope,
        bucket_id,
        versioning_configuration = s3.CfnBucket.VersioningConfigurationProperty(
            status = "Enabled"
        ),
        bucket_name = "cdk-test-{0}-c3hw2bm4".format(bucket_id)
    )
    return cfn_bucket

# CodeBuild用のIAMロールを作成
def CreateBuildIAMRole(scope, cfn_pkg_s3bucket, artifact_s3bucket):

    assume_role_policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "sts:AssumeRole",
                "Effect": "Allow",
                "Principal": {
                    "Service": "codebuild.amazonaws.com"
                }
            }
        ]
    }

    policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Resource": [
                    "arn:aws:s3:::{0}/*".format(cfn_pkg_s3bucket.bucket_name),
                    "arn:aws:s3:::{0}".format(cfn_pkg_s3bucket.bucket_name),
                    "arn:aws:s3:::{0}/*".format(artifact_s3bucket.bucket_name),
                    "arn:aws:s3:::{0}".format(artifact_s3bucket.bucket_name)
                ],
                "Action": [
                    "s3:PutObject",
                    "s3:GetObject",
                    "s3:GetObjectVersion",
                    "s3:GetBucketAcl",
                    "s3:GetBucketLocation"
                ]
            },
            {
                "Effect": "Allow",
                "Resource": "*",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ]
            },
            {
                "Action": [
                    "iam:PassRole"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "cloudformation:*"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }

    cfn_role = iam.CfnRole(
            scope,
            "codebuild-role",
            assume_role_policy_document=assume_role_policy_document,
            policies=[
                iam.CfnRole.PolicyProperty(
                    policy_document=policy_document,
                    policy_name="codebuild-policy"
                )
            ],
            role_name="codebuild-role"
        )
    
    return cfn_role

# CodePipeline用のIAMロールを作成
def CreatePipelineIAMRole(scope, source_s3bucket, artifact_s3bucket):

    assume_role_policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "sts:AssumeRole",
                "Effect": "Allow",
                "Principal": {
                    "Service": "codepipeline.amazonaws.com"
                }
            }
        ]
    }

    policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "codecommit:CancelUploadArchive",
                    "codecommit:GetBranch",
                    "codecommit:GetCommit",
                    "codecommit:GetUploadArchiveStatus",
                    "codecommit:UploadArchive"
                ],
                "Resource": "*"
            },
            {
                "Action": [
                    "codebuild:BatchGetBuilds",
                    "codebuild:StartBuild",
                    "codebuild:BatchGetBuildBatches",
                    "codebuild:StartBuildBatch"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "s3:Get*",
                    "s3:ListBucket"
                ],
                "Resource": [
                    "arn:aws:s3:::{0}/*".format(source_s3bucket.bucket_name),
                    "arn:aws:s3:::{0}".format(source_s3bucket.bucket_name)
                ],
                "Effect": "Allow"
            },
            {
                "Action": [
                    "s3:Get*",
                    "s3:Put*",
                    "s3:ListBucket"
                ],
                "Resource": [
                    "arn:aws:s3:::{0}/*".format(artifact_s3bucket.bucket_name),
                    "arn:aws:s3:::{0}".format(artifact_s3bucket.bucket_name)
                ],
                "Effect": "Allow"
            },
            {
                "Action": [
                    "cloudformation:DescribeStacks",
                    "cloudformation:DescribeChangeSet",
                    "cloudformation:CreateChangeSet",
                    "cloudformation:ExecuteChangeSet",
                    "cloudformation:DeleteChangeSet"
                ],
                "Resource": [
                    "arn:aws:cloudformation:{0}:{1}:stack/lambda-stack-*".format("ap-northeast-1", os.environ["CDK_DEFAULT_ACCOUNT"])
                ],
                "Effect": "Allow"
            },
            {
                "Action": [
                    "cloudwatch:*"
                ],
                "Resource": "*",
                "Effect": "Allow"
            },
            {
                "Action": [
                    "iam:PassRole"
                ],
                "Resource": "*",
                "Effect": "Allow"
            }
        ]
    }

    cfn_role = iam.CfnRole(
            scope,
            "codepipeline-role",
            assume_role_policy_document=assume_role_policy_document,
            policies=[
                iam.CfnRole.PolicyProperty(
                    policy_document=policy_document,
                    policy_name="codepipeline-policy"
                )
            ],
            role_name="codepipeline-role"
        )
    
    return cfn_role

# LambdaFunction用のIAMロールを作成
def CreateLambdaIAMRole(scope):

    assume_role_policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "sts:AssumeRole",
                "Effect": "Allow",
                "Principal": {
                    "Service": "lambda.amazonaws.com"
                }
            }
        ]
    }

    cfn_role = iam.CfnRole(
            scope,
            "lambda-role",
            assume_role_policy_document=assume_role_policy_document,
            managed_policy_arns=[
                "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
            ],
            role_name="lambda-role"
        )
    
    return cfn_role

# EvantBridge用のIAMロールを作成
def CreateExecPipelineIAMRole(scope, pipeline_arn):

    assume_role_policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Action": "sts:AssumeRole",
                "Effect": "Allow",
                "Principal": {
                    "Service": "events.amazonaws.com"
                }
            }
        ]
    }

    policy_document = {
        "Version": "2012-10-17",
        "Statement": [
            {
                    "Effect": "Allow",
                    "Action": "codepipeline:StartPipelineExecution",
                    "Resource": pipeline_arn
                }
        ]
    }

    cfn_role = iam.CfnRole(
            scope,
            "exec-codepipeline-role",
            assume_role_policy_document=assume_role_policy_document,
            policies=[
                iam.CfnRole.PolicyProperty(
                    policy_document=policy_document,
                    policy_name="exec-codepipeline-policy"
                )
            ],
            role_name="exec-codepipeline-role"
        )
    
    return cfn_role

# ↓↓ Layer対応版 追加コード
# LambdaLayer作成用のSAM Templateファイルを作成
def EditSAMLayerTemplate(layer_id):

    sam_template_data = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Transform": "AWS::Serverless-2016-10-31",
        "Description": "An AWS Serverless Specification template describing your layer.",
        "Resources": {
            "TestLambdLayer": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "",
                    "ContentUri": "",
                    "CompatibleRuntimes": ["python3.10"]
                }
            },
            "TestLayerArnParameter": {
                "Type": "AWS::SSM::Parameter",
                "Properties": {
                    "Name": "",
                    "Type": "String",
                    "Value": { "Ref" : "TestLambdLayer" }
                }
            }
        }
    }

    sam_template_data["Resources"]["TestLambdLayer"]["Properties"]["ContentUri"] = layer_id
    sam_template_data["Resources"]["TestLambdLayer"]["Properties"]["LayerName"] = layer_id
    sam_template_data["Resources"]["TestLayerArnParameter"]["Properties"]["Name"] =\
        "/test/layer_arn/{0}".format(layer_id)

    return sam_template_data
# ↑↑ Layer対応版 追加コード


# LambdaFunction作成用のSAM Templateファイルを作成
# ↓↓ Layer対応版 変更コード
def EditSAMTemplate(lambda_id, lambda_role, layer_id):
# def EditSAMTemplate(lambda_id, lambda_role):
# ↑↑ Layer対応版 変更コード

    sam_template_data = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Transform": "AWS::Serverless-2016-10-31",
        "Resources": {
            "TestLambdaFunction": {
                "Type": "AWS::Serverless::Function",
                "Properties": {
                    "Handler": "lambda_function.lambda_handler",
                    "Runtime": "python3.10",
                    "CodeUri": "",
                    "Description": "",
                    "FunctionName": "",
                    "AutoPublishAlias": "",
# ↓↓ Layer対応版 追加コード
                    "AutoPublishAliasAllProperties": true,
# ↑↑ Layer対応版 追加コード
                    "MemorySize": 128,
                    "Timeout": 3,
                    "Role": "",
                    "Environment": {
                        "Variables": {
                        }
                    },
                    "DeploymentPreference": {
                        "Enabled": True,
                        "Type": "AllAtOnce"
                    }
                }
            }
        }
    }
    
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["CodeUri"] = lambda_id
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["FunctionName"] = lambda_id
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["AutoPublishAlias"] = "Dev"
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Role"] =\
        "arn:aws:iam::{0}:role/{1}".format(os.environ["CDK_DEFAULT_ACCOUNT"], lambda_role.role_name)
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Environment"]["Variables"] =\
        {"test_variables": "test"}
# ↓↓ Layer対応版 追加コード
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Environment"]["Variables"]["layer"] =\
        "{{resolve:ssm:" + "/test/layer_arn/{0}".format(layer_id) + "}}"
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Layers"] =\
        ["{{resolve:ssm:" + "/test/layer_arn/{0}".format(layer_id) + "}}"]
# ↑↑ Layer対応版 追加コード

    return sam_template_data


# CodeCommitリポジトリの変更を検知するEventBridgeルールの作成
def CodecommitEvents(scope, rep_name, pipeline_arn, exec_pipeline_role):

    event_pattern = {
        "source": ["aws.codecommit"],
        "detail-type": ["CodeCommit Repository State Change"],
        "resources": [
            "arn:aws:codecommit:{0}:{1}:{2}".format(
                "ap-northeast-1", os.environ["CDK_DEFAULT_ACCOUNT"], rep_name)
        ],
        "detail": {
            "event": ["referenceCreated", "referenceUpdated"],
            "referenceType": ["branch"],
            "referenceName": ["main"]
        }
    }

    cfn_rule = events.CfnRule(
        scope,
        "change-repository-events",
        event_pattern=event_pattern,
        name="change-repository-events",
        state="ENABLED",
        targets=[
            events.CfnRule.TargetProperty(
                arn=pipeline_arn,
                id="change-repository-events",
                role_arn=exec_pipeline_role.attr_arn,
            )
        ]
    )

class CdkAppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_id = "TestFunc"
# ↓↓ Layer対応版 追加コード
        layer_id = "TestLayer"
# ↑↑ Layer対応版 追加コード
        repository_name = "test-lambda-rep"

        # buildspec、SAM TemplateファイルをアップロードするS3バケットを作成
        source_s3bucket = CreateBucket(self, "source-bucket")
        source_s3bucket_l2 =  s3.Bucket.from_bucket_arn(
            self, "bucket_l1tol2-{0}".format(source_s3bucket.bucket_name), source_s3bucket.attr_arn)
        # cloudformation packageのパッケージファイルをアップロードするS3バケットを作成
        cfn_pkg_s3bucket = CreateBucket(self, "cfn-pkg")
        # アーティファクトバケットを作成
        artifact_s3bucket = CreateBucket(self, "artifact-bucket")

        # CodeBuild用のIAMロールの作成
        build_role = CreateBuildIAMRole(self, cfn_pkg_s3bucket, artifact_s3bucket)
        # CodePipeline用のIAMロールの作成
        pipeline_role = CreatePipelineIAMRole(self, source_s3bucket, artifact_s3bucket)
        # LambdaFunction用のIAMロールの作成
        lambda_role = CreateLambdaIAMRole(self)

        zip_dir = "./cdk_app/work/"
        zip_file = "template_{0}.zip".format(lambda_id)
        zip_path = zip_dir + zip_file
        template_file_name = "sam-template"
        # SAM Templateファイルの編集
        sam_template_data = EditSAMTemplate(lambda_id, lambda_role, layer_id)
        # sam_template_data = EditSAMTemplate(lambda_id, lambda_role)
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_STORED) as z:
            z.writestr(template_file_name + ".json", json.dumps(sam_template_data, indent=4))

# ↓↓ Layer対応版 追加コード
        layer_zip_file = "template_{0}.zip".format(layer_id)
        layer_zip_path = zip_dir + layer_zip_file
        layer_template_file_name = "sam-layer-template"
        # SAM Templateファイルの編集
        sam_layer_template_data = EditSAMLayerTemplate(layer_id)
        with zipfile.ZipFile(layer_zip_path, 'w', zipfile.ZIP_STORED) as z:
            z.writestr(layer_template_file_name + ".json", json.dumps(sam_layer_template_data, indent=4))
# ↑↑ Layer対応版 追加コード

        # 作成したSAM TemplateファイルをS3へアップロード
        s3_deploy = s3deploy.BucketDeployment(
            self,
            "TemplateFileUploadS3",
            sources=[s3deploy.Source.asset(zip_dir)],
            destination_bucket=source_s3bucket_l2,
        )


        # Buildプロジェクトの作成
        cfn_project = codebuild.CfnProject(
            self, 
            "build-lambda-project",
            artifacts=codebuild.CfnProject.ArtifactsProperty(
                type="CODEPIPELINE",
            ),
            environment=codebuild.CfnProject.EnvironmentProperty(
                compute_type="BUILD_GENERAL1_SMALL",
                image="aws/codebuild/standard:7.0",
                type="LINUX_CONTAINER",
                privileged_mode=False,
                environment_variables=[
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="TEMPLATE_FILE_NAME",
                        value=template_file_name,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="CFN_PKG_BUCKET",
                        value=cfn_pkg_s3bucket.bucket_name,
                    )
                ],
            ),
            service_role=build_role.attr_arn,
            source=codebuild.CfnProject.SourceProperty(
                type="CODEPIPELINE",
                build_spec="buildspec_{0}.yaml".format(lambda_id)
            ),
            logs_config=codebuild.CfnProject.LogsConfigProperty(
                cloud_watch_logs=codebuild.CfnProject.CloudWatchLogsConfigProperty(
                    status="ENABLED",
                ),
            ),
            name="build_lambda_project",
        )

# ↓↓ Layer対応版 追加コード
        # Buildプロジェクト(Layer)の作成
        cfn_layer_project = codebuild.CfnProject(
            self, 
            "build-layer-project",
            artifacts=codebuild.CfnProject.ArtifactsProperty(
                type="CODEPIPELINE",
            ),
            environment=codebuild.CfnProject.EnvironmentProperty(
                compute_type="BUILD_GENERAL1_SMALL",
                image="aws/codebuild/standard:7.0",
                type="LINUX_CONTAINER",
                privileged_mode=False,
                environment_variables=[
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="TEMPLATE_FILE_NAME",
                        value=layer_template_file_name,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="CFN_PKG_BUCKET",
                        value=cfn_pkg_s3bucket.bucket_name,
                    )
                ],
            ),
            service_role=build_role.attr_arn,
            source=codebuild.CfnProject.SourceProperty(
                type="CODEPIPELINE",
                build_spec="buildspec_{0}.yaml".format(layer_id)
            ),
            logs_config=codebuild.CfnProject.LogsConfigProperty(
                cloud_watch_logs=codebuild.CfnProject.CloudWatchLogsConfigProperty(
                    status="ENABLED",
                ),
            ),
            name="build_layer_project",
        )
# ↑↑ Layer対応版 追加コード

        # CodePipelineの作成
        source_actions = []
        # Sourceアクション(CodeCommit(Lambdaコード、buildspec))の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="CodeCommit",
                    version="1"
                ),
                name="Source_codecommit",
                configuration={
                    "RepositoryName": repository_name,
                    "BranchName": "main",
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_codecommit",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_codecommit"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )

        # Sourceアクション(SAM Template)の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="S3",
                    version="1"
                ),
                name="Source_template",
                configuration={
                    "S3Bucket": source_s3bucket.bucket_name,
                    "S3ObjectKey": zip_file,
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_Template",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_template"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↓↓ Layer対応版 追加コード
        # Sourceアクション(LayerのSAM Template)の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="S3",
                    version="1"
                ),
                name="Source_layer_template",
                configuration={
                    "S3Bucket": source_s3bucket.bucket_name,
                    "S3ObjectKey": layer_zip_file,
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_LayerTemplate",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_layer_template"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↑↑ Layer対応版 追加コード

        # Buildアクションの作成
        build_actions = []
        build_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Build",
                    owner="AWS",
                    provider="CodeBuild",
                    version="1"
                ),
                name="Build",
                configuration={
                    "ProjectName": cfn_project.name,
                    "PrimarySource": "SourceArtifact_codecommit"
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_codecommit"
                    ),
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_template"
                    )
                ],
                namespace="BuildVariables",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="BuildArtifact"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↓↓ Layer対応版 追加コード
        # Buildアクション(Layer)の作成
        build_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Build",
                    owner="AWS",
                    provider="CodeBuild",
                    version="1"
                ),
                name="Build_layer",
                configuration={
                    "ProjectName": cfn_layer_project.name,
                    "PrimarySource": "SourceArtifact_codecommit"
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_codecommit"
                    ),
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_layer_template"
                    )
                ],
                namespace="BuildLayerVariables",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="BuildLayerArtifact"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↑↑ Layer対応版 追加コード

        deploy_actions = []
# ↓↓ Layer対応版 追加コード
        # Deployアクション(Layerの変更セットの作成)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="CreateLayerChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_REPLACE",
                    "RoleArn": "arn:aws:iam::{0}:role/cdk-hnb659fds-cfn-exec-role-{0}-{1}".\
        format(os.environ["CDK_DEFAULT_ACCOUNT"], "ap-northeast-1"),
                    "StackName": "lambda-stack-{0}".format(layer_id),
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(layer_id),
                    "Capabilities": "CAPABILITY_NAMED_IAM",
                    "TemplatePath": "BuildLayerArtifact::{0}".format("out-{0}.yaml".format(layer_template_file_name))
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildLayerArtifact"
                    )
                ],
                namespace="DeployCreateLayerVariables",
                region="ap-northeast-1",
                run_order=1
            )
        )

        # Deployアクション(Layerの変更セットの実行)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="ExecuteLayerChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_EXECUTE",
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(layer_id),
                    "StackName": "lambda-stack-{0}".format(layer_id)
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildLayerArtifact"
                    )
                ],
                namespace="DeployExecuteLayerVariables",
                region="ap-northeast-1",
                run_order=2
            )
        )
# ↑↑ Layer対応版

        # Deployアクション(変更セットの作成)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="CreateChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_REPLACE",
                    "RoleArn": "arn:aws:iam::{0}:role/cdk-hnb659fds-cfn-exec-role-{0}-{1}".\
        format(os.environ["CDK_DEFAULT_ACCOUNT"], "ap-northeast-1"),
                    "StackName": "lambda-stack-{0}".format(lambda_id),
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(lambda_id),
                    "Capabilities": "CAPABILITY_NAMED_IAM",
                    "TemplatePath": "BuildArtifact::{0}".format("out-{0}.yaml".format(template_file_name))
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildArtifact"
                    )
                ],
                namespace="DeployCreateVariables",
                region="ap-northeast-1",
                run_order=3
                # run_order=1
            )
        )

        # Deployアクション(変更セットの実行)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="ExecuteChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_EXECUTE",
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(lambda_id),
                    "StackName": "lambda-stack-{0}".format(lambda_id)
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildArtifact"
                    )
                ],
                namespace="DeployExecuteVariables",
                region="ap-northeast-1",
                run_order=4
                # run_order=2
            )
        )
    
        # Pipelineの作成
        cfn_pipeline = codepipeline.CfnPipeline(
            self,
            "pipeline-lambda",
            role_arn=pipeline_role.attr_arn,
            stages=[
                codepipeline.CfnPipeline.StageDeclarationProperty(
                    actions=source_actions,
                    name="Source",
                ),
                codepipeline.CfnPipeline.StageDeclarationProperty(
                    actions=build_actions,
                    name="Build",
                ),
                codepipeline.CfnPipeline.StageDeclarationProperty(
                    actions=deploy_actions,
                    name="Deploy",
                )
            ],
            artifact_store=codepipeline.CfnPipeline.ArtifactStoreProperty(
                location=artifact_s3bucket.ref,
                type="S3",
            ),
            name="pipeline_lambda",
            restart_execution_on_update=False
        )
        cfn_pipeline.node.add_dependency(s3_deploy)

        pipeline_arn = "arn:aws:codepipeline:{0}:{1}:{2}".format(
                "ap-northeast-1", os.environ["CDK_DEFAULT_ACCOUNT"], cfn_pipeline.name)

        # EventBridge用のIAMロールの作成
        exec_pipeline_role = CreateExecPipelineIAMRole(self, pipeline_arn)
        # CodeCommitリポジトリの変更を検知するEventBridgeルールの作成
        CodecommitEvents(self, repository_name, pipeline_arn, exec_pipeline_role)

以下は、CodeCommitリポジトリに用意するファイルです。
TestFunc/lambda_function.pyとbuildspec_TestFunc.yamlは、Layerの確認のため、少し前回と変更しています。

test-lambda-rep/TestFunc/lambda_function.py
from test_util import test_message
def lambda_handler(event, context):
    print(test_message())
test-lambda-rep/buildspec_TestFunc.yaml
version: 0.2
phases:
  install:
    commands:
      - aws --version
      - cp -pr ../0[1-9]/* .
      - ls -lR ./
  build:
    commands:
      - aws cloudformation package --template-file ${TEMPLATE_FILE_NAME}.json --s3-bucket ${CFN_PKG_BUCKET} --output-template-file out-${TEMPLATE_FILE_NAME}.yaml
artifacts:
 files:
    - out-${TEMPLATE_FILE_NAME}.yaml
test-lambda-rep/TestLayer/python/test_util.py
def test_message():
    return "test message!"
test-lambda-rep/buildspec_TestLayer.yaml
version: 0.2
phases:
  install:
    commands:
      - aws --version
      - cp -pr ../0[1-9]/* .
      - ls -lR ./
  build:
    commands:
      - aws cloudformation package --template-file ${TEMPLATE_FILE_NAME}.json --s3-bucket ${CFN_PKG_BUCKET} --output-template-file out-${TEMPLATE_FILE_NAME}.yaml
artifacts:
 files:
    - out-${TEMPLATE_FILE_NAME}.yaml

では、変更箇所を処理毎に順に説明します。

SAM Templateファイルの作成

Lambdaと同じように、Layerの設定を定義したSAM Templateファイルを作成します。
Layerの定義に加えて、LayerのARNをパラメータストアに格納するようにします。併せて、Lambdaの定義にLayerの設定を追加し、ARNをパラメータストアから読み込むようにします。
パラメータストアを使用せずにスタック参照(ExportValue&ImportValue)でもいけるかと思いましたが、Layerの更新の際にエラーになるため、パラメータストアを使用することにしました。
あと、環境変数にLayerのARNを設定するようにしています。Layerだけ更新された場合に、Lambdaのバージョンが作成されない(バグらしいですが。。)ため、Layerに伴って環境変数が変更されることで、Lambdaもバージョンが作成されるようにしています。
作成したSAM Templateファイルは、zip化した後、LambdaのSAM Templateファイルと一緒にaws_s3_deploymentでS3バケットへアップロードします。

cdk_app/cdk_app_stack.py

(中略)

# ↓↓ Layer対応版 追加コード
# LambdaLayer作成用のSAM Templateファイルを作成
def EditSAMLayerTemplate(layer_id):

    sam_template_data = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Transform": "AWS::Serverless-2016-10-31",
        "Description": "An AWS Serverless Specification template describing your layer.",
        "Resources": {
            "TestLambdLayer": {
                "Type": "AWS::Serverless::LayerVersion",
                "Properties": {
                    "LayerName": "",
                    "ContentUri": "",
                    "CompatibleRuntimes": ["python3.10"]
                }
            },
            "TestLayerArnParameter": {
                "Type": "AWS::SSM::Parameter",
                "Properties": {
                    "Name": "",
                    "Type": "String",
                    "Value": { "Ref" : "TestLambdLayer" }
                }
            }
        }
    }

    sam_template_data["Resources"]["TestLambdLayer"]["Properties"]["ContentUri"] = layer_id
    sam_template_data["Resources"]["TestLambdLayer"]["Properties"]["LayerName"] = layer_id
    sam_template_data["Resources"]["TestLayerArnParameter"]["Properties"]["Name"] =\
        "/test/layer_arn/{0}".format(layer_id)

    return sam_template_data
# ↑↑ Layer対応版 追加コード


# LambdaFunction作成用のSAM Templateファイルを作成
# ↓↓ Layer対応版 変更コード
def EditSAMTemplate(lambda_id, lambda_role, layer_id):
# def EditSAMTemplate(lambda_id, lambda_role):
# ↑↑ Layer対応版 変更コード

    sam_template_data = {
        "AWSTemplateFormatVersion": "2010-09-09",
        "Transform": "AWS::Serverless-2016-10-31",
        "Resources": {
            "TestLambdaFunction": {
                "Type": "AWS::Serverless::Function",
                "Properties": {
                    "Handler": "lambda_function.lambda_handler",
                    "Runtime": "python3.10",
                    "CodeUri": "",
                    "Description": "",
                    "FunctionName": "",
                    "AutoPublishAlias": "",
# ↓↓ Layer対応版 追加コード
                    "AutoPublishAliasAllProperties": true,
# ↑↑ Layer対応版 追加コード
                    "MemorySize": 128,
                    "Timeout": 3,
                    "Role": "",
                    "Environment": {
                        "Variables": {
                        }
                    },
                    "DeploymentPreference": {
                        "Enabled": True,
                        "Type": "AllAtOnce"
                    }
                }
            }
        }
    }
    
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["CodeUri"] = lambda_id
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["FunctionName"] = lambda_id
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["AutoPublishAlias"] = "Dev"
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Role"] =\
        "arn:aws:iam::{0}:role/{1}".format(os.environ["CDK_DEFAULT_ACCOUNT"], lambda_role.role_name)
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Environment"]["Variables"] =\
        {"test_variables": "test"}
# ↓↓ Layer対応版 追加コード
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Environment"]["Variables"]["layer"] =\
        "{{resolve:ssm:" + "/test/layer_arn/{0}".format(layer_id) + "}}"
    sam_template_data["Resources"]["TestLambdaFunction"]["Properties"]["Layers"] =\
        ["{{resolve:ssm:" + "/test/layer_arn/{0}".format(layer_id) + "}}"]
# ↑↑ Layer対応版 追加コード

    return sam_template_data

(中略)

class CdkAppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_id = "TestFunc"
# ↓↓ Layer対応版 追加コード
        layer_id = "TestLayer"
# ↑↑ Layer対応版 追加コード
        repository_name = "test-lambda-rep"

(中略)

        zip_dir = "./cdk_app/work/"
        zip_file = "template_{0}.zip".format(lambda_id)
        zip_path = zip_dir + zip_file
        template_file_name = "sam-template"
        # SAM Templateファイルの編集
        sam_template_data = EditSAMTemplate(lambda_id, lambda_role, layer_id)
        # sam_template_data = EditSAMTemplate(lambda_id, lambda_role)
        with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_STORED) as z:
            z.writestr(template_file_name + ".json", json.dumps(sam_template_data, indent=4))

# ↓↓ Layer対応版 追加コード
        layer_zip_file = "template_{0}.zip".format(layer_id)
        layer_zip_path = zip_dir + layer_zip_file
        layer_template_file_name = "sam-layer-template"
        # SAM Templateファイルの編集
        sam_layer_template_data = EditSAMLayerTemplate(layer_id)
        with zipfile.ZipFile(layer_zip_path, 'w', zipfile.ZIP_STORED) as z:
            z.writestr(layer_template_file_name + ".json", json.dumps(sam_layer_template_data, indent=4))
# ↑↑ Layer対応版 追加コード

        # 作成したSAM TemplateファイルをS3へアップロード
        s3_deploy = s3deploy.BucketDeployment(
            self,
            "TemplateFileUploadS3",
            sources=[s3deploy.Source.asset(zip_dir)],
            destination_bucket=source_s3bucket_l2,
        )

(中略)

Sourceアクションの作成

LayerのSAMテンプレートファイル分のSourceアクションを追加します。(今回、CodeCommitリポジトリはLambdaソースコードと同じにしているので、追加はしていません。)

cdk_app/cdk_app_stack.py

(中略)

class CdkAppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_id = "TestFunc"
# ↓↓ Layer対応版 追加コード
        layer_id = "TestLayer"
# ↑↑ Layer対応版 追加コード
        repository_name = "test-lambda-rep"

(中略)

        source_actions = []
        # Sourceアクション(CodeCommit(Lambdaコード、buildspec))の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="CodeCommit",
                    version="1"
                ),
                name="Source_codecommit",
                configuration={
                    "RepositoryName": repository_name,
                    "BranchName": "main",
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_codecommit",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_codecommit"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )

        # Sourceアクション(SAM Template)の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="S3",
                    version="1"
                ),
                name="Source_template",
                configuration={
                    "S3Bucket": source_s3bucket.bucket_name,
                    "S3ObjectKey": zip_file,
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_Template",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_template"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↓↓ Layer対応版 追加コード
        # Sourceアクション(LayerのSAM Template)の作成
        source_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Source",
                    owner="AWS",
                    provider="S3",
                    version="1"
                ),
                name="Source_layer_template",
                configuration={
                    "S3Bucket": source_s3bucket.bucket_name,
                    "S3ObjectKey": layer_zip_file,
                    "PollForSourceChanges": False
                },
                namespace="SourceVariables_LayerTemplate",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="SourceArtifact_layer_template"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↑↑ Layer対応版 追加コード

(中略)

CodeBuildプロジェクト、Buildアクションの作成

Layer分のCodeBuildプロジェクトを作成し、そのCodeBuildプロジェクトを実行するパイプラインのBuildアクションを作成します。
内容としては、ほぼLambdaと同じです。(すみません。共通化をサボりました。。)

cdk_app/cdk_app_stack.py

(中略)

class CdkAppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_id = "TestFunc"
# ↓↓ Layer対応版 追加コード
        layer_id = "TestLayer"
# ↑↑ Layer対応版 追加コード
        repository_name = "test-lambda-rep"

(中略)

        # Buildプロジェクトの作成
        cfn_project = codebuild.CfnProject(
            self, 
            "build-lambda-project",
            artifacts=codebuild.CfnProject.ArtifactsProperty(
                type="CODEPIPELINE",
            ),
            environment=codebuild.CfnProject.EnvironmentProperty(
                compute_type="BUILD_GENERAL1_SMALL",
                image="aws/codebuild/standard:7.0",
                type="LINUX_CONTAINER",
                privileged_mode=False,
                environment_variables=[
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="TEMPLATE_FILE_NAME",
                        value=template_file_name,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="CFN_PKG_BUCKET",
                        value=cfn_pkg_s3bucket.bucket_name,
                    )
                ],
            ),
            service_role=build_role.attr_arn,
            source=codebuild.CfnProject.SourceProperty(
                type="CODEPIPELINE",
                build_spec="buildspec_{0}.yaml".format(lambda_id)
            ),
            logs_config=codebuild.CfnProject.LogsConfigProperty(
                cloud_watch_logs=codebuild.CfnProject.CloudWatchLogsConfigProperty(
                    status="ENABLED",
                ),
            ),
            name="build_lambda_project",
        )

# ↓↓ Layer対応版 追加コード
        # Buildプロジェクト(Layer)の作成
        cfn_layer_project = codebuild.CfnProject(
            self, 
            "build-layer-project",
            artifacts=codebuild.CfnProject.ArtifactsProperty(
                type="CODEPIPELINE",
            ),
            environment=codebuild.CfnProject.EnvironmentProperty(
                compute_type="BUILD_GENERAL1_SMALL",
                image="aws/codebuild/standard:7.0",
                type="LINUX_CONTAINER",
                privileged_mode=False,
                environment_variables=[
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="TEMPLATE_FILE_NAME",
                        value=layer_template_file_name,
                    ),
                    codebuild.CfnProject.EnvironmentVariableProperty(
                        name="CFN_PKG_BUCKET",
                        value=cfn_pkg_s3bucket.bucket_name,
                    )
                ],
            ),
            service_role=build_role.attr_arn,
            source=codebuild.CfnProject.SourceProperty(
                type="CODEPIPELINE",
                build_spec="buildspec_{0}.yaml".format(layer_id)
            ),
            logs_config=codebuild.CfnProject.LogsConfigProperty(
                cloud_watch_logs=codebuild.CfnProject.CloudWatchLogsConfigProperty(
                    status="ENABLED",
                ),
            ),
            name="build_layer_project",
        )
# ↑↑ Layer対応版 追加コード

(中略)

        # Buildアクションの作成
        build_actions = []
        build_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Build",
                    owner="AWS",
                    provider="CodeBuild",
                    version="1"
                ),
                name="Build",
                configuration={
                    "ProjectName": cfn_project.name,
                    "PrimarySource": "SourceArtifact_codecommit"
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_codecommit"
                    ),
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_template"
                    )
                ],
                namespace="BuildVariables",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="BuildArtifact"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↓↓ Layer対応版 追加コード
        # Buildアクション(Layer)の作成
        build_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Build",
                    owner="AWS",
                    provider="CodeBuild",
                    version="1"
                ),
                name="Build_layer",
                configuration={
                    "ProjectName": cfn_layer_project.name,
                    "PrimarySource": "SourceArtifact_codecommit"
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_codecommit"
                    ),
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="SourceArtifact_layer_template"
                    )
                ],
                namespace="BuildLayerVariables",
                output_artifacts=[codepipeline.CfnPipeline.OutputArtifactProperty(
                    name="BuildLayerArtifact"
                )],
                region="ap-northeast-1",
                run_order=1
            )
        )
# ↑↑ Layer対応版 追加コード

(中略)
test-lambda-rep/buildspec_TestFunc.yaml
version: 0.2
phases:
  install:
    commands:
      - aws --version
      - cp -pr ../0[1-9]/* .
      - ls -lR ./
  build:
    commands:
      - aws cloudformation package --template-file ${TEMPLATE_FILE_NAME}.json --s3-bucket ${CFN_PKG_BUCKET} --output-template-file out-${TEMPLATE_FILE_NAME}.yaml
artifacts:
 files:
    - out-${TEMPLATE_FILE_NAME}.yaml
test-lambda-rep/buildspec_TestLayer.yaml
version: 0.2
phases:
  install:
    commands:
      - aws --version
      - cp -pr ../0[1-9]/* .
      - ls -lR ./
  build:
    commands:
      - aws cloudformation package --template-file ${TEMPLATE_FILE_NAME}.json --s3-bucket ${CFN_PKG_BUCKET} --output-template-file out-${TEMPLATE_FILE_NAME}.yaml
artifacts:
 files:
    - out-${TEMPLATE_FILE_NAME}.yaml

Deployアクションの作成

パイプラインに組み込むDeployアクションを作成します。
ここもほほLambdaの時と同じですね。
LambdaよりLayerが先にデプロイされるようにrun_orderの値を設定します。

cdk_app/cdk_app_stack.py

(中略)

class CdkAppStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        lambda_id = "TestFunc"
# ↓↓ Layer対応版 追加コード
        layer_id = "TestLayer"
# ↑↑ Layer対応版 追加コード
        repository_name = "test-lambda-rep"

(中略)

        deploy_actions = []
# ↓↓ Layer対応版 追加コード
        # Deployアクション(Layerの変更セットの作成)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="CreateLayerChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_REPLACE",
                    "RoleArn": "arn:aws:iam::{0}:role/cdk-hnb659fds-cfn-exec-role-{0}-{1}".\
        format(os.environ["CDK_DEFAULT_ACCOUNT"], "ap-northeast-1"),
                    "StackName": "lambda-stack-{0}".format(layer_id),
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(layer_id),
                    "Capabilities": "CAPABILITY_NAMED_IAM",
                    "TemplatePath": "BuildLayerArtifact::{0}".format("out-{0}.yaml".format(layer_template_file_name))
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildLayerArtifact"
                    )
                ],
                namespace="DeployCreateLayerVariables",
                region="ap-northeast-1",
                run_order=1
            )
        )

        # Deployアクション(Layerの変更セットの実行)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="ExecuteLayerChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_EXECUTE",
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(layer_id),
                    "StackName": "lambda-stack-{0}".format(layer_id)
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildLayerArtifact"
                    )
                ],
                namespace="DeployExecuteLayerVariables",
                region="ap-northeast-1",
                run_order=2
            )
        )
# ↑↑ Layer対応版

        # Deployアクション(変更セットの作成)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="CreateChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_REPLACE",
                    "RoleArn": "arn:aws:iam::{0}:role/cdk-hnb659fds-cfn-exec-role-{0}-{1}".\
        format(os.environ["CDK_DEFAULT_ACCOUNT"], "ap-northeast-1"),
                    "StackName": "lambda-stack-{0}".format(lambda_id),
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(lambda_id),
                    "Capabilities": "CAPABILITY_NAMED_IAM",
                    "TemplatePath": "BuildArtifact::{0}".format("out-{0}.yaml".format(template_file_name))
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildArtifact"
                    )
                ],
                namespace="DeployCreateVariables",
                region="ap-northeast-1",
                run_order=3
                # run_order=1
            )
        )

        # Deployアクション(変更セットの実行)の作成
        deploy_actions.append(
            codepipeline.CfnPipeline.ActionDeclarationProperty(
                action_type_id=codepipeline.CfnPipeline.ActionTypeIdProperty(
                    category="Deploy",
                    owner="AWS",
                    provider="CloudFormation",
                    version="1"
                ),
                name="ExecuteChangeSet",
                configuration={
                    "ActionMode": "CHANGE_SET_EXECUTE",
                    "ChangeSetName": "lambda-stack-{0}-changeSet".format(lambda_id),
                    "StackName": "lambda-stack-{0}".format(lambda_id)
                },
                input_artifacts=[
                    codepipeline.CfnPipeline.InputArtifactProperty(
                        name="BuildArtifact"
                    )
                ],
                namespace="DeployExecuteVariables",
                region="ap-northeast-1",
                run_order=4
                # run_order=2
            )
        )
    
(中略)

CI/CD環境のデプロイ&実行確認

以上で準備が整ったので、CI/CD環境のデプロイを行います。デプロイすると自動でパイプラインも実行されます。

(.venv) user_name:~/environment/cdk-app (master) $ cdk deploy

image.png

Layerを確認すると、きちんと作成されていることが確認できました。
image.png

Lambdaを実行し、Layerが使用されていることも確認できました。
image.png

CodeCommitでLayerのソースコードを変更してみます。
image.png

パイプラインが自動で起動しました。
image.png

パイプラインが完了すると、Layerが更新されていることが確認できます。
image.png

Lambdaを実行し、更新したLayerが使用されていることが確認できました!
image.png

まとめ

AWS CDKでLayerに対応したLambdaのCI/CD環境を作成しました。Lambda使うならLayerも使えた方がいいよなぁと思ってしましたが、ほぼLambdaと同じでしたね。
最後まで読んでいただいてありがとうございます。
少しでも参考になれば幸いです。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0