1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

CodePipelineによるクロスアカウントデプロイをCloudFormationで実装してみる

Last updated at Posted at 2021-10-12

背景

業務で、CodePipelineを使い別AWSアカウントへのS3デプロイ構成を構築することがありました。
公式の記事を参考にしようとしたのですが、こちらだと最後の手順でAWS CLIを使用する必要あり。しかし諸事情で現在CLIを使えず…。
いっそのこと、学習も兼ねてCloudFormationで作ってみました。

概要

image.png
アカウントAでCodePipelineを構築し、アカウントBのS3バケットにデプロイします。

ポイント
・KMSカスタマー管理型のキーを作成し、用いることアーティファクト格納用バケットの暗号化に用いること
 (理由)CodePipelineデフォルトの暗号化であるAWS管理型のキーは、別アカウントによる使用権限を持たせることができないため。

・アカウントBでデプロイ先S3にアクセス許可するprods3roleを作成し、アカウントAのCodePipelineでそのロールを使用する。(厳密には、CodePipelineサービスロールがprods3roleを引き受ける)
prods3roleの信頼関係で、CodePipelineサービスロールを指定して引き受け許可しておく必要あり。

前提
以下2点はコードで書かず、すでに作成済みという前提です。
アカウントA:CodeCommitリポジトリ(今回はリポジトリ名"my-Repository"で作成)
アカウントB:デプロイ先S3バケット(今回はバケット名"crossaccount-deploy-test"で作成)

今回一部しか載せていないコードもありますが、全体は[GitHub] (https://github.com/Makoto-Taguchi/CFn-CodePipeline-CrossAccountDeploy)で公開しているのでよければご覧ください。

コード

1. 【アカウントA】CodePipelineのサービスロールを作成

3つに分けてコードを展開します。なぜかというと、後述2.でprods3roleのprincipalに、既に存在するリソースを指定する必要がありCodePipelineサービスロールはあらかじめ作成する必要があるためです。

CodePipelineServiceRole.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Create CodePipeline ServiceRole
Parameters:
  NamePrefix:
    Type: String
    Default: myproject
  DeployAccountID:
    Type: String
  CrossAccountRoleName:
    Type: String
    Default: prods3role

Resources:
  # CodePipelineに適用するIAMRole
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${NamePrefix}-CodePipelineServiceRole
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
  AssumeRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: !Sub ${NamePrefix}-AssumeProdRolePolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Resource:
              - !Sub arn:aws:iam::${DeployAccountID}:role/${CrossAccountRoleName}
      Roles:
        - !Ref CodePipelineServiceRole
  CodePipelineBasePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: !Sub ${NamePrefix}-CodePipelineBasePolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - codecommit:GetRepository
              - codecommit:ListBranches
              - codecommit:GetUploadArchiveStatus
              - codecommit:UploadArchive
              - codecommit:CancelUploadArchive
              - codebuild:StartBuild
              - codebuild:StopBuild
              - codebuild:BatchGet*s
              - codebuild:Get*
              - codebuild:List*
              - codecommit:GetBranch
              - codecommit:GetCommit
              - s3:*
              - iam:PassRole
            Resource: "*"
      Roles:
        - !Ref CodePipelineServiceRole

Outputs:
  CodePipelineServiceRoleArn:
    Value: !GetAtt CodePipelineServiceRole.Arn

後述の2.で作成するprods3roleをパラメータCrossAccountRoleNameとして入力。
ポイントは以下の記述です。

  AssumeRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: !Sub ${NamePrefix}-AssumeProdRolePolicy
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Resource:
              - !Sub arn:aws:iam::${DeployAccountID}:role/${CrossAccountRoleName}
      Roles:
        - !Ref CodePipelineServiceRole

ResourceにアカウントBのprods3roleを指定します。
このポリシーをCodePipelineServiceRoleにアタッチすることで、CodePipelienデプロイステージでロールがsts:AssumeRoleによりprods3roleを引き受けるようにします。

2. 【アカウントB】クロスアカウントデプロイさせるprods3roleを作成

CodePipelineサービスロールが引き受ける、prods3roleを作ります。このロールが持つべき権限は以下。
・アカウントBのデプロイ先バケットへの書き込み権限
・アカウントAのKMSの使用権限(アーティファクトバケット復号用)
・アカウントBのアーティファクトバケットの読み取り権限
そして、これをCodePipelineが使用できるよう、信頼関係を記述します。

CrossAccountAccessRole.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Create CrossAccountRole which Deploy Stage Use
Parameters:
  DeployBucketName:
    Type: String
    Default: crossaccount-deploy-test
  PipelineAccountID:
    Type: String
  CodePipelineServiceRoleName:
    Type: String
    Default: myproject-CodePipelineServiceRole

Resources:
  CrossAccountRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: prods3role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${PipelineAccountID}:role/${CodePipelineServiceRoleName}
            Action: sts:AssumeRole
  DeployBucketAccessPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: outputbucketfullaccess
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          # Enable DeployBucket Access in DeployAccount
          - Effect: Allow
            Action:
              - s3:*
            Resource:
              - !Sub arn:aws:s3:::${DeployBucketName}/*
          - Effect: Allow
            Action:
              - s3:ListBucket
            Resource:
              - !Sub arn:aws:s3:::${DeployBucketName}
      Roles:
        - !Ref CrossAccountRole
  KMSAccessPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: !Sub devkmss3access
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          # Enable EncryptionKey Access for ArtifactBucket in PipelineAccount
          - Effect: Allow
            Action:
              - kms:Encrypt
              - kms:Decrypt
              - kms:ReEncrypt*
              - kms:GenerateDataKey*
              - kms:DescribeKey
            Resource:
              - !Sub arn:aws:kms:ap-northeast-1:${PipelineAccountID}:key/*
          # Enable ArtifactBucket Access in PipelineAccount
          - Effect: Allow
            Action:
              - s3:Get*
              - s3:ListBucket
            Resource:
              - arn:aws:s3:::*
      Roles:
        - !Ref CrossAccountRole

Resourceで、KMSキー名とアーティファクトバケット名を指定すべきですが、後述3.でそれらは作成するので、ここでは*としてます。全て構築後に指定し直すと良いです。

(抜粋)
Principal:
  AWS: !Sub arn:aws:iam::${PipelineAccountID}:role/${CodePipelineServiceRoleName}

CrossAccountRoleで、Principalに1.で作成したCodePipelineサービスロール名を指定します。これによってサービスロールがprods3roleを使用することを許可しています。

余談ですが、信頼関係の記述でPrincipalに特定アカウント中の全てのロールという意味でワイルドカード*で指定するのは不可みたいです。試しに作ろうとするとエラーになりました。

Principal:
  AWS: !Sub arn:aws:iam::${PipelineAccountID}:role/*

また、以下のように匿名ユーザを指定すると、どのアカウントからでもロール使用できるようになってしまうのでめちゃくちゃ危険です。絶対しないようにしましょう。

Principal: "*"

参考

3. 【アカウントA】CodePipelineを構築

全部書くと長いのでCodePipelineの記述だけ抜粋します。
詳細は GitHub をご覧ください。

CodePipeline.yml(抜粋)
  # CodePipeline
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub ${NamePrefix}-Pipeline
      RoleArn: !Ref CodePipelineServiceRoleArn
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactBucket
        EncryptionKey:
          Id: !GetAtt KMSKey.Arn
          Type: KMS
      Stages:
        - Name: Source
          Actions:
            - Name: Source
              ActionTypeId:
                Category: Source
                Owner: AWS
                Version: 1
                Provider: CodeCommit
              Configuration:
                RepositoryName: !Ref CodeCommitRepositoryName
                BranchName: !Ref CodeCommitBranchName
              RunOrder: 1
              OutputArtifacts:
                - Name: SourceArtifact
        - Name: Build
          Actions:
            - Name: Build
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProject
              RunOrder: 1
              InputArtifacts:
                - Name: SourceArtifact
              OutputArtifacts:
                - Name: BuildArtifact
        - Name: Deploy
          Actions:
            - Name: Deploy
              ActionTypeId:
                Category: Deploy
                Owner: AWS
                Version: 1
                Provider: S3
              Configuration:
                BucketName: !Ref DeployBucketName
                Extract: true
              RunOrder: 1
              InputArtifacts:
                - Name: BuildArtifact
              RoleArn: !Sub  arn:aws:iam::${DeployAccountID}:role/${CrossAccountRoleName}

一番のポイントは最後の行で、CodePipelineデプロイステージで使用するロールにprod3sroleを使用することを記述しています。

(ちなみにBuildSpecは今回の主題と関係ないためかなりテキトーに書いてます。要はtest.txtがアーティファクトとして出力されます。)

実行確認

全部CFn構築できたら、AWSコンソール上で実行確認してみます。
まずアカウントAで、CodeCommitで適当にコミットします。
image.png

CodePipelineの画面を見ると、パイプライン起動し無事デプロイまで成功しました。
image.png

アカウントBにサインインし直し、デプロイ先S3バケットを見てみます。
test.txtがデプロイされていることが確認できました!
image.png

まとめ

AWS CLIを使えないためCloudFormationで構築してみました。CLIの代わりにAWS CloudShellで同じことができ、CFnなくても実現可能なことに構築後に気づきましたが。。
まあ、CFnで全体見ながら構築することでIAMの構造やKMSの使い方も理解できたので、良い機会になりました!

1
2
0

Register as a new user and use Qiita more conveniently

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?