めちゃめちゃ期間をあけてしまいましたが前回の続きです。
前回のあらすじ
リポジトリアカウント : CodeCommitのRepositoryがある方のアカウント
パイプラインアカウント : CodePipelineがある方のアカウント
リポジトリアカウントにあるCodeCommitの変更をEventBus経由でパイプラインアカウントのEventRuleで検出できるようになりました。
この記事のゴール
リポジトリアカウントのCodeCommitに対して行なった変更がパイプラインアカウントでデプロイされるようになります。
なぜこの記事を書いたか
アカウントを跨いだコードパイプラインの構築についてはWeb上にいくつか記事があります。その通りに構築すれば出来上がったのですが、このIAM Roleはだれのためのものだったっけ?など自分の頭の整理が必要になったので書いてみました。
前提知識
CodePipeline, CodeCommit, KMSなどの各サービスがどのようなものか知っている
IAM Roleを引き受けるという概念を知っている
クロスアカウントアクセスというものを聞いたことがある
CodePipeline V1/V2について
CodePipelineには現在V1とV2という2種類のタイプがあります(知りませんでした)。
適切なパイプラインのタイプの選択 を確認すると今回は特にV2が必要な要件がないのでV1を選択します。
出来上がる構成
今回は説明をシンプルにするため、デプロイはCodePipelineからS3にファイルを配置するのみの構成とします。
大まかな説明
CodePipelineはソース(CodeCommieやS3、Githubなど)から取得したファイルをアーティファクトストアと呼ばれるS3 Bucketに一旦コピーします。
クロスアカウントでCodeCommit/CodePipelineを構成する場合、このアカウントを跨いだアーティファクトストアにファイルを置くための権限をどのように作るかというところがキモになります。
CodePipelineはリポジトリアカウントに用意されたRoleを引き受けてCodeCommitからソースを取得します。
同時にそのRoleを使ってリポジトリアカウントからパイプラインアカウントのアーティファクトストアにファイルをおきます。
この時、KMSカスタママネージドキーによる暗号化が必須です。
パイプラインアカウントにアーティファクトストアを作成する。
リポジトリアカウントにパイプラインアカウントに引き受けさせるIAM Roleを用意する。このRoleはカスタママネージドキーを使ってパイプラインアカウントのアーティファクトストアへの書き込みができるようにする。
パイプラインアカウントにKMSでカスタママネージドキーを作成する
CodePipelineのIAM RoleにリポジトリアカウントにあるIAM Roleを引き受けられるように sts:AssumeRole を許可する。
アーティファクトストアのBuckePolicyでリポジトリアカウントからのアクセスを許可するように設定する。
余談:IAM Roleのアイコンがなぜヘルメットなのかというと、RoleをAssumeする(引き受ける)ことをヘルメットを被ってその役職で振る舞うことに準えているのだということをどこかで聞きました。真偽も記憶も定かではないですが。
作成していく
アーティファクトストア作成
パイプラインアカウントで行う。
アーティファクトストアは普通のS3Bucketです
ポイント
バージョニングを有効にする
リポジトリアカウントからの書き込みを許可
書き込み時にはKMSを使った暗号化とSecureTransportを必須にする
# pipeline-artifact-store.yml
PipelineArtifactBucket:
# DeletionPolicy: Retain
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "${Environment}-${ResourceNamePrefix}-pipeline-artifact-bucket-${AWS::AccountId}"
VersioningConfiguration:
Status: Enabled
PipelineArtifactBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PipelineArtifactBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- s3:PutObject
Effect: Deny
Principal: "*"
Resource:
- !Sub arn:aws:s3:::${PipelineArtifactBucket}/*
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: aws:kms
- Action:
- s3:*
Effect: Deny
Principal: "*"
Resource:
- !Sub arn:aws:s3:::${PipelineArtifactBucket}/*
Condition:
Bool:
aws:SecureTransport: false
- Action:
- s3:Get*
- s3:Put*
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${RepositoryAccountId}:root
Resource:
- !Sub arn:aws:s3:::${PipelineArtifactBucket}/*
- Action:
- s3:ListBucket
Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${RepositoryAccountId}:root
Resource:
- !Sub arn:aws:s3:::${PipelineArtifactBucket}
CodePipelineに付与するIAM Role作成
パイプラインアカウントで行う。
CodePipelineの実行時のRoleです。
ポイント
sts:AssumeRole のところでリポジトリアカウントからのRoleを引き受けられるようにする
# pipeline-role.yml
# 別アカウントRepositoryをソースにするCodepipelineのRole
# リポジトリ側のRoleを引き受けられるようにAssumeRoleを許可している
PipelineRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${Environment}-${ResourceNamePrefix}-CodePielineRole"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- codepipeline.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: Pipeline
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- sts:AssumeRole
Resource:
- !Sub arn:aws:iam::${RepositoryAccountId}:role/*
- Effect: Allow
Action:
- iam:PassRole
Resource:
- "*"
- Effect: Allow
Action:
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:UploadArchive
- codecommit:GetUploadArchiveStatus
- codecommit:CancelUploadArchive
- cloudformation:CreateStack
- cloudformation:DeleteStack
- cloudformation:DescribeStacks
- cloudformation:UpdateStack
- cloudformation:CreateChangeSet
- cloudformation:DeleteChangeSet
- cloudformation:DescribeChangeSet
- cloudformation:ExecuteChangeSet
- cloudformation:SetStackPolicy
- cloudformation:ValidateTemplate
- iam:PassRole
- codebuild:BatchGetBuilds
- codebuild:StartBuild
Resource: '*'
- Effect: Allow
Action: s3:*
Resource:
- !Sub "${PipelineArtifactBucket.Arn}"
- !Sub "${PipelineArtifactBucket.Arn}/*"
- Effect: Allow
Action: s3:Put*
Resource:
- !Sub "${DeployTargetBucketArn}"
- !Sub "${DeployTargetBucketArn}/*"
- Effect: Allow
Action:
- kms:DescribeKey
- kms:GenerateDataKey*
- kms:Encrypt
- kms:ReEncrypt*
- kms:Decrypt
Resource: !Ref KmsKeyArn
KMS カスタママネージドキー
パイプラインアカウントで行う。
アーティファクトストアに書き込むときに使うカスタママネージドキーです。
ポイント
リポジトリアカウントからの使用を許可する
# kms-key.yml
Key:
Type: AWS::KMS::Key
Properties:
KeyPolicy:
Version: 2012-10-17
Id: key-default-1
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${AWS::AccountId}:root
Action: kms:*
Resource: "*"
- Effect: Allow
Principal:
AWS:
- !Sub "arn:aws:iam::${AWS::AccountId}:group/admin-group-${AWS::AccountId}"
Action:
- kms:Create*
- kms:Describe*
- kms:Enable*
- kms:List*
- kms:Put*
- kms:Update*
- kms:Revoke*
- kms:Disable*
- kms:Get*
- kms:Delete*
- kms:ScheduleKeyDeletion
- kms:CancelKeyDeletion
Resource: "*"
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${RepositoryAccountId}:root
- !GetAtt PipelineRole.Arn
Action:
- kms:DescribeKey
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey
- kms:GenerateDataKeyWithoutPlaintext
Resource: "*"
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${RepositoryAccountId}:root
- !GetAtt PipelineRole.Arn
Action:
- kms:CreateGrant
- kms:ListGrants
- kms:RevokeGrant
Resource: "*"
Condition:
Bool:
kms:GrantIsForAWSResource: true
Alias:
Type: AWS::KMS::Alias
Properties:
AliasName: alias/CodePipelineArtifact
TargetKeyId:
Ref: Key
CodePipelineに渡すリポジトリアカウント側Role
リポジトリアカウントで行う。
CodePipelineが実行されるときにリポジトリアカウントから引き受けるIAM Roleです。
ポイント
Principal でリポジトリアカウントを設定する
パイプラインアカウントにあるアーティファクトストアとカスタママネージドキーの利用を許可する
ExternalPipelineExecutionRole:
Properties:
RoleName: !Sub "${AWS::StackName}-ExternalPipelineExecutionRole"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub arn:aws:iam::${ExternalAccountId}:root
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: source
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:PutObject
- s3:PutObjectAcl
Resource:
- !Sub ${PipelineArtifactBucketArn}/*
- Effect: Allow
Action:
- kms:DescribeKey
- kms:GenerateDataKey*
- kms:Encrypt
- kms:ReEncrypt*
- kms:Decrypt
Resource:
- !Ref ExternalKeyArn
- Effect: Allow
Action:
- codecommit:GetBranch
- codecommit:GetCommit
- codecommit:UploadArchive
- codecommit:GetUploadArchiveStatus
- codecommit:CancelUploadArchive
Resource:
- !GetAtt Repo.Arn
CodePipeline
パイプラインアカウントで行う。
CodePipelineの構成自体は今回の本題ではないのでシンプルにS3に配置するだけのものです。
ポイント
ArtifactStore の指定でカスタママネージドキーを設定
このカスタママネージドキーがリポジトリアカウント側とパイプラインアカウント側の共通の暗号鍵になる
Actionの実行時のRoleをリポジトリアカウントから引き受けるIAM Roleにする
2つ目のActionでDeployTargetBucketに release/ というPrefixをつけてデプロイする
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub "${AWS::StackName}-${RepositoryName}-pipeline"
ArtifactStore:
Location: !Ref PipelineArtifactBucketName
Type: S3
EncryptionKey:
Id: !Ref KmsKeyArn
Type: KMS
RoleArn: !Ref PipelineRoleArn
RestartExecutionOnUpdate: False
Stages:
- Actions:
- ActionTypeId:
Category: Source
Owner: AWS
Provider: CodeCommit
Version: 1
Configuration:
RepositoryName: !Ref RepositoryName
BranchName: !Ref TargetBranchName
PollForSourceChanges: false
OutputArtifactFormat: CODE_ZIP
Name: Source
Namespace: SourceVariables
OutputArtifacts:
- Name: SourceArtifact
Region: !Ref AWS::Region
RunOrder: 1
# CodeCommit側アカウントに作成したRole
RoleArn: !Ref RepositoryAccountRoleArn
Name: Source
- Actions:
- ActionTypeId:
Category: Deploy
Owner: AWS
Provider: S3
Version: 1
Configuration:
BucketName: !Ref DeployTargetBucketName
Extract: true
ObjectKey: release
InputArtifacts:
- Name: SourceArtifact
Name: Deploy
Namespace: DeployVariables
RunOrder: 1
Name: Deploy
いよいよCodePipeline本体を作ります。
CodePipeline自身には PipelineRole が設定されています。
Source ActionではリポジトリアカウントにあるCodeCommitリポジトリを参照していますが、この時のIAM Roleはリポジトリアカウントに作成しておいた ExternalPipelineExecutionRoleが使われます。
EventBridgeとつなぐ
前回の記事でリポジトリアカウントからパイプラインアカウントにリポジトリ更新が通知されるようにEventBridgeでRuleを作成しました。
今回はRuleのターゲットにCodePIpelineを設定します。
動作確認
ここまでくるとCodePipelineがクロスアカウント動作するようになるので動かしてみましょう。
リポジトリにPushされたCommit
CodePipelineの起動
commit idとメッセージが期待した通り、リポジトリにPushされたCommitのもので起動されています。
まとめ
これでAWSアカウントを跨いだCodeCommit/CodePipelineの連携ができるようになりました。さまざまな事情でGitHub/GitLabや外部CIサービスを使用できない場合でもAWS純正サービスでデプロイメントパイプラインを構築することが可能です。
補足
今回はリポジトリ1に対してパイプライン1でしたが、商用を考えると開発AWSアカウント/本番AWSアカウントが分かれていて、それぞれにパイプラインを配置する構成が考えられます。
その場合、CodeCommitは開発AWSアカウントではなく本番アカウントとも独立したAWSアカウントにした方が良いと思います。なぜなら、開発/本番AWSアカウントでCodePipeline周りの設定が異なっているとトラブルシューティングやメンテナンスが面倒になりそうだからです。
Комментарии