IAMをゼロから設定し、Code Buildのビルドプロジェクトを作成・ビルド実行までやってみた（Management Console版）

## はじめに
GitHub Actions、CircleCiを使ったCIは構築した事があったが、Code BuildでのCIはやった事がなかったのでやってみた
その際の備忘録を残す

※前提条件として既にCode Commitにbuild対象のソースコードがある状態からスタートした
※ビルドするものはGoのLambda関数

buildspec.ymlや、GitHub Actionsでのビルドは以下のリポジトリを参照

https://github.com/yuta-katayama-23/Go

## Code Buildのビルドプロジェクトを作成する
### Management Consoleからビルドプロジェクトを作成する
以下の画像のように設定できる

| 1 | 2 | 3 | 4 |
|:-:|:-:|:-:|:-:|
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/93f0af98-6363-6d0d-68f9-17d44940727c.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/6e6f9d41-f5f3-4197-b0dd-1e71ae3ab22f.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a9851926-1406-2e30-1f0d-95071a3cec1e.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/8ab8975b-7251-de43-5843-ff3f9a0122b8.png) |

※上記の画像では、`イメージ`（docker image）は`aws/codebuild/standard:4.0`になっているが、作成後にGoの1.16をruntime versionに指定できるのは`aws/codebuild/standard:5.0`である事が後かは判明したのでそれに修正した

※「ソース」で`ブランチ(ビルドするコードを含むブランチを選択します。)`を設定しているが、これはstart buildをした時にオプションで何も指定されなかった時にどのソースバージョンでbuildするか？を決められるようにするために設定しているもので、start build時に指定すればこの設定は上書きされる

>CodeCommit の場合、ビルドするソースコードのバージョンに対応するコミット ID。sourceVersion が指定されなければ、デフォルトブランチの HEAD コミット ID が使用されます。(sourceVersion にタグ名は指定できません。しかし、タグのコミット ID は指定できます。)

・参考：[ビルドの実行 (AWS CLI)](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/run-build-cli.html)

### ビルドプロジェクトを作成するために必要なIAM
上記のビルドプロジェクトを作成する上で必要になる権限はいくつかある
※Code Buildのビルドプロジェクトは、他のAWSサービスとの連携が結構あるのでその分必要な権限も多い

- Code Build：言わずもがな
- Code Commit：今回はソースプロバイダをCode Commitにするので必要
- IAM：ビルドプロジェクトへサービスロールをアタッチしたり、そのサービスロールを作成するために必要
- S3：アーティファクトの保存先のBucket作成、保存されたものへアクセスするために必要

・参考：[AWS CodeBuild のアクセス許可に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/auth-and-access-control-permissions-reference.html)

#### Code Build
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "codebuild:BatchGetProjects",
                "codebuild:CreateProject"
            ],
            "Resource": "arn:aws:codebuild:ap-northeast-1:xxxxxxxxxxxx:project/go-build-console"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "codebuild:ListCuratedEnvironmentImages",
                "codebuild:ListProjects"
            ],
            "Resource": "*"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| codebuild:BatchGetProjects | 1つ以上のビルドプロジェクトに関する情報を取得する権限。単純に自分で作成したビルドプロジェクトの内容を確認するために必要。 |
| codebuild:CreateProject | ビルドプロジェクトを作成する権限。 |
| codebuild:ListCuratedEnvironmentImages | AWSCodeBuildのコンテナ（Docker）イメージに関する情報を取得する権限。これないと`環境イメージ`の選択とかができない。 |
| codebuild:ListProjects | ビルドプロジェクト名の一覧をリストを取得する権限。Code Buildの`ビルドプロジェクト`メニューを選択して表示されるビルドプロジェクト一覧を見れるようにするに必要。 |

#### Code Commit
`ソース`の項目を設定する上で必要になる

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "codecommit:GetCommit",
                "codecommit:GetCommitHistory",
                "codecommit:GetReferences"
            ],
            "Resource": "arn:aws:codecommit:ap-northeast-1:xxxxxxxxxxxx:go"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "codecommit:ListRepositories",
            "Resource": "*"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| codecommit:GetCommit | コミットメッセージやコミッター情報などのコミットに関する情報を取得する権限。`ソース`の`コミット ID - オプショナル`の部分を設定できるようにするために必要。 |
| codecommit:GetCommitHistory | リポジトリ内のコミットの履歴に関する情報を取得する権限。`ソース`の`コミット ID - オプショナル`の部分を設定できるようにするために必要。 |
| codecommit:GetReferences | CodeCommitリポジトリ内の参照に関する詳細を取得する権限。`ソース`の`ソースバージョン`を表示させるために必要。 |
| codecommit:ListRepositories | AWSアカウントの現在のリージョンにあるAWSCodeCommitリポジトリに関する情報を一覧表示する権限。`ソース`の`リポジトリ`の選択リストを表示させるための必要。 |

※`ソース`とは以下の画像の部分で、`コミット ID - オプショナル`とは以下のリストの部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/4626f85d-3ed7-230b-639f-c03eee51b019.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/a171ce2c-0d98-38d0-6008-5c419fd26767.png)

#### IAM
ビルドプロジェクトにアタッチしたサービスロールに対し、ビルドプロジェクトが必要になる権限（アーティファクトの保存先であるS3の権限、ビルドのlogを記録するためのCloud Watch Logsの権限、ビルド実行後のレポートを出力するためのCode Buildの権限）などのポリシーを追加したりそのポリシーのバージョンを操作するための権限

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:CreatePolicy",
                "iam:PassRole",
                "iam:CreateRole",
                "iam:AttachRolePolicy",
                "iam:CreatePolicyVersion",
                "iam:DeletePolicyVersion"
            ],
            "Resource": [
                "arn:aws:iam::xxxxxxxxxxxx:role/go-build-console",
                "arn:aws:iam::xxxxxxxxxxxx:policy/*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "iam:ListPolicies",
                "iam:ListRoles"
            ],
            "Resource": "*"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| iam:CreatePolicy | 新しい管理ポリシーを作成する権限。AWS側で自動的にポリシー作成できるようにするために必要。 |
| iam:PassRole | サービスに役割を渡す権限。Code Buildのビルドプロジェクトにサービスロールを指定できるようにするのに必要。 |
| iam:CreateRole | 新しいロールを作成する権限。新規でロールを作成するのに必要で、サービスロールとしてアタッチするロール作成で必要になった。 |
| iam:AttachRolePolicy | 指定されたIAMロールに管理ポリシーをアタッチする権限。AWS側で自動的にロールにポリシーをアタッチできるようにするのに必要。 |
| iam:CreatePolicyVersion | 指定された管理ポリシーの新しいバージョンを作成する権限。AWS側で自動的にポリシーを作成できるようにするために必要。 |
| iam:DeletePolicyVersion | 指定された管理対象ポリシーからバージョンを削除する権限。AWS側で自動的にポリシーを変更できるようにするために必要。 |
| iam:ListPolicies | すべての管理対象ポリシーを一覧表示する権限。AWS側で自動的にポリシーを変更できるようにするために必要。 |
| iam:ListRoles | 指定されたパスプレフィックスを持つIAMロールを一覧表示する権限。`環境`の`サービスロール`にロールを一覧表示させるのに必要。 |

※AWS側で自動的に
　→AWSが勝手にやるが、その権限はログインユーザの権限で動くのでログインユーザにそれを行う権限を付与させる必要がある

※Management Consoleからビルドプロジェクトを作成すると、自動的にサービスロールにポリシーが新規でアタッチされる（`go-build-console`というロールは自分で作成したものだが、`CodeBuildBasePolicy-go-build-console-ap-northeast-1`というポリシーはAWSによって自動的に作成されたもの）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/3aef674d-946c-b8c0-90ff-f3b5cf4c4618.png)

#### S3
ビルドプロジェクトを作成する際に、ソースプロバイダやアーティファクトなどでS3を設定できるので最低限S3のアクセス権限がないとビルドプロジェクト作成が`Access Denied`になる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/cfd275eb-bcad-ac03-7f18-ad83c571cfa9.png)

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:CreateBucket",
            "Resource": "arn:aws:s3:::go-build-console"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| s3:CreateBucket | 新しいバケットを作成する権限。`アーティファクト`で保存先（`タイプ`）にS3を選択した際に、`バケット名`を指定するがそのBucketを作成するのに必要。 |
| s3:ListAllMyBuckets | リクエストの認証された送信者が所有するすべてのバケットを一覧表示する権限。`アーティファクト`で保存先（`タイプ`）にS3を選択した際の`バケット名`を指定際に表示されるBucket一覧を表示させるために必要。 |

## buildspecを作成しビルドを実行する
### buildspecを実装する
作業に必要なIAMは[buildspecを実装しビルドを実行するのに必要なIAM](#buildspecを実装しビルドを実行するのに必要なiam)の項を参照

#### SSHでgit cloneできるようにする
以下の参考のサイトにある通り

・参考：[Step ステップ 3: Linux、macOS、または Unix で認証情報を設定する](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/setting-up-ssh-unixes.html#setting-up-ssh-unixes-keys)
・参考：[複数のAWSアカウントを利用する場合のCodeCommit SSHパスの設定方法](https://qiita.com/Junpei_Takagi/items/0ff85de3bf2de3aaf4eb)

#### Code Buildのyamlの書き方のルール
正直全部は分からないので、ひとまず[CodeBuild のビルド仕様に関するリファレンス](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)を見ながら手さぐりで書いてみた

#### GoのLambdaのbuild（zipファイルを作成する）のyaml
Goのruntimeの指定は以下のようにできる

```yaml
install: 
   runtime-versions: 
     golang: 1.16
```

後はLinux環境でのGoのLambdaのbuild手順（[.zip ファイルアーカイブを使用して Go Lambda 関数をデプロイする](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/golang-package.html#golang-package-mac-linux)）通りにコマンドを実行すればよく、buildspec.yml全体としては以下のようになる

```yaml:buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      golang: 1.16

  pre_build:
    commands:
      - echo "go version"
      - go version
      - echo "go get aws lambda library"
      - go get github.com/aws/aws-lambda-go/lambda

  build:
    commands:
      - echo "go build"
      - GOOS=linux go build -o main lambda.go

  post_build:
    commands:
      - echo "create zip"
      - zip main.zip main config.yaml

artifacts:
  files:
    - main.zip
```

・参考：[CodeBuild の Amazon ECR サンプル　Go プロジェクトのファイル](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-ecr.html#sample-ecr-go-project-files)
・参考：[使用可能なランタイム　Linux イメージのランタイム](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/available-runtimes.html#linux-runtimes)
・参考：[ランタイムバージョン](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/runtime-versions.html)

### ビルドを実行する
buildが成功すると以下のようにS3にビルド成果物が`s3:::go-build-console/artifact/`以下にuploadされる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/4f0ca79b-0b67-323d-9cdb-e9929b32ff6b.png)

### buildspecを実装しビルドを実行するのに必要なIAM
上記の[ビルドプロジェクトを作成するために必要なIAM](#ビルドプロジェクトを作成するために必要なiam)で付与した権限以外に以下のactionを実行する権限を追加で付与する
（Code Commitについてはbuildspecが見れないと困るので追加で権限付与をしている）

#### Code Build
| action名 | 説明 |
|---|---|
| codebuild:ListBuildsForProject | 指定されたビルドプロジェクトのビルドIDのリストを取得する権限。ビルド履歴を閲覧できるようにするのに必要。 |
| codebuild:StartBuild | ビルドの実行を開始する権限。 |
| codebuild:UpdateProject | 既存のビルドプロジェクトの設定を変更する権限。 |
| codebuild:BatchGetBuilds | 1つ以上のビルドに関する情報を取得する権限。ビルド履歴のビルド1つ1つの詳細を確認できるようにするのに必要。 |

※`"Resource"`の設定は、[ビルドプロジェクトを作成するために必要なIAM](#ビルドプロジェクトを作成するために必要なiam)のCode Buildの項と同じで、`"Resource": "arn:aws:codebuild:ap-northeast-1:xxxxxxxxxxxx:project/go-build-console"`

#### Code Commit
| action名 | 説明 |
|---|---|
| codecommit:GetRepository | AWSCodeCommitリポジトリに関する情報を取得する権限。 |
| codecommit:GetTree | AWSCodeCommitコンソールからAWSCodeCommitリポジトリ内の指定されたツリーのコンテンツを表示する権限。リポジトリのフォルダ（階層構造）を表示させるのに必要。 |
| codecommit:GetObjectIdentifier | ブロブ、ツリーを解決する権限。 |
| codecommit:GetBlob | AWSCodeCommitコンソールからAWSCodeCommitリポジトリ内の個々のファイルのエンコードされたコンテンツを表示する権限。ファイルの中身を見れるようにするのに必要。 |
| codecommit:GitPush | ローカルリポジトリからAWSCodeCommitリポジトリに情報をプッシュする権限。 |

※`"Resource"`の設定は、[ビルドプロジェクトを作成するために必要なIAM](#ビルドプロジェクトを作成するために必要なiam)のCode Commitの項と同じで、`"Resource": "arn:aws:codecommit:ap-northeast-1:xxxxxxxxxxxx:go"`

#### S3
| action名 | 説明 |
|---|---|
| s3:ListBucket | Amazon S3バケット内のオブジェクトの一部またはすべてを一覧表示する権限。build成果物を確認するために必要。 |

※`"Resource"`の設定は、[ビルドプロジェクトを作成するために必要なIAM](#ビルドプロジェクトを作成するために必要なiam)のS3の`CreateBucket`と同じで、`"Resource": "arn:aws:s3:::go-build-console"`

#### Cloud Watch Logs
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "logs:GetLogEvents",
            "Resource": "arn:aws:logs:ap-northeast-1:xxxxxxxxxxxx:log-group:/aws/codebuild/go-build-console:log-stream:*"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| logs:GetLogEvents | 指定されたログストリームからログイベントを取得する権限。ビルド結果のlogを見るために必要。 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/aedd9c58-a13a-cfb8-0f4d-ca462625eb93.png)

#### IAM
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "iam:UploadSSHPublicKey",
            "Resource": "arn:aws:iam::xxxxxxxxxxxx:user/codebuild-only"
        }
    ]
}
```
| action名 | 説明 |
|---|---|
| iam:UploadSSHPublicKey | SSH公開鍵をアップロードし、それを指定されたIAMユーザーに関連付ける権限。gitの操作をSSHで行うのに必要。 |

## おまけ
### S3のIAMで追加した方がいいと思われるもの
以下はBucketを作成した時に`ブロックパブリックアクセス (バケット設定)`の`パブリックアクセスをすべて ブロック`がオフになっている状態を、オンにする修正するのに必要な権限
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/d3dc996a-13f9-7067-35aa-5469c3f739a2.png)

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:GetBucketPublicAccessBlock",
                "s3:PutBucketPublicAccessBlock"
            ],
            "Resource": "arn:aws:s3:::go-build-console"
        }
    ]
}
```

・参考：[Amazon S3 ストレージへのパブリックアクセスのブロック Permissions](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/access-control-block-public-access.html#access-control-block-public-access-permissions)

### Code Buildのlog
```
[Container] 2021/09/01 13:01:30 Waiting for agent ping
[Container] 2021/09/01 13:01:33 Waiting for DOWNLOAD_SOURCE
[Container] 2021/09/01 13:01:38 Phase is DOWNLOAD_SOURCE
[Container] 2021/09/01 13:01:38 CODEBUILD_SRC_DIR=/codebuild/output/src575922894/src/git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/go
[Container] 2021/09/01 13:01:38 YAML location is /codebuild/output/src575922894/src/git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/go/.aws/buildspec.yml
[Container] 2021/09/01 13:01:38 No commands found for phase name: install
[Container] 2021/09/01 13:01:38 Processing environment variables
[Container] 2021/09/01 13:01:38 Selecting 'golang' runtime version '1.16' based on manual selections...
[Container] 2021/09/01 13:01:38 Running command echo "Installing Go version 1.16 ..."
Installing Go version 1.16 ...

[Container] 2021/09/01 13:01:38 Running command goenv global  $GOLANG_16_VERSION

[Container] 2021/09/01 13:01:38 Moving to directory /codebuild/output/src575922894/src/git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/go
[Container] 2021/09/01 13:01:39 Registering with agent
[Container] 2021/09/01 13:01:39 Phases found in YAML: 4
[Container] 2021/09/01 13:01:39  INSTALL: 0 commands
[Container] 2021/09/01 13:01:39  PRE_BUILD: 4 commands
[Container] 2021/09/01 13:01:39  BUILD: 2 commands
[Container] 2021/09/01 13:01:39  POST_BUILD: 2 commands
[Container] 2021/09/01 13:01:39 Phase complete: DOWNLOAD_SOURCE State: SUCCEEDED
[Container] 2021/09/01 13:01:39 Phase context status code:  Message: 
[Container] 2021/09/01 13:01:39 Entering phase INSTALL
[Container] 2021/09/01 13:01:39 Phase complete: INSTALL State: SUCCEEDED
[Container] 2021/09/01 13:01:39 Phase context status code:  Message: 
[Container] 2021/09/01 13:01:39 Entering phase PRE_BUILD
[Container] 2021/09/01 13:01:39 Running command echo "go version"
go version

[Container] 2021/09/01 13:01:39 Running command go version
go version go1.16.4 linux/amd64

[Container] 2021/09/01 13:01:40 Running command echo "go get aws lambda library"
go get aws lambda library

[Container] 2021/09/01 13:01:40 Running command go get github.com/aws/aws-lambda-go/lambda
go: downloading github.com/aws/aws-lambda-go v1.26.0

[Container] 2021/09/01 13:01:50 Phase complete: PRE_BUILD State: SUCCEEDED
[Container] 2021/09/01 13:01:50 Phase context status code:  Message: 
[Container] 2021/09/01 13:01:50 Entering phase BUILD
[Container] 2021/09/01 13:01:50 Running command echo "go build"
go build

[Container] 2021/09/01 13:01:50 Running command GOOS=linux go build -o main lambda.go
go: downloading github.com/jinzhu/configor v1.2.1
go: downloading github.com/slack-go/slack v0.9.4
go: downloading github.com/BurntSushi/toml v0.3.1
go: downloading gopkg.in/yaml.v2 v2.2.3
go: downloading github.com/pkg/errors v0.8.0
go: downloading github.com/gorilla/websocket v1.4.2

[Container] 2021/09/01 13:01:56 Phase complete: BUILD State: SUCCEEDED
[Container] 2021/09/01 13:01:56 Phase context status code:  Message: 
[Container] 2021/09/01 13:01:56 Entering phase POST_BUILD
[Container] 2021/09/01 13:01:56 Running command echo "create zip"
create zip

[Container] 2021/09/01 13:01:56 Running command zip main.zip main config.yaml
  adding: main (deflated 50%)
  adding: config.yaml (deflated 71%)

[Container] 2021/09/01 13:01:57 Phase complete: POST_BUILD State: SUCCEEDED
[Container] 2021/09/01 13:01:57 Phase context status code:  Message: 
[Container] 2021/09/01 13:01:57 Expanding base directory path: .
[Container] 2021/09/01 13:01:57 Assembling file list
[Container] 2021/09/01 13:01:57 Expanding .
[Container] 2021/09/01 13:01:57 Expanding file paths for base directory .
[Container] 2021/09/01 13:01:57 Assembling file list
[Container] 2021/09/01 13:01:57 Expanding main.zip
[Container] 2021/09/01 13:01:57 Found 1 file(s)
[Container] 2021/09/01 13:01:57 Phase complete: UPLOAD_ARTIFACTS State: SUCCEEDED
[Container] 2021/09/01 13:01:57 Phase context status code:  Message: 
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/eea900ab-50b1-503f-9995-53fd3233787b.png)