CodePipelineで推奨のEventBridgeがクロスアカウントだと中々大変な件
記事の内容
少し前の話なのですが、2023年8月頃からCodePipelineのCodeCommitの検出がポーリングで非推奨となっておりその対応としてEventBridgeによるイベント駆動にする必要がありました。
この関係でCodePipelineを使ってCI/CDを構築する際、イベント駆動にしたかったのですが、プロジェクトの環境的にCodeCommitとCodePipelineが異なるアカウント同士いわゆるクロスアカウントの関係で中々大変でした。
備忘録として今回の作業内容をまとめてみたいと思います。
アーキテクチャ
今回のアーキテクチャイメージです。
before
CodePipelineのポーリングでCodeCommitソースにアクセスする場合、CodePipelineが能動的にCodeCommitの変更を確認するので、CodePipelineのIAM RoleとPolicyを気をつければよいです。
例えば、CI/CD上ではCodeCommitのソースはS3に展開された上でフローが実行されるため、CodeCommit側のアカウント(上記でいうcommon環境)のS3がある場合、CodePipelineはCodeCommit側のS3にアクセスできるようにする必要があります。
after
一方で、EventBridgeでCodeCommitの変更をトリガーするイベント駆動でCodePipelineがソースにアクセスする場合、クロスアカウントになるとCodeCommitアカウント側のEventBridgeのイベントをCodePipeline側のアカウント(上記でいうdev)に転送する必要があります。
この役割を担うのがEventBridgeのBusというやつになります。AWSアカウントにはdefaultのBusが既にあるのですが、これは基本的に自アカウント内で完結するイベントの受信に使うので、他アカウントのイベントの受信はできない状態です。
クロスアカウントの場合、他アカウントのEventBridgeをトリガーできるようには初めからなっていないのでアカウント間でこの設定もしてやる必要があります。
関係する技術・サービス・ツール
- Amazon EventBridge
- AWS CloudFormation
- AWS CodePipeline
- AWS CodeCommit
- Amazon S3
前提
- CodeCommitとS3は事前に作成済み
作業の流れ
- CodePipelineアカウントでCodePipelineサービスロールを作成
- CodeCommitアカウントでEventBridge関係のリソースを作成
- CodePipelineアカウントでEventBridge関係とCodePipelineのリソースを作成
- CodeCommitをトリガーしてCodePipeline動作確認
1. CodePipelineアカウントでCodePipelineサービスロールを作成
まずCodePipelineアカウント(dev環境)でCodePipelineのサービスロールを作成します。
今回はCloudFormationで作成しました。
AWSTemplateFormatVersion: "2010-09-09"
Description: CodePipeline Service Role for trigger AWS Account
Parameters:
ProjectEnv:
Description: Envirment of project
Type: String
AllowedValues:
- prd
- sta
- dev
TriggerAccount:
Type: String
Description: Account to trigger the pipeline
Default: <CodeCommit AWSアカウントID>
Resources:
CodePipelineServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub CodePipelineServiceRole-${ProjectEnv}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: CodePipelineServiceRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource: !Sub arn:aws:iam::${TriggerAccount}:role/*
ParameterでCodeCommit AWSアカウントを指定できるようにしています。
これによりCodePipelineがCodeCommitのあるアカウントのIAM Roleを引き受けることできるようになります。
これは後ほどCodeCommmitアカウント側に作成するS3とCodeCommitにアクセスするロールを引き受けることになります。
2. CodeCommitアカウントでEventBridge関係のリソースを作成
次にCodeCommit側のアカウントでEventBridge関係のリソースやIAM Roleを作成します。
AWSTemplateFormatVersion: "2010-09-09"
Description: CodeCommit Trigger Event Rule for trigger AWS Account
Parameters:
ProjectEnv:
Description: Envirment of project
Type: String
AllowedValues:
- prd
- stg
- dev
PipelineAccount:
Type: String
Description: Pipeline Account
Default: <CodePipeline AWSアカウントID>
BranchName:
Type: String
Description: Branch Name
Default: main
Resources:
CodeCommitTriggerEventRule:
Type: AWS::Events::Rule
Properties:
Name: !Sub CodeCommitTriggerEvent_${ProjectEnv}
Description: CodeCommit Trigger Event Rule
EventBusName: default
EventPattern:
source:
- aws.codecommit
detail-type:
- CodeCommit Repository State Change
resources:
- prefix: !Sub arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:test-codecommit-repo
detail:
referenceType:
- branch
event:
- referenceCreated
- referenceUpdated
referenceName:
- !Ref BranchName
State: ENABLED
Targets:
# 受信者のeventbusを指定
- Arn: !Sub arn:aws:events:${AWS::Region}:${PipelineAccount}:event-bus/CodeCommit-EventBus-${ProjectEnv}
Id: CodeCommitTriggerEventRule
RoleArn: !GetAtt EventTriggerRole.Arn
EventTriggerRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub CodeCommitEventTriggerRole-${ProjectEnv}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: EventInvokePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- events:PutEvents
# 受信者のeventbusを指定
Resource: !Sub arn:aws:events:${AWS::Region}:${PipelineAccount}:event-bus/CodeCommit-EventBus-${ProjectEnv}
CodeCommitAccessRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub CodeCommitAccessRole_${ProjectEnv}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${PipelineAccount}:role/CodePipelineServiceRole-${ProjectEnv}
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AWSCodeCommitFullAccess
- arn:aws:iam::aws:policy/AmazonS3FullAccess
CodeCommitの変更をトリガーできるEvent Ruleを作成しています。併せてEvent RuleはCodePipelineアカウント側のBusにイベントを送信するためにIAM Roleも必要です。
また、今回最終的にCodePipelineはCodeCommitからソースを取得してS3に保存する処理をするため、そのための権限を付与してIAM Roleも作成します。
ちなみにCodeCommitはレポ名でどのレポからのイベントをトリガーするか指定できます。今回はプレフィックスが
test-codecommit-repo
のレポをトリガーすることにします。
3. CodePipelineアカウントでEventBridge関係とCodePipelineのリソースを作成
最後に再度CodePipeline側のアカウントでEventBridge関係のリソースとCodePipelineを作成します。
AWSTemplateFormatVersion: "2010-09-09"
Description: Create EventBridge EventBus for CodeCommit trigger CodePipeline
Parameters:
ProjectEnv:
Description: Envirment of project
Type: String
AllowedValues:
- prd
- sta
- dev
CodePipelineServiceRoleName:
Type: String
Description: CodePipeline Service Role
Default: CodePipelineServiceRole-dev
TriggerAccount:
Type: String
Description: Account to trigger the pipeline
Default: <CodeCommit AWSアカウントID>
RepoName:
Type: String
Description: Repository name to trigger the pipeline
Default: test-codecommit-repo
BranchName:
Type: String
Description: Branch name to trigger the pipeline
Default: main
BucketName:
Type: String
Description: Bucket name to deploy the code
Default: saito-test-2024
Resources:
CodeCommitEventBus:
Type: AWS::Events::EventBus
Properties:
Name: !Sub CodeCommit-EventBus-${ProjectEnv}
EventBusPolicy:
Type: AWS::Events::EventBusPolicy
Properties:
EventBusName: !Ref CodeCommitEventBus
StatementId: AllowEventForTriggerAccount
Statement:
Sid: AllowEventForTriggerAccount
Effect: Allow
Principal:
AWS: !Sub arn:aws:iam::${TriggerAccount}:root
Action: events:PutEvents
Resource: "*"
PipelineEventInvokeRule:
Type: AWS::Events::Rule
Properties:
Description: CodeCommit Event Rule
EventBusName: !Ref CodeCommitEventBus
EventPattern:
source:
- aws.codecommit
detail-type:
- CodeCommit Repository State Change
resources:
- prefix: !Sub arn:aws:codecommit:${AWS::Region}:${TriggerAccount}:${RepoName}
detail:
referenceType:
- branch
event:
- referenceCreated
- referenceUpdated
referenceName:
- !Ref BranchName
State: ENABLED
Targets:
- Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:pipeline-${ProjectEnv}
RoleArn: !GetAtt EventBridgeCodePipelineInvokeRole.Arn
Id: CodePipelineInvoke
EventBridgeCodePipelineInvokeRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub EventBridgeCodePipelineInvokeRole-${ProjectEnv}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
Path: /
Policies:
- PolicyName: EventBridgeCodePipelineInvokeRolePolicy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- codepipeline:StartPipelineExecution
Resource: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:pipeline-${ProjectEnv}
BackupCodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub pipeline-${ProjectEnv}
RestartExecutionOnUpdate: false
RoleArn: !Sub arn:aws:iam::${AWS::AccountId}:role/${CodePipelineServiceRoleName}
ArtifactStore:
Location: !Ref BucketName
Type: S3
Stages:
- Name: Source
Actions:
- Name: BackupCodecommit
ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
RoleArn: !Sub arn:aws:iam::${TriggerAccount}:role/CodeCommitAccessRole_${ProjectEnv}
Configuration:
RepositoryName: !Ref RepoName
BranchName: !Ref BranchName
PollForSourceChanges: false
RunOrder: 1
OutputArtifacts:
- Name: codecommit-source
- Name: Deploy
Actions:
- Name: DeployS3
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: S3
Version: 1
InputArtifacts:
- Name: codecommit-source
RoleArn: !Sub arn:aws:iam::${TriggerAccount}:role/CodeCommitAccessRole_${ProjectEnv}
Configuration:
BucketName: !Ref BucketName
Extract: false
ObjectKey:
!Sub backup/${BranchName}.zip
RunOrder: 1
リソース多めなので補足しておきます。
-
CodeCommitEventBus、EventBusPolicy・・・CodeCommit側アカウントで作成したEvent Ruleを受信するためのBus。併せてBusにリソースベースポリシーも作成してアタッチします。
-
PipelineEventInvokeRule、EventBridgeCodePipelineInvokeRole・・・上記Event BusがEventを受信した際に実行されるEvent Rule。このRuleはCodePipelineを実行するので、併せてCodePipelineの実行権限をIAM ROLEで付与します。
-
BackupCodePipeline・・・CodeCommitトリガーで実行されるCodePipeline本体。CodeCommitとS3は他アカウントなので、RoleArnで手順1で作成したサービスロールを指定。さらに、StageでRoleArnも指定します。
余談ですが、AWSコンソールからStageのRoleArnの指定できない気がします。入力欄がわからなかったので、詳しい方教えてください。
4. CodeCommitをトリガーしてCodePipeline動作確認
ここまでできたら動作を確認してみます。
CodeCommit側のアカウントにログインしてAWSコンソールでトリガーするCodeCommitを開きます。
対象のレポを選択して「ファイルの追加」から適当なファイルを作成してコミットしてみます。
コミットしたらCodePipelineアカウント側に切り替えて、CodePipelineの動作を確認します。
対象のコミットでCodePipelineが実行されフローが成功しました。
無事トリガーされていそうですね。
今回は以上になります。
CodePipelineのCodeCommitソースへのポーリングアクセスが非推奨になったものの、EventBrige + クロスアカウントの場合は設定が増えて少し大変でした。
ですが改めてクロスアカウントのロール引き受けについて理解が深めることができました。
NCDC株式会社( ncdc.co.jp/ )のエンジニアチームです。 募集中のエンジニアのポジションや、採用している技術スタックの紹介などはこちら( github.com/ncdcdev/recruitment )をご覧ください! ※エンジニア以外も記事を投稿することがあります
Discussion