お知らせ フロントエンド バックエンド インフラ 品質保証 セキュリティ 製品 興味・関心 その他

2023.08.18

インフラ

別のAWSアカウントのCodeCommitリポジトリを起点としたSAM CI/CDパイプライン

AWS SAMはAWS公式のサーバーレスアプリケーション構築用のオープンソースフレームワークで、サーバレスアプリケーションを作成するための便利な機能が実装されています。AWS SAMでCI/CDパイプラインも簡単に構築できますが、CodeCommitを別のAWSアカウントに設置する場合は一工夫が必要でした。

また、AWS SAMで自動生成されるCI/CDパイプラインはさまざまなケースに対応できるように冗長な構成となっています。不要な機能をバッサリ削除してシンプルに、かつ弊社の運用では汎用となるような構成となるように改変しました。

サンプルアプリケーション

この記事では次のような2つの機能を持った簡単なAPIを実装し、CI/CDパイプラインを構築します。

  • 住所を「都道府県」/「市区町村」/「町域以下」の3つの部分に分割
  • 郵便番号から住所を返す

日本郵便の郵便番号データ「住所の郵便番号(1レコード1行、UTF-8形式)」をDynamoDBへ保存し、APIで利用します。

API実装

sam initから開始し、PythonでAPIを実装しました。APIの実装についてはこの記事の本筋とは外れますので、概要の説明と主要コードの掲載のみとします。

DynamoDBには、郵便番号データを次のようなフィールドで保存しています。DynamoDBへのデータインポート方法の説明は、この記事では省略します。

{
 "zipcode": "0640941",              # 郵便番号
 "prefcode": "01",                  # 都道府県コード(JIS X 0401)
 "address1": "北海道",               # 都道府県
 "address2": "札幌市中央区",          # 市区町村
 "address3": "旭ケ丘",               # 町域
 "kana1": "ホッカイドウ",             # 都道府県カナ
 "kana2": "サッポロシチュウオウク",    # 市区町村カナ
 "kana3": "アサヒガオカ",             # 町域カナ
 "first2": "札幌"                    # 市区町村最初の2文字
}

DynamoDBのパーティションキーはzipcodeとしています。

グローバルセカンダリインデックス(キー名: prefcode-first2-index)として、パーティションキーprefcode, ソートキー first2を設定しています。日本の市区町村が必ず2文字以上あるということから市区町村の最初の2文字をDynamoDBでインデックスし、検索に利用しています。DynamoDBのScanではなくQueryを使うことにより、高速に効率よく住所の候補を検索することができます。

APIのQuery Stringにzipcode引数が与えられた場合は、DynamoDBからzipcodeを元に住所情報を取得して返します。

address引数が与えられた場合は、DynamoDBから都道府県コードと市区町村の最初の2文字を元に住所の候補を取得し、候補の中で、一番長くマッチする住所情報を探して返します。

実行例1 (郵便番号から住所を検索)

URL: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/search?zipcode=1600023

{
  "message": null,
  "results": [
    {
      "zipcode": "1600023",
      "prefcode": "13",
      "address1": "東京都",
      "address2": "新宿区",
      "address3": "西新宿",
      "kana1": "トウキョウト",
      "kana2": "シンジュクク",
      "kana3": "ニシシンジュク"
    }
  ],
  "status": 200
}

実行例2 (住所を都道府県/市区町村/町域以下に分割)

URL: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/search?address=東京都新宿区西新宿1丁目22-15%20グラフィオ西新宿9F

{
  "message": null,
  "results": {
    "zipcode": "1600023",
    "prefcode": "13",
    "address1": "東京都",
    "address2": "新宿区",
    "address3": "西新宿1丁目22-15 グラフィオ西新宿9F",
    "kana1": "トウキョウト",
    "kana2": "シンジュクク",
    "kana3": "ニシシンジュク"
  },
  "status": 200
}

APIのコード(python/app.py)は以下のようになります。サンプルなので、エラー処理等がいいかげんです。

import json
import logging
import os
import boto3
import botocore

TABLENAME = os.getenv("TABLENAME", default="ZipCode")

# 都道府県コード(JIS X 0401)
prefs = {
    "北海道": "01", "青森県": "02", "岩手県": "03", "宮城県": "04",
    "秋田県": "05", "山形県": "06", "福島県": "07", "茨城県": "08",
    "栃木県": "09", "群馬県": "10", "埼玉県": "11", "千葉県": "12",
    "東京都": "13", "神奈川県": "14", "新潟県": "15", "富山県": "16",
    "石川県": "17", "福井県": "18", "山梨県": "19", "長野県": "20",
    "岐阜県": "21", "静岡県": "22", "愛知県": "23", "三重県": "24",
    "滋賀県": "25", "京都府": "26", "大阪府": "27", "兵庫県": "28",
    "奈良県": "29", "和歌山県": "30", "鳥取県": "31", "島根県": "32",
    "岡山県": "33", "広島県": "34", "山口県": "35", "徳島県": "36",
    "香川県": "37", "愛媛県": "38", "高知県": "39", "福岡県": "40",
    "佐賀県": "41", "長崎県": "42", "熊本県": "43", "大分県": "44",
    "宮崎県": "45", "鹿児島県": "46", "沖縄県": "47"
}
logger = logging.getLogger()
logger.setLevel(logging.INFO)

def from_dynamodb_item(item):
    ret = {}
    for key in item:
        ret[key] = item[key]["S"]
    return ret

def search(zipcode):
    try:
        dynamodb = boto3.client("dynamodb")
        response = dynamodb.query(
            TableName=TABLENAME,
            KeyConditionExpression="zipcode = :zipcode",
            ExpressionAttributeValues={
                ":zipcode": {"S": zipcode}
            }
        )
    except botocore.exceptions.ClientError as err:
        if err.response["Error"]["Code"] == "LimitExceededException":
            logger.warning("API Limit exceeded")
        else:
            raise err
    if "Items" in response and len(response["Items"]) > 0:
        items = []
        for item in response["Items"]:
            decoded = from_dynamodb_item(item)
            del decoded["first2"]
            items.append(decoded)
        return items
    return None

def longest_match(rest, items):
    longest = None
    longest_len = 0
    for item in items:
        decoded = from_dynamodb_item(item)
        del decoded["first2"]
        addr23 = decoded["address2"] + decoded["address3"]
        length = len(addr23)
        if longest_len < length and addr23 == rest[0:length]:
            longest = decoded
            longest["address3"] = rest[len(decoded["address2"]):]
            longest_len = length
            continue
        length = len(decoded["address2"])
        if longest_len < length and decoded["address2"] == rest[0:length]:
            longest = decoded
            longest["address3"] = rest[len(decoded["address2"]):]
            longest_len = length
    return longest

def match(address):
    try:
        prefcode = None
        first2 = None
        rest = None
        for pref, prefcode in prefs.items():
            if address[0:len(pref)] == pref:
                rest = address[len(pref):]
                first2 = rest[0:2]
                break
        if prefcode is None:
            raise ValueError
        dynamodb = boto3.client("dynamodb")
        response = dynamodb.query(
            TableName=TABLENAME,
            IndexName="prefcode-first2-index",
            KeyConditionExpression="prefcode = :prefcode AND first2 = :first2",
            ExpressionAttributeValues={
                ":prefcode": {"S": prefcode},
                ":first2": {"S": first2}
            }
        )
    except botocore.exceptions.ClientError as err:
        if err.response["Error"]["Code"] == "LimitExceededException":
            logger.warning("API Limit exceeded")
        else:
            raise err
    if "Items" in response and len(response["Items"]) > 0:
        matched = longest_match(rest, response["Items"])
        return matched
    return None

def lambda_handler(event, context):
    logger.info("Received event: %s", json.dumps(event, indent=2))
    logger.info(context)

    try:
        if "queryStringParameters" not in event:
            raise ValueError
        params = event["queryStringParameters"]
        if "zipcode" in params:
            results = search(params["zipcode"])
        elif "address" in params:
            results = match(params["address"])
        else:
            raise ValueError
        if results is None:
            results = []
        return {
            "statusCode": 200,
            "body": json.dumps({
                "message": None,
                "results": results,
                "status": 200
            }),
            "headers": {
                "Content-Type": "application/json"
            }
        }
    except ValueError:
        return {
            "statusCode": 400,
            "body": json.dumps({
                "message": "Invalid parameters.",
                "results": None,
                "status": 400
            }),
            "headers": {
                "Content-Type": "application/json"
            }
        }

AWSリソース定義のtemplate.yamlは以下のようになります。DynamoDBキャパシティユニットのオートスケールを設定しているため記述が長くなってしまっていますが、オートスケールの部分(98行目からのDynamoDBScalingRoleから下すべて)を除けばシンプルな構成となっています。

Lambda実行ログを保存するCloudWatch Logsロググループのログ保持期間を設定するため、ロググループを明示的に作成しています。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  zipcode

Globals:
  Function:
    Timeout: 30

Parameters:
  Stage:
    Description: Deploy Stage
    Type: String
    Default: Staging
    AllowedValues:
      - Staging
      - Production
  MinCapacityUnit:
    Description: DynamoDB Minimum Capacity Unit
    Type: Number
    Default: 1
  MaxCapacityUnit:
    Description: DynamoDB Maximum Capacity Unit
    Type: Number
    Default: 10
  FunctionLogRetentionInDays:
    Type: Number
    Default: 7

Conditions:
  IsProduction: !Equals [ !Ref Stage, Production ]

Resources:
  ZipCodeFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: python/
      Handler: app.lambda_handler
      Runtime: python3.10
      Architectures:
        - x86_64
      Events:
        ZipCode:
          Type: Api
          Properties:
            Path: /search
            Method: GET
      Environment:
        Variables:
          TABLENAME: !Ref ZipCodeDynamoDB
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref ZipCodeDynamoDB
        - DynamoDBWritePolicy:
            TableName: !Ref ZipCodeDynamoDB

  ZipCodeFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${ZipCodeFunction}"
      RetentionInDays: !Ref FunctionLogRetentionInDays

  ZipCodeDynamoDB:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: zipcode
          AttributeType: S
        - AttributeName: prefcode
          AttributeType: S
        - AttributeName: first2
          AttributeType: S
      GlobalSecondaryIndexes:
        - IndexName: prefcode-first2-index
          KeySchema:
            - AttributeName: prefcode
              KeyType: HASH
            - AttributeName: first2
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: !Ref MinCapacityUnit
            WriteCapacityUnits: !Ref MinCapacityUnit
      KeySchema:
        - AttributeName: zipcode
          KeyType: HASH
        - AttributeName: first2
          KeyType: RANGE
      ProvisionedThroughput:
        ReadCapacityUnits: !Ref MinCapacityUnit
        WriteCapacityUnits: !Ref MinCapacityUnit
      TableName: !If
        - IsProduction
        - ZipCode
        - !Ref AWS::NoValue

  DynamoDBScalingRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: application-autoscaling.amazonaws.com
            Action: sts:AssumeRole
      Path: "/"
      Policies:
        - PolicyName: DynamoDBAutoScale
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "dynamodb:DescribeTable"
                  - "dynamodb:UpdateTable"
                  - "cloudwatch:PutMetricAlarm"
                  - "cloudwatch:DescribeAlarms"
                  - "cloudwatch:GetMetricStatistics"
                  - "cloudwatch:SetAlarmState"
                  - "cloudwatch:DeleteAlarms"
                Resource: "*"

  DynamoDBWriteCapacityScalableTarget:
    Type: "AWS::ApplicationAutoScaling::ScalableTarget"
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Sub "table/${ZipCodeDynamoDB}"
      RoleARN: !GetAtt DynamoDBScalingRole.Arn
      ScalableDimension: "dynamodb:table:WriteCapacityUnits"
      ServiceNamespace: dynamodb

  DynamoDBWriteScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Properties:
      PolicyName: WriteAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref DynamoDBWriteCapacityScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBWriteCapacityUtilization

  DynamoDBReadCapacityScalableTarget:
    Type: "AWS::ApplicationAutoScaling::ScalableTarget"
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Sub "table/${ZipCodeDynamoDB}"
      RoleARN: !GetAtt DynamoDBScalingRole.Arn
      ScalableDimension: "dynamodb:table:ReadCapacityUnits"
      ServiceNamespace: dynamodb

  DynamoDBReadScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Properties:
      PolicyName: ReadAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref DynamoDBReadCapacityScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBReadCapacityUtilization

  DynamoDBPrefcodeWriteCapacityScalableTarget:
    Type: "AWS::ApplicationAutoScaling::ScalableTarget"
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Sub "table/${ZipCodeDynamoDB}/index/prefcode-first2-index"
      RoleARN: !GetAtt DynamoDBScalingRole.Arn
      ScalableDimension: "dynamodb:index:WriteCapacityUnits"
      ServiceNamespace: dynamodb

  DynamoDBPrefcodeWriteScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Properties:
      PolicyName: PrefcodeWriteAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref DynamoDBPrefcodeWriteCapacityScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBWriteCapacityUtilization

  DynamoDBPrefcodeReadCapacityScalableTarget:
    Type: "AWS::ApplicationAutoScaling::ScalableTarget"
    Properties:
      MaxCapacity: !Ref MaxCapacityUnit
      MinCapacity: !Ref MinCapacityUnit
      ResourceId: !Sub "table/${ZipCodeDynamoDB}/index/prefcode-first2-index"
      RoleARN: !GetAtt DynamoDBScalingRole.Arn
      ScalableDimension: "dynamodb:index:ReadCapacityUnits"
      ServiceNamespace: dynamodb

  DynamoDBPrefcodeReadScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Properties:
      PolicyName: PrefcodeReadAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref DynamoDBPrefcodeReadCapacityScalableTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBReadCapacityUtilization

AWS SAMでCI/CDパイプラインを作成

AWS SAMの機能を使ってCI/CDパイプラインを作成します。sam pipeline init --bootstrapを実行して質問に回答していくと、CodePipelineと関連するリソースを作成するCloudFormationテンプレートcodepipeline.yamlが自動生成されます。

今回は、CodeCommitとCodePipelineを使った2-stage Pipelineを作成しました。1ステージ目の名称はstaging、2ステージ目の名称はproductionとしています。ステージを設定する過程で(–bootstrap引数の効果)、ふたつのCloudFormationスタック(aws-sam-cli-managed-staging-pipeline-resources, aws-sam-cli-managed-production-pipeline-resources)が自動生成されます。

生成されたcodepipeline.yamlをCloudFormationで適用すると(sam deploy -t codepipeline.yaml --stack-name ZipCodePipeline --capabilities=CAPABILITY_IAM)、最終的に次のようなリソースが生成されます。

sam pipeline init –bootstrapで生成されたリソース

CodeCommitを別AWSアカウントへ

sam pipeline initで生成された構成から、CodeCommitを別のAWSアカウントへ移動します。

AWS SAMで自動生成されるパイプラインは、さまざまな構成で使用できるよう、冗長な部分があります。使わない部分を削ってシンプルな構成にします。

  • CodePipeline実行アカウントとデプロイ先(Test/Prod)のAWSアカウントを全て別のアカウントにするパターンは不要
    • Artifact保存用S3 Bucketが3つあるため、ひとつにまとめる
    • S3 Bucketのアクセスログ(参考リンク)は不要なので全て削除
    • デプロイ時のロールが冗長なのでまとめる
  • FeatureBranch、CodeStar、Lambda Imageに関する機能は不要なので削除

また、AWS SAMの不具合と思われる部分を修正します。

  • CodeBuildServiceRoleに「DependsOn: PipelineStackCloudFormationExecutionRole」を追加(これがないと、パイプラインのCloudFormationスタックを削除する時にエラーが発生する場合がある)
  • CodePipelineの「UpdatePipelineステージ」CreateChangeSetのParameterOverridesで一部しかパラメータを渡していないため、全パラメータをフルで渡すように修正

上記を施した構成は次のようになります。

CodeCommitを別AWSアカウントへ

この構成のポイントは以下のとおりです。

  • CodeCommitを配置するアカウント(RepositoryAccount)とCodePipelineを配置、APIをデプロイするアカウント(PipelineAccount)を別のAWSアカウントとします
  • CodeCommitの状態変更は、Pipelineアカウント内のカスタムイベントバス(名称: codecommit)に転送されます
  • PipelineAccount側のEventルール(名称: CloudWatchEventRule)がカスタムイベントバスcodecommitを監視し、mainブランチへの更新を検知してCodePipelineを起動します
  • CodePipelineはRepositoryAccount側のIAMロール(名称: CodeCommitRole)にAssumeRoleすることが可能で、このロールの権限でCodeCommitのデータにアクセスします
  • CodeCommitRoleはPipelineAccount側のS3 Bucket(名称: PipelineArtifactsBucket)およびKMS Keyへのアクセス権限を持つため、CodePipelineの求めに応じてソースコードのアーカイブをPipelineArtifactsBucketへ保存可能です
  • CodeCommitとCodePipelineを別のAWSアカウントに設置する構成の場合、S3 Bucketの暗号化はデフォルトのキーを使えず、カスタムキーで実施しなければならないようなので、KMSキーを作成しています
  • CodePipeline実行の過程で生成される中間生成物は(CodeBuildから起動されるsamコマンドの生成物も含め)、全てPipelineArtifactsBucketに保存されます
  • samを実行する権限、sam deployでCloudFormationを実行する権限はそれぞれPipelineExecutionRole, CloudFormationExecutionRoleが使用され、第1ステージ、第2ステージで共通です

この構成のリソースの作成は、次に説明するように3つのCloudFormationテンプレートで行います。

CodeCommitと関連リソース

RepositoryAccountでCodeCommit関連のリソースを作成します。CloudFormationテンプレートcodecommit.yamlを適用して作成します。リポジトリひとつにつき、CloudFormationスタックをひとつ作成します。今回はリポジトリがひとつなので、CloudFormationスタックをひとつ作成します。

パラメータにはリポジトリ名(RepositoryName)などの他に、CodePipelineを設置するAWSアカウントIDをPipelineAccountIdに与えます。

AWSTemplateFormatVersion: 2010-09-09
Description: "CodeCommit for cross-account pipeline"

Parameters:
    RepositoryName:
        Description: Repository Name
        Type: String
    RepositoryDescription:
        Description: Repository Description
        Type: String
    MainBranchName:
        Description: Main Branch Name of the Repository
        Type: String
        Default: main
    PipelineAccountId:
        Description: Account ID of the Pipeline
        Type: String
        Default: 123456789012
    EventBusName:
        Description: Event Bus name for CodeCommit state change notification
        Type: String
        Default: codecommit

Resources:
    CodeCommitRepository:
        Type: AWS::CodeCommit::Repository
        Properties: 
            RepositoryName: !Ref RepositoryName
            RepositoryDescription: !Ref RepositoryDescription

    EventRule:
        Type: AWS::Events::Rule
        Properties:
            EventPattern:
                source:
                  - aws.codecommit
                detail-type:
                  - CodeCommit Repository State Change
                resources:
                  - !GetAtt CodeCommitRepository.Arn
                detail:
                    event:
                      - referenceCreated
                      - referenceUpdated
                    referenceType:
                      - branch
                    referenceName:
                      - !Ref MainBranchName
            Targets:
              - Arn: !Sub "arn:aws:events:${AWS::Region}:${PipelineAccountId}:event-bus/${EventBusName}"
                RoleArn: !GetAtt EventRole.Arn
                Id: !Sub "codepipeline-AppPipeline"

    EventRole:
        Type: AWS::IAM::Role
        Properties:
            RoleName: !Sub ${RepositoryName}-EventRole
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Principal:
                        Service:
                          - events.amazonaws.com
                    Action: sts:AssumeRole
            Path: /
            Policies:
              - PolicyName: EventsBus
                PolicyDocument:
                    Version: 2012-10-17
                    Statement:
                      - Effect: Allow
                        Action: events:PutEvents
                        Resource: !Sub "arn:aws:events:${AWS::Region}:${PipelineAccountId}:event-bus/${EventBusName}"

    CodeCommitRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Principal:
                      AWS:
                        - !Sub arn:aws:iam::${PipelineAccountId}:root
                    Action:
                      - sts:AssumeRole
            Path: /
            Policies:
              - PolicyName: source
                PolicyDocument:
                    Version: 2012-10-17
                    Statement:
                      - Effect: Allow
                        Action:
                          - s3:PutObject
                          - s3:PutObjectAcl
                        Resource: "*"
                      - Effect: Allow
                        Action:
                          - kms:DescribeKey
                          - kms:GenerateDataKey*
                          - kms:Encrypt
                          - kms:ReEncrypt*
                          - kms:Decrypt
                        Resource: "*"
                      - Effect: Allow
                        Action:
                          - codecommit:GetBranch
                          - codecommit:GetCommit
                          - codecommit:UploadArchive
                          - codecommit:GetUploadArchiveStatus
                          - codecommit:CancelUploadArchive
                        Resource: "*"

Outputs:
    RepositoryUrl:
        Description: URL to use for cloning the repository over SSH
        Value: !GetAtt CodeCommitRepository.CloneUrlSsh
    CodeCommitRole:
        Description: Role ARN for accessing CodeCommit
        Value: !GetAtt CodeCommitRole.Arn

SAMパイプライン共通リソース

PipelineAccountでS3 BucketやIAMロールなどの権限を作成します。このプロジェクトだけではなく、他のプロジェクトでもこのリソースを利用することができますので、AWSアカウントにつき一回だけ、下記のCloudFormationテンプレートを適用してリソースを作成します。

CloudFormationのパラメータには、CodeCommitを設置したRepositoryAccountのAWSアカウントIDを指定します。

このCloudFormationテンプレートはsam pipeline init --bootstrapで自動作成されるCloudFormationスタック(aws-sam-cli-managed-staging-pipeline-resources, aws-sam-cli-managed-production-pipeline-resources)のテンプレートを参考に作成しました。

AWSTemplateFormatVersion: '2010-09-09'
Description: "SAM Codepipeline resources"

Parameters:
    RepositoryAccountId:
        Description: AWS Account ID of the repository
        Type: String
        Default: 987654321098
    EventBusName:
        Description: Event Bus name for CodeCommit state change notification
        Type: String
        Default: codecommit

Resources:
    PipelineUser:
        Type: AWS::IAM::User
        Properties:
            Policies:
              - PolicyName: AssumeRoles
                PolicyDocument:
                    Version: "2012-10-17"
                    Statement:
                      - Effect: Allow
                        Action:
                            - "sts:AssumeRole"
                        Resource: "*"
                        Condition:
                            StringEquals:
                                aws:ResourceTag/Role: pipeline-execution-role

    PipelineUserAccessKey:
        Type: AWS::IAM::AccessKey
        Properties:
            Serial: 1
            Status: Active
            UserName: !Ref PipelineUser

    PipelineUserSecretKey:
        Type: AWS::SecretsManager::Secret
        Properties:
            SecretString: !Sub '{"aws_access_key_id": "${PipelineUserAccessKey}", "aws_secret_access_key": "${PipelineUserAccessKey.SecretAccessKey}"}'

    CloudFormationExecutionRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Principal:
                        Service: cloudformation.amazonaws.com
                    Action:
                        - 'sts:AssumeRole'
            Policies:
              - PolicyName: GrantCloudFormationFullAccess
                PolicyDocument:
                    Version: 2012-10-17
                    Statement:
                      - Effect: Allow
                        Action: '*'
                        Resource: '*'

    PipelineExecutionRole:
        Type: AWS::IAM::Role
        Properties:
            Tags:
              - Key: Role
                Value: pipeline-execution-role
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Principal:
                        AWS: !GetAtt PipelineUser.Arn
                    Action:
                        - 'sts:AssumeRole'
                  - Effect: Allow
                    Principal:
                      # Allow roles with tag Role=aws-sam-pipeline-codebuild-service-role to assume this role.
                      # This is required when CodePipeline is the CI/CD system of choice.
                        AWS: !Ref AWS::AccountId
                    Action:
                        - 'sts:AssumeRole'
                    Condition:
                        StringEquals:
                            aws:PrincipalTag/Role: aws-sam-pipeline-codebuild-service-role

    ArtifactsBucket:
        Type: AWS::S3::Bucket

    ArtifactsBucketPolicy:
        Type: AWS::S3::BucketPolicy
        Properties:
            Bucket: !Ref ArtifactsBucket
            PolicyDocument:
                Statement:
                  - Effect: "Deny"
                    Action: "s3:*"
                    Principal: "*"
                    Resource:
                        - !Join [ '',[ !GetAtt ArtifactsBucket.Arn, '/*' ] ]
                        - !GetAtt ArtifactsBucket.Arn
                    Condition:
                        Bool:
                            aws:SecureTransport: false
                  - Effect: "Allow"
                    Action:
                        - 's3:GetObject*'
                        - 's3:PutObject*'
                        - 's3:GetBucket*'
                        - 's3:List*'
                    Resource:
                        - !Join ['',[!GetAtt ArtifactsBucket.Arn, '/*']]
                        - !GetAtt ArtifactsBucket.Arn
                    Principal:
                        AWS:
                            - !GetAtt PipelineExecutionRole.Arn
                            - !GetAtt CloudFormationExecutionRole.Arn
                            - !Sub arn:aws:iam::${RepositoryAccountId}:root

    ArtifactsBucketKey:
        Type: AWS::KMS::Key
        Properties:
            Description: Encription key for Artifacts Bucket
            KeyPolicy:
                Version: 2012-10-17
                Id: key-default-1
                Statement:
                  - Sid: Enable IAM User Permissions
                    Effect: Allow
                    Principal:
                        AWS:
                            - !Sub arn:aws:iam::${AWS::AccountId}:root
                    Action: kms:*
                    Resource: "*"
                  - Sid: Allow use by repository account and pipeline
                    Effect: Allow
                    Principal:
                        AWS: "*"
                    Action:
                        - kms:DescribeKey
                        - kms:Encrypt
                        - kms:Decrypt
                        - kms:ReEncrypt*
                        - kms:GenerateDataKey
                        - kms:GenerateDataKeyWithoutPlaintext
                    Resource: "*"
                    Condition:
                        ArnEquals:
                            aws:PrincipalArn:
                                - !Sub arn:aws:iam::${AWS::AccountId}:root
                                - !Sub "arn:aws:iam::${RepositoryAccountId}:role/*"
                  - Sid: Allow attachment of persistent resources
                    Effect: Allow
                    Principal:
                        AWS: "*"
                    Action:
                        - kms:CreateGrant
                        - kms:ListGrants
                        - kms:RevokeGrant
                    Resource: "*"
                    Condition:
                        Bool:
                            kms:GrantIsForAWSResource: true
                        ArnEquals:
                            aws:PrincipalArn:
                                - !Sub arn:aws:iam::${AWS::AccountId}:root
                                - !Sub "arn:aws:iam::${RepositoryAccountId}:role/*"

    PipelineExecutionRolePermissionPolicy:
        Type: AWS::IAM::Policy
        Properties:
            PolicyName: PipelineExecutionRolePermissions
            PolicyDocument:
                Version: 2012-10-17
                Statement:
                  - Effect: Allow
                    Action: 'iam:PassRole'
                    Resource: !GetAtt CloudFormationExecutionRole.Arn
                  - Effect: Allow
                    Action:
                        - "cloudformation:CreateChangeSet"
                        - "cloudformation:DescribeChangeSet"
                        - "cloudformation:ExecuteChangeSet"
                        - "cloudformation:DeleteStack"
                        - "cloudformation:DescribeStackEvents"
                        - "cloudformation:DescribeStacks"
                        - "cloudformation:GetTemplate"
                        - "cloudformation:GetTemplateSummary"
                        - "cloudformation:DescribeStackResource"
                    Resource: '*'
                  - Effect: Allow
                    Action:
                        - 's3:DeleteObject'
                        - 's3:GetObject*'
                        - 's3:PutObject*'
                        - 's3:GetBucket*'
                        - 's3:List*'
                    Resource:
                        - !Join [ '',[ !GetAtt ArtifactsBucket.Arn, '/*' ] ]
                        - !GetAtt ArtifactsBucket.Arn
            Roles:
                - !Ref PipelineExecutionRole

    EventBus:
        Type: AWS::Events::EventBus
        Properties:
            Name: !Ref EventBusName

    EventBusPolicy:
        Type: AWS::Events::EventBusPolicy
        Properties:
            EventBusName: !Ref EventBus
            StatementId: RepositoryStateChange
            Statement:
                Effect: Allow
                Principal:
                    AWS: !Sub arn:aws:iam::${RepositoryAccountId}:root
                Action: events:PutEvents
                Resource: !GetAtt EventBus.Arn

Outputs:
    PipelineUser:
        Description: ARN of the Pipeline IAM User
        Value: !GetAtt PipelineUser.Arn
  
    PipelineUserSecretKey:
        Description: AWS Access Key and Secret Key of pipeline user.
        Value: !Ref PipelineUserSecretKey
  
    CloudFormationExecutionRole:
        Description: ARN of the IAM Role(CloudFormationExecutionRole)
        Value: !GetAtt CloudFormationExecutionRole.Arn
  
    PipelineExecutionRole:
        Description: ARN of the IAM Role(PipelineExecutionRole)
        Value: !GetAtt PipelineExecutionRole.Arn
  
    ArtifactsBucket:
        Description: ARN of the Artifacts bucket
        Value: !GetAtt ArtifactsBucket.Arn
  
    ArtifactsBucketKey:
        Description: ARN of the Artifacts bucket encryption key
        Value: !GetAtt ArtifactsBucketKey.Arn

CodePipelineと関連リソース

PipelineAccountでCodePipelineと関連するリソースを作成します。

CloudFormationのパラメータには、先ほどのSAMパイプライン共通リソースで作成されたいくかのリソースを指定します。作成されたCloudFormationスタックの出力からコピーします。

  • PipelineExecutionRole (共通リソースのPipelineExecutionRole)
  • CloudFormationExecutionRole (共通リソースのCloudFormationExecutionRole)
  • PipelineArtifactsBucket (共通リソースのArtifactsBucketのBucket名)
  • PipelineArtifactsBucketKey (共通リソースのArtifactsBucketKey)

また、CodeCommitを設置したRepositoryAccountで作成したCloudFormationスタックから、以下のパラメータをコピーします。

  • RepositoryAccountId (CodeCommit設置AWSアカウントID)
  • CodeCommitRoleArn (CodeCommitリソースのCodeCommitRole)

このCloudFormationテンプレートはsam pipeline init --bootstrapで自動作成されるcodepipeline.yamlを修正して作成しました。

AWSTemplateFormatVersion : '2010-09-09'
Description: >
  This template deploys a CodePipeline with its required resources.

Parameters:
  CodeCommitRepositoryName:
    Type: String
    Default: "zipcode"
  GitBranch:
    Type: String
    Default: "main"
  SamTemplate:
    Type: String
    Default: "template.yaml"
  Region:
    Type: String
    Default: "ap-northeast-1"
  TestingStackName:
    Type: String
    Default: "ZipCodeStaging"
  ProdStackName:
    Type: String
    Default: "ZipCode"
  PipelineExecutionRole:
    Type: String
    Default: "arn:aws:iam::123456789012:role/SamPipelineResource-PipelineExecutionRole-XXXXXXXXXXXX"
  CloudFormationExecutionRole:
    Type: String
    Default: "arn:aws:iam::123456789012:role/SamPipelineResource-CloudFormationExecutionRole-XXXXXXXXXXXX"
  PipelineArtifactsBucket:
    Type: String
    Default: "sampipelineresource-artifactsbucket-xxxxxxxxxxxxx"
  PipelineArtifactsBucketKey:
    Type: String
    Default: "arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  RepositoryAccountId:
    Description: Account ID of CodeCommit
    Type: String
    Default: 987654321098
  CodeCommitRoleArn:
    Description: ARN of CodeCommit access Role
    Type: String
    Default: "arn:aws:iam::987654321098:role/ZipCodeRepository-CodeCommitRole-XXXXXXXXXXXX"
  EventBusName:
    Description: Event Bus name for CodeCommit state change notification
    Type: String
    Default: codecommit
  BuildLogRetentionInDays:
    Description: Retention days of build log on CloudWatch Logs LogGroup
    Type: Number
    Default: 1

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:
      EventBusName: !Ref EventBusName
      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:
            - !Ref GitBranch
      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
        EncryptionKey:
          Type: KMS
          Id: !Ref PipelineArtifactsBucketKey
      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: !Ref GitBranch
              OutputArtifacts:
                - Name: SourceCodeAsZip
              RunOrder: 1
              RoleArn: !Ref CodeCommitRoleArn
        - 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 |
                  {
                    "CodeCommitRepositoryName": "${CodeCommitRepositoryName}",
                    "GitBranch": "${GitBranch}",
                    "Region": "${Region}",
                    "TestingStackName": "${TestingStackName}",
                    "ProdStackName": "${ProdStackName}",
                    "PipelineExecutionRole": "${PipelineExecutionRole}",
                    "CloudFormationExecutionRole": "${CloudFormationExecutionRole}",
                    "PipelineArtifactsBucket": "${PipelineArtifactsBucket}",
                    "PipelineArtifactsBucketKey": "${PipelineArtifactsBucketKey}",
                    "RepositoryAccountId": "${RepositoryAccountId}",
                    "CodeCommitRoleArn": "${CodeCommitRoleArn}",
                    "EventBusName": "${EventBusName}",
                    "BuildLogRetentionInDays": "${BuildLogRetentionInDays}"
                  }
              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

        - Name: BuildAndPackage
          Actions:
            - Name: CodeBuild
              ActionTypeId:
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: "1"
              Configuration:
                ProjectName: !Ref CodeBuildProjectBuildAndPackage
              InputArtifacts:
                - Name: SourceCodeAsZip
              OutputArtifacts:
                - Name: BuildArtifactAsZip

        - 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": "${Region}"},
                    {"name": "ENV_STACK_NAME", "value": "${TestingStackName}"},
                    {"name": "ENV_STAGE", "value": "Staging"},
                    {"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${PipelineExecutionRole}"},
                    {"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${CloudFormationExecutionRole}"},
                    {"name": "ENV_BUCKET", "value": "${PipelineArtifactsBucket}"}
                  ]
              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

        - 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": "${Region}"},
                    {"name": "ENV_STACK_NAME", "value": "${ProdStackName}"},
                    {"name": "ENV_STAGE", "value": "Production"},
                    {"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${PipelineExecutionRole}"},
                    {"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${CloudFormationExecutionRole}"},
                    {"name": "ENV_BUCKET", "value": "${PipelineArtifactsBucket}"}
                  ]
              InputArtifacts:
                - Name: BuildArtifactAsZip

  CodePipelineExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action:
              - "sts:AssumeRole"
            Effect: Allow
            Principal:
              Service:
                - codepipeline.amazonaws.com
      Policies:
        - PolicyName: CrossAccountAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "sts:assumeRole"
                Resource:
                  - !Sub "arn:aws:iam::${RepositoryAccountId}:role/*"
        - PolicyName: Decrypt
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "kms:Decrypt"
                Resource:
                  - !Ref PipelineArtifactsBucketKey
        - 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: !Sub "arn:aws:s3:::${PipelineArtifactsBucket}"
              - Action:
                  - "s3:GetObject"
                  - "s3:GetObjectVersion"
                  - "s3:PutObject"
                Effect: Allow
                Resource: !Sub "arn:aws:s3:::${PipelineArtifactsBucket}/*"

        - 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
                  - !GetAtt CodeBuildProjectBuildAndPackage.Arn
                  # Uncomment the following step for running the integration tests
                  # - !GetAtt CodeBuildProjectIntegrationTest.Arn
                  - !GetAtt CodeBuildProjectDeploy.Arn
              - 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:
    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
    DependsOn:
      - PipelineStackCloudFormationExecutionRole
    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
  #     LogsConfig:
  #       CloudWatchLogs:
  #         GroupName: !Ref UnitTestLogGroup
  #         Status: ENABLED

  CodeBuildProjectBuildAndPackage:
    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 Region
          - Name: PROD_REGION
            Value: !Ref Region
          - Name: TESTING_PIPELINE_EXECUTION_ROLE
            Value: !Ref PipelineExecutionRole
          - Name: PROD_PIPELINE_EXECUTION_ROLE
            Value: !Ref PipelineExecutionRole
          - Name: TESTING_ARTIFACT_BUCKET
            Value: !Ref PipelineArtifactsBucket
          - Name: PROD_ARTIFACT_BUCKET
            Value: !Ref PipelineArtifactsBucket
      ServiceRole: !GetAtt CodeBuildServiceRole.Arn
      Source:
        Type: CODEPIPELINE
        BuildSpec: pipeline/buildspec_build_package.yml
      LogsConfig:
        CloudWatchLogs:
          GroupName: !Ref BuildPackageLogGroup
          Status: ENABLED

  # 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
  #     LogsConfig:
  #       CloudWatchLogs:
  #         GroupName: !Ref IntegrationTestLogGroup
  #         Status: ENABLED

  CodeBuildProjectDeploy:
    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
      LogsConfig:
        CloudWatchLogs:
          GroupName: !Ref DeployLogGroup
          Status: ENABLED

  BuildPackageLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/codebuild/${AWS::StackName}-BuildPackage"
      RetentionInDays: !Ref BuildLogRetentionInDays

  DeployLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/codebuild/${AWS::StackName}-Deploy"
      RetentionInDays: !Ref BuildLogRetentionInDays

上に掲載したcodepipeline.yamlは長いので、オリジナルからの変更部分(diff -bu)の結果を掲載します。diffの出力も長いですが…

diffの結果が分かりやすいように、コメントアウトされているUnitTestとIntegrationテストの部分は削除してからdiffを実行しました。

@@ -6,59 +6,50 @@
   CodeCommitRepositoryName:
     Type: String
     Default: "zipcode"
-  MainGitBranch:
+  GitBranch:
     Type: String
     Default: "main"
   SamTemplate:
     Type: String
     Default: "template.yaml"
-  TestingRegion:
+  Region:
     Type: String
     Default: "ap-northeast-1"
   TestingStackName:
     Type: String
     Default: "ZipCodeStaging"
-  TestingPipelineExecutionRole:
-    Type: String
-    Default: "arn:aws:iam::123456789012:role/aws-sam-cli-managed-staging-PipelineExecutionRole-XXXXXXXXXXXXX"
-  TestingCloudFormationExecutionRole:
-    Type: String
-    Default: "arn:aws:iam::123456789012:role/aws-sam-cli-managed-stagi-CloudFormationExecutionR-XXXXXXXXXXXXX"
-  TestingArtifactBucket:
-    Type: String
-    Default: "aws-sam-cli-managed-staging-pipel-artifactsbucket-xxxxxxxxxxxxx"
-  TestingImageRepository:
-    Type: String
-    Default: ""
-  ProdRegion:
-    Type: String
-    Default: "ap-northeast-1"
   ProdStackName:
     Type: String
     Default: "ZipCode"
-  ProdPipelineExecutionRole:
+  PipelineExecutionRole:
     Type: String
-    Default: "arn:aws:iam::123456789012:role/aws-sam-cli-managed-producti-PipelineExecutionRole-XXXXXXXXXXXX"
-  ProdCloudFormationExecutionExeRole:
+    Default: "arn:aws:iam::123456789012:role/SamPipelineResource-PipelineExecutionRole-XXXXXXXXXXXX"
+  CloudFormationExecutionRole:
     Type: String
-    Default: "arn:aws:iam::123456789012:role/aws-sam-cli-managed-produ-CloudFormationExecutionR-XXXXXXXXXXXXX"
-  ProdArtifactBucket:
+    Default: "arn:aws:iam::123456789012:role/SamPipelineResource-CloudFormationExecutionRole-XXXXXXXXXXXX"
+  PipelineArtifactsBucket:
     Type: String
-    Default: "aws-sam-cli-managed-production-pi-artifactsbucket-xxxxxxxxxxxx"
-  ProdImageRepository:
+    Default: "sampipelineresource-artifactsbucket-xxxxxxxxxxxxx"
+  PipelineArtifactsBucketKey:
     Type: String
-    Default: ""
-  CodeStarConnectionArn:
+    Default: "arn:aws:kms:ap-northeast-1:123456789012:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+  RepositoryAccountId:
+    Description: Account ID of CodeCommit
     Type: String
-    Default: ""
-  FeatureGitBranch:
+    Default: 987654321098
+  CodeCommitRoleArn:
+    Description: ARN of CodeCommit access Role
     Type: String
-    Default: ""
+    Default: "arn:aws:iam::987654321098:role/ZipCodeRepository-CodeCommitRole-XXXXXXXXXXXX"
+  EventBusName:
+    Description: Event Bus name for CodeCommit state change notification
+    Type: String
+    Default: codecommit
+  BuildLogRetentionInDays:
+    Description: Retention days of build log on CloudWatch Logs LogGroup
+    Type: Number
+    Default: 1
 
-Conditions:
-  IsMainBranchPipeline: !Equals [!Ref FeatureGitBranch, ""]
-  IsFeatureBranchPipeline: !Not [Condition: IsMainBranchPipeline]
-
 Resources:
   #   ____
   # / ___|  ___  _   _ _ __ ___ ___
@@ -91,6 +82,7 @@
   CloudWatchEventRule:
     Type: AWS::Events::Rule
     Properties:
+      EventBusName: !Ref EventBusName
       EventPattern:
         source:
           - aws.codecommit
@@ -105,7 +97,7 @@
           referenceType:
             - branch
           referenceName:
-            - !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, !Ref MainGitBranch]
+            - !Ref GitBranch
       Targets:
         - Arn: !Sub "arn:${AWS::Partition}:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}"
           RoleArn: !GetAtt CloudWatchEventRole.Arn
@@ -123,6 +115,9 @@
       ArtifactStore:
         Location: !Ref PipelineArtifactsBucket
         Type: S3
+        EncryptionKey:
+          Type: KMS
+          Id: !Ref PipelineArtifactsBucketKey
       RoleArn: !GetAtt CodePipelineExecutionRole.Arn
       RestartExecutionOnUpdate: true
       Stages:
@@ -137,10 +132,11 @@
               Configuration:
                 RepositoryName: !Ref CodeCommitRepositoryName
                 PollForSourceChanges: false
-                BranchName: !If [IsFeatureBranchPipeline, !Ref FeatureGitBranch, !Ref MainGitBranch]
+                BranchName: !Ref GitBranch
               OutputArtifacts:
                 - Name: SourceCodeAsZip
               RunOrder: 1
+              RoleArn: !Ref CodeCommitRoleArn
         - Name: UpdatePipeline
           Actions:
             - Name: CreateChangeSet
@@ -158,8 +154,19 @@
                 Capabilities: CAPABILITY_NAMED_IAM
                 ParameterOverrides: !Sub |
                   {
-                    "FeatureGitBranch": "${FeatureGitBranch}",
-                    "CodeStarConnectionArn": "${CodeStarConnectionArn}"
+                    "CodeCommitRepositoryName": "${CodeCommitRepositoryName}",
+                    "GitBranch": "${GitBranch}",
+                    "Region": "${Region}",
+                    "TestingStackName": "${TestingStackName}",
+                    "ProdStackName": "${ProdStackName}",
+                    "PipelineExecutionRole": "${PipelineExecutionRole}",
+                    "CloudFormationExecutionRole": "${CloudFormationExecutionRole}",
+                    "PipelineArtifactsBucket": "${PipelineArtifactsBucket}",
+                    "PipelineArtifactsBucketKey": "${PipelineArtifactsBucketKey}",
+                    "RepositoryAccountId": "${RepositoryAccountId}",
+                    "CodeCommitRoleArn": "${CodeCommitRoleArn}",
+                    "EventBusName": "${EventBusName}",
+                    "BuildLogRetentionInDays": "${BuildLogRetentionInDays}"
                   }
               InputArtifacts:
                 - Name: SourceCodeAsZip
@@ -179,24 +186,6 @@
                 - Name: !Sub ${AWS::StackName}ChangeSet
               RunOrder: 2
 
-        - !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
@@ -211,10 +200,7 @@
                   - Name: SourceCodeAsZip
                 OutputArtifacts:
                   - Name: BuildArtifactAsZip
-          - !Ref AWS::NoValue
 
-        - !If
-          - IsMainBranchPipeline
         - Name: DeployTest
           Actions:
             - Name: DeployTest
@@ -228,20 +214,16 @@
                   EnvironmentVariables: !Sub |
                     [
                       {"name": "ENV_TEMPLATE", "value": "packaged-test.yaml"},
-                      {"name": "ENV_REGION", "value": "${TestingRegion}"},
+                    {"name": "ENV_REGION", "value": "${Region}"},
                       {"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}"}
+                    {"name": "ENV_STAGE", "value": "Staging"},
+                    {"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${PipelineExecutionRole}"},
+                    {"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${CloudFormationExecutionRole}"},
+                    {"name": "ENV_BUCKET", "value": "${PipelineArtifactsBucket}"}
                     ]
                 InputArtifacts:
                   - Name: BuildArtifactAsZip
                 RunOrder: 1
-          - !Ref AWS::NoValue
-
-        - !If
-          - IsMainBranchPipeline
         - Name: DeployProd
           Actions:
             - Name: DeployProd
@@ -256,90 +238,16 @@
                   EnvironmentVariables: !Sub |
                     [
                       {"name": "ENV_TEMPLATE", "value": "packaged-prod.yaml"},
-                      {"name": "ENV_REGION", "value": "${ProdRegion}"},
+                    {"name": "ENV_REGION", "value": "${Region}"},
                       {"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}"}
+                    {"name": "ENV_STAGE", "value": "Production"},
+                    {"name": "ENV_PIPELINE_EXECUTION_ROLE", "value": "${PipelineExecutionRole}"},
+                    {"name": "ENV_CLOUDFORMATION_EXECUTION_ROLE", "value": "${CloudFormationExecutionRole}"},
+                    {"name": "ENV_BUCKET", "value": "${PipelineArtifactsBucket}"}
                     ]
                 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"
-      OwnershipControls:
-        Rules:
-          - ObjectOwnership:  ObjectWriter
-      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:
@@ -353,6 +261,24 @@
               Service:
                 - codepipeline.amazonaws.com
       Policies:
+        - PolicyName: CrossAccountAccess
+          PolicyDocument:
+            Version: "2012-10-17"
+            Statement:
+              - Effect: Allow
+                Action:
+                  - "sts:assumeRole"
+                Resource:
+                  - !Sub "arn:aws:iam::${RepositoryAccountId}:role/*"
+        - PolicyName: Decrypt
+          PolicyDocument:
+            Version: "2012-10-17"
+            Statement:
+              - Effect: Allow
+                Action:
+                  - "kms:Decrypt"
+                Resource:
+                  - !Ref PipelineArtifactsBucketKey
         - PolicyName: CodePipelineAccess
           PolicyDocument:
             Version: "2012-10-17"
@@ -382,17 +308,13 @@
                   - s3:GetBucketAcl
                   - s3:GetBucketLocation
                 Effect: Allow
-                Resource:
-                  Fn::GetAtt:
-                    - PipelineArtifactsBucket
-                    - Arn
+                Resource: !Sub "arn:aws:s3:::${PipelineArtifactsBucket}"
               - Action:
                   - "s3:GetObject"
                   - "s3:GetObjectVersion"
                   - "s3:PutObject"
                 Effect: Allow
-                Resource:
-                  Fn::Sub: ${PipelineArtifactsBucket.Arn}/*
+                Resource: !Sub "arn:aws:s3:::${PipelineArtifactsBucket}/*"
 
         - PolicyName: CodePipelineCodeBuildAndCloudformationAccess
           PolicyDocument:
@@ -403,18 +325,8 @@
                   - "codebuild:StartBuild"
                   - "codebuild:BatchGetBuilds"
                 Resource:
-                  - !If
-                    - IsFeatureBranchPipeline
-                    - !GetAtt CodeBuildProjectBuildAndDeployFeature.Arn
-                    - !Ref AWS::NoValue
-                  - !If
-                    - IsMainBranchPipeline
                     - !GetAtt CodeBuildProjectBuildAndPackage.Arn
-                    - !Ref AWS::NoValue
-                  - !If
-                    - IsMainBranchPipeline
                     - !GetAtt CodeBuildProjectDeploy.Arn
-                    - !Ref AWS::NoValue
               - Effect: Allow
                 Action:
                   - "cloudformation:CreateStack"
@@ -457,6 +369,8 @@
   #  \____\___/ \__,_|\___|____/ \__,_|_|_|\__,_|
   CodeBuildServiceRole:
     Type: AWS::IAM::Role
+    DependsOn:
+      - PipelineStackCloudFormationExecutionRole
     Properties:
       Tags:
         - Key: Role
@@ -505,39 +419,7 @@
                   StringEquals:
                     aws:ResourceTag/Role: pipeline-execution-role
 
-  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:
@@ -551,28 +433,27 @@
           - Name: SAM_TEMPLATE
             Value: !Ref SamTemplate
           - Name: TESTING_REGION
-            Value: !Ref TestingRegion
+            Value: !Ref Region
           - Name: PROD_REGION
-            Value: !Ref ProdRegion
+            Value: !Ref Region
           - Name: TESTING_PIPELINE_EXECUTION_ROLE
-            Value: !Ref TestingPipelineExecutionRole
+            Value: !Ref PipelineExecutionRole
           - Name: PROD_PIPELINE_EXECUTION_ROLE
-            Value: !Ref ProdPipelineExecutionRole
+            Value: !Ref PipelineExecutionRole
           - Name: TESTING_ARTIFACT_BUCKET
-            Value: !Ref TestingArtifactBucket
+            Value: !Ref PipelineArtifactsBucket
           - Name: PROD_ARTIFACT_BUCKET
-            Value: !Ref ProdArtifactBucket
-          - Name: TESTING_IMAGE_REPOSITORY
-            Value: !Ref TestingImageRepository
-          - Name: PROD_IMAGE_REPOSITORY
-            Value: !Ref ProdImageRepository
+            Value: !Ref PipelineArtifactsBucket
       ServiceRole: !GetAtt CodeBuildServiceRole.Arn
       Source:
         Type: CODEPIPELINE
         BuildSpec: pipeline/buildspec_build_package.yml
+      LogsConfig:
+        CloudWatchLogs:
+          GroupName: !Ref BuildPackageLogGroup
+          Status: ENABLED
 
   CodeBuildProjectDeploy:
-    Condition: IsMainBranchPipeline
     Type: AWS::CodeBuild::Project
     Properties:
       Artifacts:
@@ -585,3 +466,19 @@
       Source:
         Type: CODEPIPELINE
         BuildSpec: pipeline/buildspec_deploy.yml
+      LogsConfig:
+        CloudWatchLogs:
+          GroupName: !Ref DeployLogGroup
+          Status: ENABLED
+
+  BuildPackageLogGroup:
+    Type: AWS::Logs::LogGroup
+    Properties:
+      LogGroupName: !Sub "/aws/codebuild/${AWS::StackName}-BuildPackage"
+      RetentionInDays: !Ref BuildLogRetentionInDays
+
+  DeployLogGroup:
+    Type: AWS::Logs::LogGroup
+    Properties:
+      LogGroupName: !Sub "/aws/codebuild/${AWS::StackName}-Deploy"
+      RetentionInDays: !Ref BuildLogRetentionInDays

ビルドスクリプトの修正

デプロイ時に現在のステージ(Staging/Production)を判断するために、デプロイで起動されるCodeBuild(CodeBuildProjectDeploy)実行時の環境変数にENV_STAGEを追加しています。この環境変数を参照するように、デフォルトのpipeline/buildspec_deploy.ymlにも下記の修正をしました。

template.yamlのStageパラメータにENV_STAGEの値が渡されます。

--- a/pipeline/buildspec_deploy.yml
+++ b/pipeline/buildspec_deploy.yml
@@ -10,6 +10,7 @@ phases:
     commands:
       - . ./assume-role.sh ${ENV_PIPELINE_EXECUTION_ROLE} deploy
       - sam deploy --stack-name ${ENV_STACK_NAME}
+                    --parameter-overrides "Stage=${ENV_STAGE}"
                     --template ${ENV_TEMPLATE}
                     --capabilities CAPABILITY_IAM
                     --region ${ENV_REGION}

まとめ

CodeCommitを別のAWSアカウントに設置する構成で、SAMのCI/CDパイプラインを構築できました。

CloudFormationテンプレートは汎用的に作成されていますので、今回のテンプレートをコピーして若干の修正をすることで、今後作成する新しいアプリケーションにも使いまわしていくことが可能です。

工作クラブ

工作クラブ

記事一覧

「マーケライズ工作クラブ」で役に立つものを作り、その過程で新しい技術を習得してスキルアップしていきましょう。