ポーリングベースのCodePipelineをイベントベースへ移行してみた[AWS CodePipeline+AWS CloudFormation]

こんにちは。SCSKのふくちーぬです。

皆さんは、イベントベースのCodePipelineのパイプラインを利用していますでしょうか。それともポーリングベースのパイプラインを利用していますでしょうか。

まだポーリングベースを利用している方は、本記事を読んでいただきイベントベースへの移行をしていただけると幸いです。(ふくちーぬもその内の1人でした。。)

本記事では、以下のAWS公式ドキュメントに従いCodeCommitをソースとしたCloudFormationで構築済みのポーリングベースのCodepipelineからイベントベースへの移行を実施します。

CodePipelineの検出オプションの種類

CodePipelineには、ソースの検出オプションとしてポーリングベース・イベントベースの2種類があります。

ポーリングベースでは、CodePipelineがCodeCommit内の対象ブランチに対して定期的に監視することで、変更を検出しています。

イベントベースでは、対象ブランチに変更があった際にEventBridgeにイベントを送信するだけです。すなわちCodePipelineは、どんと構えてEventBridgeからのメッセージを待っていればよいだけです。

ポーリングベースのパイプラインの移行を推奨している件

2023年4月下旬に、AWS サポートから以下のようなメールが届きました。

本メッセージは、1つ以上ポーリングパイプラインをお持ちのお客様にお送りしています。ポーリングパイプラインは、変更をポーリングするように設定されたソースアクションが少なくとも1つあるパイプラインとして定義されます。AWS CodePipelineチームは、2023年5月25日より、非アクティブなパイプラインでのポーリングを無効にします。非アクティブなパイプラインとは、過去30日間にパイプラインの実行が開始されていないパイプラインと定義されます。Amazon EventBridgeルール、AWS CodeStar接続、またはウェブフックのみを使用してパイプラインをトリガーする非アクティブなパイプラインは影響を受けません。また、アクティブなパイプラインも影響を受けません。

ソースのトリガーメカニズムとして、Amazon EventBridge、AWS CodeStar接続、またはウェブフックを使用するように更新することをお勧めします。

つまり、「30日以上利用されていないポーリングベースのパイプラインを無効にする」、「ポーリングベースのパイプラインを移行することを推奨する」のアナウンスをしています。

現時点でも、ポーリングベースのパイプラインを新規に作成することはできます。また無効になったパイプラインも手動で実行することで、パイプラインを起動することは可能です。

推測ですが、AWS的にCodePipelineがポーリングするコストが割りに合っていないことも要因の1つかもしれませんね。

イベントベースのパイプラインを推奨する理由

  • ソースリポジトリのブランチの変更を迅速に検出できるようになり、パイプライン実行パフォーマンスが向上する

CI/CDパイプラインの準備

Cloud9の作成

以下に AWS Cloud9 の作成方法を記載しているので参考にしてください。

Cloud9とCodeCommitの連携

Cloud9からCodeCommitにプッシュできるように準備してください。

CodeCommitへのソースコード配置

今回の検証ではCodeCommitに配置するものは任意のもので問題ありませんが、以下を利用することにします。

CI/CDパイプラインの作成

ご使用のAWSアカウント内にポーリングベースのパイプラインがCloudFormationにて作成済みの方は、それをご利用ください。

今回は、新規にポーリングベースのパイプラインを作成した後に、イベントベースへ移行します。

ポーリングベースのパイプラインの作成

ポイントはパイプラインの定義にて、”PollForSourceChanges”を”true”に設定することでポーリングベースのパイプラインを作成できます。

以下のCloudFormationテンプレートをデプロイして、ポーリングベースのパイプラインを構築してください。

AWSTemplateFormatVersion: 2010-09-09
Description: cfn CI/CD Pipeline

Parameters:
  ResourceName:
    Type: String
  REPOSITORYNAME:
    Type: String
    Description: aws codecommit repository name
  STACKNAME:
    Type: String
  MailAddress:
    Type: String

Resources:
  ArtifactStoreBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub s3bucket-${AWS::AccountId}-artifactbucket

  CodeBuildBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub s3bucket-${AWS::AccountId}-codebuildtbucket
# ------------------------------------------------------------#
#  CodeBuild Role (IAM)
# ------------------------------------------------------------#
  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref CodeBuildPolicy
        - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess
      RoleName: !Sub "IRL-CODEBUILD-S3CloudWatchlogsAccess"
# ------------------------------------------------------------#
#  CodeBuild Role Policy (IAM)
# ------------------------------------------------------------#
  CodeBuildPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodeBuildAccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudWatchLogsAccess
            Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource:
              - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*           
          - Sid: S3Access
            Effect: Allow
            Action:
              - s3:PutObject
              - s3:GetObject
              - s3:GetObjectVersion
            Resource:
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*
              - !Sub arn:aws:s3:::${CodeBuildBucket}
              - !Sub arn:aws:s3:::${CodeBuildBucket}/*
          - Sid: IAMPass
            Effect: Allow
            Action:
              - iam:PassRole
            Resource: "*"          
          - Sid: CloudFormationAccess
            Effect: Allow
            Action: cloudformation:ValidateTemplate
            Resource: "*" 
# ------------------------------------------------------------#
#  CodeBuild linter Project
# ------------------------------------------------------------#
  CodeBuildProjectLint:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub ${ResourceName}-project-lint
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        EnvironmentVariables:
          - Name: AWS_REGION
            Value: !Ref AWS::Region
          - Name: S3_BUCKET
            Value: !Ref CodeBuildBucket     
      Source:
        Type: CODEPIPELINE       
# ------------------------------------------------------------#
#  CodeBuild changeset Project
# ------------------------------------------------------------#
  CodeBuildProjectChangeset:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub ${ResourceName}-project-changeset
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        EnvironmentVariables:
          - Name: AWS_REGION
            Value: !Ref AWS::Region
          - Name: S3_BUCKET
            Value: !Ref CodeBuildBucket
          - Name: STACK_NAME
            Value: !Ref STACKNAME
          - Name: CFNROLE_ARN
            Value: !GetAtt  CloudformationRole.Arn
      Source:
        Type: CODEPIPELINE
        BuildSpec: changeset-buildspec.yaml   
# ------------------------------------------------------------#
#  CloudFormation Role (IAM)
# ------------------------------------------------------------#
  CloudformationRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: cloudformation.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
      RoleName: "IRL-CLOUDFORMATION-ServiceFullAccess"
# ------------------------------------------------------------#
#  CodePipeline Role (IAM)
# ------------------------------------------------------------#
  PipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref PipelinePolicy
      RoleName: "IRL-CODEPIPELINE-Access"
# ------------------------------------------------------------#
#  CodePipeline Role Policy (IAM)
# ------------------------------------------------------------#
  PipelinePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodePipelineAccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: S3FullAccess
            Effect: Allow
            Action: s3:*
            Resource:
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*        
          - Sid: FullAccess
            Effect: Allow
            Action:
              - cloudformation:*
              - iam:PassRole
              - codecommit:GetRepository
              - codecommit:ListBranches
              - codecommit:GetUploadArchiveStatus
              - codecommit:UploadArchive
              - codecommit:CancelUploadArchive
              - codecommit:GetBranch
              - codecommit:GetCommit
            Resource: "*"
          - Sid: CodeBuildAccess
            Effect: Allow
            Action:
              - codebuild:BatchGetBuilds
              - codebuild:StartBuild
            Resource: !GetAtt CodeBuildProjectLint.Arn
          - Sid: CodeBuildChangesetAccess
            Effect: Allow
            Action:
              - codebuild:BatchGetBuilds
              - codebuild:StartBuild
            Resource: !GetAtt CodeBuildProjectChangeset.Arn      
          - Sid: SNSAccess
            Effect: Allow
            Action:
              - sns:Publish
            Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${ResourceName}-topic                
# ------------------------------------------------------------#
#  CodePipeline
# ------------------------------------------------------------#
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${ResourceName}-pipeline
      RoleArn: !GetAtt PipelineRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      Stages:
        - Name: Source
          Actions:
            - Name: download-source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration:
                RepositoryName: !Ref REPOSITORYNAME
                BranchName: main
                PollForSourceChanges: true #ポーリングベースのパイプラインでは、"true"に設定
              OutputArtifacts:
                - Name: SourceOutput
        - Name: Test
          Actions:
            - InputArtifacts:
                - Name: SourceOutput
              Name: testing
              ActionTypeId:
                Category: Test
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProjectLint              
        - Name: Build
          Actions:
            - InputArtifacts:
                - Name: SourceOutput
              Name: changeset
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              OutputArtifacts:
                - Name: BuildOutput
              Configuration:
                ProjectName: !Ref CodeBuildProjectChangeset    
              Namespace: BuildVariables
        - Name: Approval
          Actions:
            - Name: approve-changeset
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Version: 1
                Provider: Manual
              Configuration:
                ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId}
                NotificationArn: !GetAtt SNSTopic.TopicArn              
        - Name: Deploy
          Actions:
            - Name: execute-changeset
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                StackName: !Join [ '-', [ !Ref ResourceName, 'infra-stack' ] ]
                ActionMode: CHANGE_SET_EXECUTE
                ChangeSetName: changeset
                RoleArn: !GetAtt CloudformationRole.Arn
# ------------------------------------------------------------#
#  SNS
# ------------------------------------------------------------#
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription: 
        - Endpoint: !Ref MailAddress
          Protocol: email
      TopicName: !Sub ${ResourceName}-topic

ポーリングベースのパイプラインの一覧の取得

今回はboto3(Python)を使用して、ポーリングベースのパイプラインの一覧を取得してみます。

Cloud9へのboto3のインストール

以下のコマンドを実行して、boto3ライブラリのインストールをします。

pip install boto3

以下のコマンドを実行して、バージョンが返却されればインストール済みとなります。今回は、”boto3 1.33.11″が返ってきています。

pip list | grep boto3

Pythonスクリプトの作成

Cloud9におけるGUI経由でのファイル作成方法は、以下を参照ください。

以下のPythonファイルを任意のディレクトリに作成してください。ファイル名は、”PollingPipelinesExtractor.py”とします。

import boto3
import sys
import time
import math

hasNextToken = True
nextToken = ""
pollablePipelines = []
lastExecutedTimes = []
if len(sys.argv) == 1:
    raise Exception("Please provide region name.")
session = boto3.Session(profile_name='default', region_name=sys.argv[1])
codepipeline = session.client('codepipeline')

def is_pollable_action(action):
    actionTypeId = action['actionTypeId']
    configuration = action['configuration']
    return actionTypeId['owner'] in {"AWS", "ThirdParty"} and actionTypeId['provider'] in {"GitHub", "CodeCommit", "S3"} and ('PollForSourceChanges' not in configuration or configuration['PollForSourceChanges'] == 'true')

def has_pollable_actions(pipeline):
    hasPollableAction = False
    pipelineDefinition = codepipeline.get_pipeline(name=pipeline['name'])['pipeline']
    for action in pipelineDefinition['stages'][0]['actions']:
        hasPollableAction = is_pollable_action(action)
        if hasPollableAction:
            break
    return hasPollableAction

def get_last_executed_time(pipelineName):
    pipelineExecutions=codepipeline.list_pipeline_executions(pipelineName=pipelineName)['pipelineExecutionSummaries']
    if pipelineExecutions:
        return pipelineExecutions[0]['startTime'].strftime("%A %m/%d/%Y, %H:%M:%S")
    else:
        return "Not executed in last year"

while hasNextToken:
    if nextToken=="":
        list_pipelines_response = codepipeline.list_pipelines()
    else:
        list_pipelines_response = codepipeline.list_pipelines(nextToken=nextToken)
    if 'nextToken' in list_pipelines_response:
        nextToken = list_pipelines_response['nextToken']
    else:
        hasNextToken= False
    for pipeline in list_pipelines_response['pipelines']:
        if has_pollable_actions(pipeline):
            pollablePipelines.append(pipeline['name'])
            lastExecutedTimes.append(get_last_executed_time(pipeline['name']))

fileName="{region}-{timeNow}.csv".format(region=sys.argv[1],timeNow=math.trunc(time.time()))
file = open(fileName, 'w')

print ("{:<30} {:<30} {:<30}".format('Polling Pipeline Name', '|','Last Executed Time'))
print ("{:<30} {:<30} {:<30}".format('_____________________', '|','__________________'))
for i in range(len(pollablePipelines)):
    print("{:<30} {:<30} {:<30}".format(pollablePipelines[i], '|', lastExecutedTimes[i]))
    file.write("{pipeline},".format(pipeline=pollablePipelines[i]))
file.close()
print("\nSaving Polling Pipeline Names to file {fileName}".format(fileName=fileName)) 

Pythonスクリプトの実行

以下のコマンドを実行して、pythonスクリプトを実行します。

引数として、指定のリージョンを入力します。ここでは、東京リージョンの”ap-northeast-1″を指定します。

python3 PollingPipelinesExtractor.py ap-northeast-1 

 

指定のリージョン内に存在しているポーリングベースのパイプラインの一覧を取得することができます。これで、どのパイプラインがポーリングベースを採用しているのか一目瞭然ですね。またCSVファイルとしても出力されています。

今回は、”cicd-20231210-pipeline”を新規作成したので表示されています。ご自身で作成したものと読み替えてください。

イベントベースのパイプラインへ移行

ポーリングベースからイベントベースへ移行するために、CloudFormationテンプレートの修正をしていきます。

PollForSourceChangesの変更

以下のように、ポーリングベースを採用しないようにCodePipelineの”PollForSourceChanges”プロパティを”false”に変更します。

      Stages:
        - Name: Source
          Actions:
            - Name: download-source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration:
                RepositoryName: !Ref REPOSITORYNAME
                BranchName: main
                PollForSourceChanges: false #イベントベースを採用するためfalseに変更
              OutputArtifacts:
                - Name: SourceOutput 

EventBridgeが利用するIAMポリシー・ロールの追加

以下のように、EventBridgeが利用するためのIAMポリシー・ロールを作成するための記述を追加します。

EventBridgeがCodePipelineを起動できる権限を付与しています。

# ------------------------------------------------------------#
#  CodePipeline Events Role  (IAM)
# ------------------------------------------------------------#
  PipelineEventsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref PipelineEventsPolicy
      RoleName: !Sub "IRL-EVENTBRIDGE-CodePipelineAccess"  
# ------------------------------------------------------------#
#  CodePipeline Events Role Policy (IAM)
# ------------------------------------------------------------#
  PipelineEventsPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodePipelineAccessForEvents
      PolicyDocument:
        Statement:
          - Action: codepipeline:StartPipelineExecution
            Effect: Allow
            Resource:
              - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}            
        Version: "2012-10-17" 

EventBridgeの追加

以下のように、EventBridge本体の記述を追加します。対象のリポジトリや付与するロール等の指定もしています。

これによりCodeCommitのブランチの更新をトリガーにCodePipelineを起動できるようになります。

# ------------------------------------------------------------#
#  EventBridge Rule for Starting CodePipeline
# ------------------------------------------------------------#
  PipelineEventsRule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub ${ResourceName}-rule-pipeline
      EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default"
      EventPattern:
        source:
          - aws.codecommit
        resources:
          - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${REPOSITORYNAME}
        detail-type:
          - "CodeCommit Repository State Change"
        detail:
          event:
            - referenceCreated
            - referenceUpdated
          referenceName:
            - main
      State: ENABLED
      Targets:
        - Arn:
            Fn::Join:
              - ""
              - - "arn:"
                - Ref: AWS::Partition
                - ":codepipeline:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - ":"
                - Ref: Pipeline
          Id: Target
          RoleArn: !GetAtt PipelineEventsRole.Arn 

完成したテンプレート

完成したテンプレートは、以下の通りです。

AWSTemplateFormatVersion: 2010-09-09
Description: cfn CI/CD Pipeline

Parameters:
  ResourceName:
    Type: String
  REPOSITORYNAME:
    Type: String
    Description: aws codecommit repository name
  STACKNAME:
    Type: String
  MailAddress:
    Type: String

Resources:
  ArtifactStoreBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub s3bucket-${AWS::AccountId}-artifactbucket

  CodeBuildBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Retain
    Properties:
      BucketName: !Sub s3bucket-${AWS::AccountId}-codebuildtbucket
# ------------------------------------------------------------#
#  EventBridge Rule for Starting CodePipeline
# ------------------------------------------------------------#
  PipelineEventsRule:
    Type: AWS::Events::Rule
    Properties:
      Name: !Sub ${ResourceName}-rule-pipeline
      EventBusName: !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:event-bus/default"
      EventPattern:
        source:
          - aws.codecommit
        resources:
          - !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${REPOSITORYNAME}
        detail-type:
          - "CodeCommit Repository State Change"
        detail:
          event:
            - referenceCreated
            - referenceUpdated
          referenceName:
            - main
      State: ENABLED
      Targets:
        - Arn:
            Fn::Join:
              - ""
              - - "arn:"
                - Ref: AWS::Partition
                - ":codepipeline:"
                - Ref: AWS::Region
                - ":"
                - Ref: AWS::AccountId
                - ":"
                - Ref: Pipeline
          Id: Target
          RoleArn: !GetAtt PipelineEventsRole.Arn
# ------------------------------------------------------------#
#  CodePipeline Events Role  (IAM)
# ------------------------------------------------------------#
  PipelineEventsRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: events.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref PipelineEventsPolicy
      RoleName: !Sub "IRL-EVENTBRIDGE-CodePipelineAccess"  
# ------------------------------------------------------------#
#  CodePipeline Events Role Policy (IAM)
# ------------------------------------------------------------#
  PipelineEventsPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodePipelineAccessForEvents
      PolicyDocument:
        Statement:
          - Action: codepipeline:StartPipelineExecution
            Effect: Allow
            Resource:
              - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${Pipeline}            
        Version: "2012-10-17"       
# ------------------------------------------------------------#
#  CodeBuild Role (IAM)
# ------------------------------------------------------------#
  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref CodeBuildPolicy
        - arn:aws:iam::aws:policy/AWSCloudFormationFullAccess
      RoleName: !Sub "IRL-CODEBUILD-S3CloudWatchlogsAccess"
# ------------------------------------------------------------#
#  CodeBuild Role Policy (IAM)
# ------------------------------------------------------------#
  CodeBuildPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodeBuildAccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: CloudWatchLogsAccess
            Effect: Allow
            Action:
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource:
              - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*           
          - Sid: S3Access
            Effect: Allow
            Action:
              - s3:PutObject
              - s3:GetObject
              - s3:GetObjectVersion
            Resource:
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*
              - !Sub arn:aws:s3:::${CodeBuildBucket}
              - !Sub arn:aws:s3:::${CodeBuildBucket}/*
          - Sid: IAMPass
            Effect: Allow
            Action:
              - iam:PassRole
            Resource: "*"          
          - Sid: CloudFormationAccess
            Effect: Allow
            Action: cloudformation:ValidateTemplate
            Resource: "*" 
# ------------------------------------------------------------#
#  CodeBuild linter Project
# ------------------------------------------------------------#
  CodeBuildProjectLint:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub ${ResourceName}-project-lint
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        EnvironmentVariables:
          - Name: AWS_REGION
            Value: !Ref AWS::Region
          - Name: S3_BUCKET
            Value: !Ref CodeBuildBucket     
      Source:
        Type: CODEPIPELINE       
# ------------------------------------------------------------#
#  CodeBuild changeset Project
# ------------------------------------------------------------#
  CodeBuildProjectChangeset:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub ${ResourceName}-project-changeset
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Artifacts:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0
        EnvironmentVariables:
          - Name: AWS_REGION
            Value: !Ref AWS::Region
          - Name: S3_BUCKET
            Value: !Ref CodeBuildBucket
          - Name: STACK_NAME
            Value: !Ref STACKNAME
          - Name: CFNROLE_ARN
            Value: !GetAtt  CloudformationRole.Arn
      Source:
        Type: CODEPIPELINE
        BuildSpec: changeset-buildspec.yaml   
# ------------------------------------------------------------#
#  CloudFormation Role (IAM)
# ------------------------------------------------------------#
  CloudformationRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: cloudformation.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AdministratorAccess
      RoleName: "IRL-CLOUDFORMATION-ServiceFullAccess"
# ------------------------------------------------------------#
#  CodePipeline Role (IAM)
# ------------------------------------------------------------#
  PipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Action: sts:AssumeRole
            Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
      Path: /
      ManagedPolicyArns:
        - !Ref PipelinePolicy
      RoleName: "IRL-CODEPIPELINE-Access"
# ------------------------------------------------------------#
#  CodePipeline Role Policy (IAM)
# ------------------------------------------------------------#
  PipelinePolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: CodePipelineAccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: S3FullAccess
            Effect: Allow
            Action: s3:*
            Resource:
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}
              - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*        
          - Sid: FullAccess
            Effect: Allow
            Action:
              - cloudformation:*
              - iam:PassRole
              - codecommit:GetRepository
              - codecommit:ListBranches
              - codecommit:GetUploadArchiveStatus
              - codecommit:UploadArchive
              - codecommit:CancelUploadArchive
              - codecommit:GetBranch
              - codecommit:GetCommit
            Resource: "*"
          - Sid: CodeBuildAccess
            Effect: Allow
            Action:
              - codebuild:BatchGetBuilds
              - codebuild:StartBuild
            Resource: !GetAtt CodeBuildProjectLint.Arn
          - Sid: CodeBuildChangesetAccess
            Effect: Allow
            Action:
              - codebuild:BatchGetBuilds
              - codebuild:StartBuild
            Resource: !GetAtt CodeBuildProjectChangeset.Arn      
          - Sid: SNSAccess
            Effect: Allow
            Action:
              - sns:Publish
            Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${ResourceName}-topic                
# ------------------------------------------------------------#
#  CodePipeline
# ------------------------------------------------------------#
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${ResourceName}-pipeline
      RoleArn: !GetAtt PipelineRole.Arn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      Stages:
        - Name: Source
          Actions:
            - Name: download-source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration:
                RepositoryName: !Ref REPOSITORYNAME
                BranchName: main
                PollForSourceChanges: false #イベントベースを採用するためfalseに変更
              OutputArtifacts:
                - Name: SourceOutput
        - Name: Test
          Actions:
            - InputArtifacts:
                - Name: SourceOutput
              Name: testing
              ActionTypeId:
                Category: Test
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProjectLint              
        - Name: Build
          Actions:
            - InputArtifacts:
                - Name: SourceOutput
              Name: changeset
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              OutputArtifacts:
                - Name: BuildOutput
              Configuration:
                ProjectName: !Ref CodeBuildProjectChangeset    
              Namespace: BuildVariables
        - Name: Approval
          Actions:
            - Name: approve-changeset
              ActionTypeId:
                Category: Approval
                Owner: AWS
                Version: 1
                Provider: Manual
              Configuration:
                ExternalEntityLink: !Sub https://${AWS::Region}.console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/changesets/changes?stackId=#{BuildVariables.SackId}&changeSetId=#{BuildVariables.ChangeSetId}
                NotificationArn: !GetAtt SNSTopic.TopicArn              
        - Name: Deploy
          Actions:
            - Name: execute-changeset
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: CloudFormation
              Configuration:
                StackName: !Join [ '-', [ !Ref ResourceName, 'infra-stack' ] ]
                ActionMode: CHANGE_SET_EXECUTE
                ChangeSetName: changeset
                RoleArn: !GetAtt CloudformationRole.Arn
# ------------------------------------------------------------#
#  SNS
# ------------------------------------------------------------#
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription: 
        - Endpoint: !Ref MailAddress
          Protocol: email
      TopicName: !Sub ${ResourceName}-topic 

CI/CDパイプラインの更新

上記テンプレートを使用して、CI/CDパイプラインのスタックを更新します。

CloudFormationコンソールから該当のスタック選択して、”更新”を押下してください。

 

修正済みのテンプレートファイルをアップデートして、”次へ”を押下してください。

 

既存のパラメータ等は変更することなく、”次へ”を押下してください。

“次へ”を押下してください。

 

最後にレビュー画面に遷移します。内容に間違いがないか確認します。

変更セットのレビューにて、IAMポリシー・IAMロール・EventBridgeルールが追加されることを確認します。またCodePipelineでは、変更があることも確認します。問題なければ”送信”を押下してください。

 

数分経過すると、スタックの更新が完了します。

移行の確認

移行が完了したかどうか、こちらの手順で再度スクリプトを実行することで確かめます。

先ほどまで存在した”cicd-20231210-pipeline”が表示されていないため、イベントベースのパイプラインとして正常に認識されています。

これにて移行完了となります。お疲れ様でした。

パイプライン起動確認

念の為、イベントベースのパイプラインが起動するか確認しておきます。

CodeCommitにプッシュすると、パイプラインが起動していることが分かります。(承認ステージを組み込んでいるため、レビュー中で一時停止しています。)

また、イベントベースへの移行を勧める警告メッセージも表示されなくなっています。

ちなみに、ポーリングベースのパイプラインを利用していると、CodePipelineの対象パイプラインの画面を開くたびに以下のように警告メッセージが表示されます。

パイプラインがアクティブの場合:

このパイプラインには、ポーリング用に設定されたソース(download-source)があります。変更検出用に推奨されるイベントベースのメカニズムにパイプラインを移行(更新)します。詳細については、「移行ガイド」を参照してください。

 

30日以上パイプラインを起動していない(アクティブでない)場合:

○か月前時点で、CodePipelineはこのパイプラインのポーリングを無効にしています。これは、以前30日間に実行されていないためです。推奨されるイベントベースのメカニズムを使用して、新しいパイプライン実行を開始します。

最後に

いかがでしたでしょうか。

CodeCommitをソースとしたCloudFormationで構築済みのポーリングベースのCodePipelineをイベントベースへと移行しました。

既存のCodePipelineでポーリングベースを利用している方は、是非この手順に沿って移行の検討をしていただければと思います。また新規でパイプラインを構築する際は、イベントベースでパイプラインを構築するように意識してください。

本記事が皆様のお役にたてば幸いです。

ではサウナラ~🔥

タイトルとURLをコピーしました