GitHub連携を含んだCodeBuildのCloudFormationテンプレート生成を試してみた

GitHub連携を含んだCodeBuild単体でのCICD環境を、非属人化するためにCloudFormation化してみました。実際のCICD処理については仮の内容になっています。
2019.11.11

はじめに

CICD環境作成の非属人化を目指して、AWS CodeBuild / AWS CodeDeploy / AWS CodePipelineの組み合わせで構築し、更にCloudFormationテンプレート化を検討していました。が、調べるうちに担当プロジェクトではAWS CodeBuildで事足りることに気が付きました。

実際のCodeBuild上でのCICD処理までは手が届いていないものの、前準備として、GitHubとの連携を含めつつAWS CodeBuild単体でのCloudFormationを作成するまでの手続きをまとめてみました。

CodeBuildとGitHubの連携

CodeBuildとGitHubとの連携は別途事前に管理コンソール上で行う必要があります。

  • GitHub上でPersonal access tokensを発行する
  • CodeBuildの新規作成でTokenを登録する

といった手続きですが、CodeBuildの新規作成はあくまでToken登録目的となります。

GitHub上でPersonal access tokensを発行する

Developer settingsにてPersonal access tokensから「Generate new token」を選択し、今回扱うトークンを作成します。

CodeBuildの新規作成でTokenを登録する

CodeBuildにて新規プロジェクト作成を選択します。

送信元を以下の構成にて選択・入力します。

ソースプロバイダ
GitHub
リポジトリ
GitHub の個人用アクセストークンで接続する
GitHub の個人用アクセストークン
生成したTokenを入力して「トークンの保存」を選択

下図のようにGitHubリポジトリが参照できれば成功です。

フォーム最下にある「キャンセル」を選択します。

CloudFormationテンプレートを作成する

CodeBuildの設定テンプレートを作成します。テンプレート設定項目については以下公式ドキュメントを参考にします。

先ずは管理コンソール上で完成させる

公式ドキュメントの項目を見ても分かりづらいため、管理コンソールにてビルドプロジェクトを作成し、実際にビルドを成功させます。

BuildSpecについては以下のような内容でも問題ありません。

version: 0.2
phases:
  install:
    runtime-versions:
      docker: 18
      python: 3.7
  build: 
    commands:
       - git status

プロジェクト設定を見ながらテンプレートを作成する

ビルドが成功したプロジェクトの各設定と、公式ドキュメント上の設定項目を見比べながら入力していきます。

AWS::IAM::Roleにて今回設定したAssumeRolePolicyDocumentは以下の通りです。

Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: "Allow"
            Action: 'sts:AssumeRole'
            Principal:
              Service: codebuild.amazonaws.c

AWS::CodeBuild::ProjectのSource → Authはドキュメントを読むと入力に迷うところですが、今回はType: OAUTHのみで問題ありませんでした。

Source:
        Auth:
          Type: OAUTH
        Location: 'https://github.com/xxxxxxxxxxxxxxxxxxxx/xxxx.git'
        Type: GITHUB
        ReportBuildStatus: true
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                docker: 18
                python: 3.7
            build:
              commands:
                - git st

AWS::CodeBuild::ProjectのTriggers → FilterGroupsについては配列の配列という入れ子にする必要があります。これも公式ドキュメントを読んだ限りでは把握し難い箇所です。

Triggers:
        Webhook: true
        FilterGroups:
          - - Type: EVENT
              Pattern: PUSH
            - Type: HEAD_REF
              Pattern: ^refs/tags/pre-release

利用するポリシーについて

必要となるポリシーがはっきりとはしないため、公式ドキュメントを参考にしています。

- 'ecr:GetAuthorizationToken'
              - 'ecr:BatchCheckLayerAvailability'
              - 'ecr:GetDownloadUrlForLayer'
              - 'ecr:GetRepositoryPolicy'
              - 'ecr:DescribeRepositories'
              - 'ecr:ListImages'
              - 'ecr:DescribeImages'
              - 'ecr:BatchGetImage'
              - 'ecr:InitiateLayerUpload'
              - 'ecr:UploadLayerPart'
              - 'ecr:CompleteLayerUpload'
              - 'ecr:PutImage'
              - 'codepipeline:*'
              - 'codebuild:*'
              - 'events:*'
              - 's3:CreateBucket'
              - 's3:GetBucket*'
              - 's3:PutBucket*'
              - 's3:GetObject*'
              - 's3:ListAllMyBuckets'
              - 's3:ListBucket*'
              - 's3:PutObject*'
              - 'logs:CreateLogGroup'
              - 'logs:CreateLogStream'
              - 'logs:PutLogEvents'
              - 'iam:AddRoleToInstanceProfile'
              - 'iam:AttachRolePolicy'
              - 'iam:AttachGroupPolicy'
              - 'iam:AttachUserPolicy'
              - 'iam:CreateInstanceProfile'
              - 'iam:CreatePolicy'
              - 'iam:CreateRole'
              - 'iam:GetRole'
              - 'iam:ListAttachedGroupPolicies'
              - 'iam:ListAttachedUserPolicies'
              - 'iam:ListGroups'
              - 'iam:ListPolicies'
              - 'iam:ListUsers'
              - 'iam:ListRoles'
              - 'iam:PutRolePolicy'
              - 'iam:PassRole'
              - 'iam:UpdateAssumeRolePolicy'
            

テンプレート全体

今回作成したテンプレートです。buildspecについては今後組み立てることとして仮入力にしています。

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  BuildRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: "Allow"
            Action: 'sts:AssumeRole'
            Principal:
              Service: codebuild.amazonaws.com
  BuildPolicies:
    Type: "AWS::IAM::Policy"
    Properties:
      PolicyName: Polocy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Action:
              - logs:*
            Resource: '*'
            Effect: Allow
          - Resource: "*"
            Effect: Allow
            Action:
              - 'ecr:GetAuthorizationToken'
              - 'ecr:BatchCheckLayerAvailability'
              - 'ecr:GetDownloadUrlForLayer'
              - 'ecr:GetRepositoryPolicy'
              - 'ecr:DescribeRepositories'
              - 'ecr:ListImages'
              - 'ecr:DescribeImages'
              - 'ecr:BatchGetImage'
              - 'ecr:InitiateLayerUpload'
              - 'ecr:UploadLayerPart'
              - 'ecr:CompleteLayerUpload'
              - 'ecr:PutImage'
              - 'codepipeline:*'
              - 'codebuild:*'
              - 'events:*'
              - 's3:CreateBucket'
              - 's3:GetBucket*'
              - 's3:PutBucket*'
              - 's3:GetObject*'
              - 's3:ListAllMyBuckets'
              - 's3:ListBucket*'
              - 's3:PutObject*'
              - 'logs:CreateLogGroup'
              - 'logs:CreateLogStream'
              - 'logs:PutLogEvents'
              - 'iam:AddRoleToInstanceProfile'
              - 'iam:AttachRolePolicy'
              - 'iam:AttachGroupPolicy'
              - 'iam:AttachUserPolicy'
              - 'iam:CreateInstanceProfile'
              - 'iam:CreatePolicy'
              - 'iam:CreateRole'
              - 'iam:GetRole'
              - 'iam:ListAttachedGroupPolicies'
              - 'iam:ListAttachedUserPolicies'
              - 'iam:ListGroups'
              - 'iam:ListPolicies'
              - 'iam:ListUsers'
              - 'iam:ListRoles'
              - 'iam:PutRolePolicy'
              - 'iam:PassRole'
              - 'iam:UpdateAssumeRolePolicy'
      Roles:
        - Ref: "BuildRole"
  LogBucket:
    Type: 'AWS::S3::Bucket'
  Build:
    Type: 'AWS::CodeBuild::Project'
    Properties:
      Artifacts:
        Type: NO_ARTIFACTS
      Name: CodeTest
      ServiceRole: !GetAtt BuildRole.Arn
      Environment:
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:1.0-1.13.0
        Type: LINUX_CONTAINER
      Triggers:
        Webhook: true
        FilterGroups:
          - - Type: EVENT
              Pattern: PUSH
            - Type: HEAD_REF
              Pattern: ^refs/tags/pre-release-v.*
      Source:
        Auth:
          Type: OAUTH
        Location: 'https://github.com/xxxxxxxxxxx/xxxx.git'
        Type: GITHUB
        ReportBuildStatus: true
        BuildSpec: |
          version: 0.2
          phases:
            install:
              runtime-versions:
                docker: 18
                python: 3.7
            build:
              commands:
                - git status
      LogsConfig:
        S3Logs:
          Status: ENABLED
          Location: !GetAtt LogBucket.Arn

あとがき

CloudFormationの設定入力はリソース毎にページが別れていることと、想定しているテンプレート例もなかなか見つからないため、入力のvalidateのチェック及びStackの試作が不可欠です。また、今回はGitHubのOAuth接続もあり、本当に正常にいっているのか検証を只管繰り返していました。

一度出来上がってしまえば、メンテナンスコストや属人性排除の点で大きなメリットが出てきます。CodeBuild単体でのCICDを検討されている方はあまり多くないかもしれませんが、今回の手続きが参考になれば幸いです。