【AWS】CloudFormation で Github/CodePipeline/CodeBuild を構築する

■ はじめに

https://dk521123.hatenablog.com/entry/2021/10/26/224812
https://dk521123.hatenablog.com/entry/2021/12/01/170326
https://dk521123.hatenablog.com/entry/2021/12/04/202519
https://dk521123.hatenablog.com/entry/2021/12/05/134313

の続き。

AWS CloudFormation (CFn) で学んだことを実践してみる。

目次

【0】今回で学べた教訓
【1】今回の仕様 - やりたいこと
【2】設計方針
 1)Security Layer
 2)Application Layer
【3】API仕様
 1)Security Layer
 2)Application Layer
【4】サンプル
 1)Security Layer
 2)Application Layer

【0】今回で学べた教訓

* 書き始めるのは、
 Application Layer (CodePipeline/CodeBuild)で
 後から、Security Layer(IAM Role/Policy)を
 書いた方がいい。
* Input(入力値。例えば、Githubの情報) を考えて、
 Parameters に書き込んでいく

【1】今回の仕様 - やりたいこと

1)以下「全体構成」のような作りをする
2)環境 EnviromemtType を定義する
 + dev (開発環境)
 + stage (検証環境)
 + prod (本番環境)
3)Githubブランチ
 「${GithubBranch(e.g. release)}/${EnviromemtType}」が
  Pushした場合、CodePiplineで関知して発火する
4)環境「stage/prod」の場合は、
  承認アクション(approval actions) が必要にする
 => dev はスルー

全体構成

Github
↓ <webhook>
CodePipline
↓ <stage/prod の場合は、承認アクションが必要>
CodeBuild

【2】設計方針

https://dk521123.hatenablog.com/entry/2021/12/01/170326

の「テンプレート管理 - スタック分割」で扱った
スタック分割で作ってみる。
 => 今回は、ネットワーク(VPCなど)は考えないものとする

1)Security Layer

* 以下のAWSサービスのIAM role および  policy を設定する
 [1] CodePipeline
 [2] CodeBuild 
 => Outputs で 2つのAWSサービスの RoleArn を出力し、
  Application Layerに渡すようにする

2)Application Layer

* 以下を作成する
 [1] CodePipeline
 [2] CodeBuild の プロジェクト
 =>  RoleArn は、「1)Security Layer」で生成された値を
  Fn::ImportValueを使って取得し設定する

【3】API仕様

1)Security Layer

Type: AWS::IAM::Role
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html

Type: AWS::IAM::Policy
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-iam-policy.html

2)Application Layer

Type: AWS::CodePipeline::Pipeline
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html

Type: AWS::CodeBuild::Project
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html

【4】サンプル

1)Security Layer

sample-security-layer-template.yaml

AWSTemplateFormatVersion: "2010-09-09"
Description: "Security layer for Github/CodePipeline/CodeBuild"
Parameters:
  EnvironmentType:
    Description: Enter environment type
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - stage
      - prod
  ArtifactStoreBucket:
    Description: Enter s3 bucket name for ArtifactStore location
    Type: String
    Default: your-s3-bucket-name
Conditions:
  IsStageOrProd:
    !Or [!Equals [stage, !Ref EnvironmentType], !Equals [prod, !Ref EnvironmentType]]
Resources:
  # For CodeBuild
  CodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Description: 'IAM Role for CodeBuild'
      RoleName: CodeBuildServiceIamRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: sts:AssumeRole
            Principal:
              Service: codebuild.amazonaws.com
      Policies:
        - PolicyName: CodeBuildServiceIamPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Sid: codebuild-logs
                Resource:
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*
                  - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*
                Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
              - Sid: codebuild-s3
                Resource:
                  - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*
                Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:GetBucketLocation
                  - s3:GetObjectVersion
                  - s3:GetBucketVersioning
                  - s3:GetBucketAcl
              - Sid: codebuild-codebuild
                Resource: "*"
                Effect: Allow
                Action:
                  - codebuild:BatchGet*
                  - codebuild:Get*
                  - codebuild:List*
                  - codebuild:CreateReportGroup
                  - codebuild:CreateReport
                  - codebuild:UpdateReport
                  - codebuild:BatchPutTestCases
                  - codebuild:BatchPutCodeCoverages
                  - secretsmanager:DescribeSecret
                  - secretsmanager:ListSecretVersionIds
                  - secretsmanager:GetResourcePolicy
                  - secretsmanager:GetSecretValue
              - Sid: codebuild-ssm
                Resource: "arn:aws:ssm:*:*:parameter/CodeBuild/*"
                Effect: Allow
                Action:
                  - ssm:PutParameter
  # CodePipeline
  CodePipelineServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: codepipeline.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodePipelineServiceRole
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Resource:
                - !Sub arn:aws:s3:::${ArtifactStoreBucket}/*
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetObjectVersion
                  - s3:GetBucketVersioning
              - Effect: Allow
                Resource: "*"
                Action:
                  - codebuild:StartBuild
                  - codebuild:StopBuild
                  - codebuild:BatchGet*
                  - codebuild:Get*
                  - codebuild:List*
                  - s3:GetBucketLocation
                  - s3:ListAllMyBuckets
                  - iam:PassRole
        - !If
          - IsStageOrProd
          -
            # See https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/approvals-iam-permissions.html
            PolicyName: Approval-CodePipelineServiceRole
            PolicyDocument:
              Version: 2012-10-17
              Statement:
                - Effect: Allow
                  Action:
                    - "codepipeline:ListPipelines"
                  Resource: "*"
                - Effect: Allow
                  Action:
                    - "codepipeline:GetPipeline"
                    - "codepipeline:GetPipelineState"
                    - "codepipeline:GetPipelineExecution"
                  # TODO: Need to modify
                  Resource: !Sub arn:aws:codepipeline:${AWS::Region}:80398EXAMPLE:MyFirstPipeline
                - Effect: Allow
                    - "codepipeline:PutApprovalResult"
                  # TODO: Need to modify
                  Resource: !Sub arn:aws:codepipeline:${AWS::Region}:80398EXAMPLE:MyFirstPipeline
          - !Ref AWS::NoValue
Outputs:
  CodePipelineRoleArn:
    Description: ARN for Role of Code Pipeline.
    Value: !GetAtt CodePipelineServiceRole.Arn
    Export:
      Name: "CodePipelineRoleArn"
  CodeBuildRoleArn:
    Description: ARN for Role of Code Build.
    Value: !GetAtt CodeBuildServiceRole.Arn
    Export:
      Name: "CodeBuildRoleArn"

2)Application Layer

sample-application-layer-template.yaml

AWSTemplateFormatVersion: "2010-09-09"
Description: "Application Layer For Github/CodePipeline/CodeBuild"

Parameters:
  EnvironmentType:
    Description: Enter environment type
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - stage
      - prod
  GithubOwner:
    Description: Enter your Github Owner
    Type: String
    Default: owner-name
  GithubRepository:
    Description: Enter Github Repository
    Type: String
    Default: your-github-repository-name
  GithubBranch:
    Description: Enter Github branch
    Type: String
    Default: release
  GithubToken:
      Description: Enter Github Token
      Type: String
  ArtifactStoreBucket:
    Description: Enter s3 bucket name for ArtifactStore location
    Type: String
    Default: your-s3-bucket-name
  CodeBuildProjectName:
    Description: Enter target CodeBuild Project name
    Type: String
    Default: CodeBuild-Project-Demo
Conditions:
  IsStageOrProd:
    !Or [!Equals [stage, !Ref EnvironmentType], !Equals [prod, !Ref EnvironmentType]]
Resources:
  # CodePipeline
  # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codepipeline-pipeline.html
  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: CodePipelineDemo
      ArtifactStore:
        Type: S3
        Location: !Ref ArtifactStoreBucket
      RoleArn: !ImportValue CodePipelineRoleArn
      RestartExecutionOnUpdate: false
      Stages:
        -
          Name: Source
          Actions:
            - 
              Name: SourceAction
              RunOrder: 1
              ActionTypeId:
                Category: Source
                Owner: ThirdParty
                Provider: GitHub
                Version: 1
              OutputArtifacts:
                - 
                  Name: SourceOutput
              Configuration:
                Owner: !Ref GithubOwner
                Repo: !Ref GithubRepository
                Branch: !Ref GithubBranch
                OAuthToken: !Ref GithubToken
        - !If
          - IsStageOrProd
          -
            Name: Approval
            Actions:
              -
                Name: Approval
                RunOrder: 2
                ActionTypeId:
                  Category: Approval
                  Owner: AWS
                  Provider: Manual
                  Version: 1
          - !Ref AWS::NoValue
        -
          Name: Build
          Actions:
            -
              Name: CodeBuild
              RunOrder: 3
              InputArtifacts:
                -
                  Name: SourceOutput
              ActionTypeId:
                Category: Build
                Owner: AWS
                Version: 1
                Provider: CodeBuild
              Configuration:
                ProjectName: !Ref CodeBuildProjectName
              OutputArtifacts:
                -
                  Name: CodebuildOutput
      Tags:
        - Key: Name
          Value: CodePipelineDemo
        - Key: Env
          Value: !Ref EnvironmentType
  # CodeBuild
  # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html
  CodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Ref CodeBuildProjectName
      Description: !Sub Building stage for ${GithubBranch} in ${EnvironmentType}.
      Artifacts:
        Type: CODEPIPELINE
      Source:
        Type: CODEPIPELINE
      Environment:
        Type: LINUX_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/standard:1.0
        # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-project-environmentvariable.html
        EnvironmentVariables:
          - Name: varName1
            Value: varValue1
            Type: PLAINTEXT
          - Name: varName2
            Value: /CodeBuild/testSecretParameter
            Type: SECRETS_MANAGER
          - Name: varName3
            Value: /CodeBuild/testParameter
            Type: PARAMETER_STORE
      ServiceRole: !ImportValue CodeBuildRoleArn
      ResourceAccessRole: String
      TimeoutInMinutes: 5
      Tags:
        - Key: Name
          Value: !Ref CodeBuildProjectName
        - Key: Env
          Value: !Ref EnvironmentType
      LogsConfig:
        CloudWatchLogs:
          Status: 'ENABLED'
Outputs:
  CodeBuildProjectArn:
    Description: ARN for Code Build
    Value: !GetAtt CodeBuildProject.Arn
    Export:
      Name: "CodeBuildProjectArn"

参考文献

https://dev.classmethod.jp/articles/create_codepipeline_in_cloudformation/
https://dev.classmethod.jp/articles/developing-cloudformation-ci-cd-pipeline-with-github-codebuild-codepipeline/
https://qiita.com/neruneruo/items/70f06323de44b901918d

関連記事

AWS CloudFormation ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2021/10/26/224812
AWS CloudFormation ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2021/12/01/170326
AWS CloudFormation ~ 組み込み関数 ~
https://dk521123.hatenablog.com/entry/2021/12/04/202519
AWS CloudFormation ~ 疑似パラメータ ~
https://dk521123.hatenablog.com/entry/2021/12/05/134313
AWS CloudFormation ~ DeletionPolicy 属性 ~
https://dk521123.hatenablog.com/entry/2021/12/27/211328
AWS CloudFormation ~ 認証情報の扱い ~
https://dk521123.hatenablog.com/entry/2021/12/28/224501
CodePipeline ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/01/23/231827
CodeBuild ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2020/01/21/221122
各開発フェーズにおける環境の呼び方
https://dk521123.hatenablog.com/entry/2020/04/16/113816