はじめに
以前の記事では、GitLab Self Managedに接続するAWS CodeBuildとAWS CodePipelineを作成してみた。
GitLab Self Managedとのインテグレーションでは、AWS CodeBuildやAWS CodePipelineの実行結果をプルリクエスト上で確認するためにはいろいろな作り込みが必要になり、それ自体がトイルになる可能性があった。
一方で、GitHubでは、2024/4/24より、GitHub ActionsのセルフホストランナーとしてAWS CodeBuildを指定できるようになった。
これにより、GitHub Actionsのプリミティブな機能を活用しつつ、コンピューティングリソースとしてAWS CodeBuildの環境を利用して、利用料金をAWSに集約したり、AWSリソースにアクセスしやすくすることが可能になった。
今回は、このGitHub Actionsとの連携をTerraformで作成していく。
GitHubのパーソナルアクセストークンを払い出す
パーソナルアクセストークン未設定の場合は、以下のページを参考にしながら、GitHubのパーソナルアクセストークンを払い出しておく。
今回の検証に用いる機能のために、トークンには以下の権限を付与する。
権限名 | 権限 | 必要な処理 |
---|---|---|
Administration | Read and write | リポジトリの作成 |
Contents | Read and write | ブランチの作成 |
Metadata | Read-only | デフォルトで付与(外せない) |
Variables | Read and write | GitHub Actions変数の作成 |
Webhooks | Read and write | WebHookの作成 |
TerraformのProvider設定
上記で払い出したトークンを使ってアクセスするように、以下のようにProvdiderを設定する。
アクセストークンをコードに埋め込むのはアンチパターンであるため、今回はTerraformの変数として渡すようにする。
terraform {
required_providers {
github = {
source = "integrations/github"
}
}
}
provider "github" {
token = var.github_token
}
AWS CodeBuildが使用するIAMロールの設定
IAMロールの設定は、最低限必要なのは以下のCodeBuildで標準的に必要になる設定のみだ。
resource "aws_iam_role" "codebuild" {
name = local.iam_codebuild_role_name
assume_role_policy = data.aws_iam_policy_document.codebuild_assume.json
}
data "aws_iam_policy_document" "codebuild_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"codebuild.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy" "codebuild" {
name = local.iam_codebuild_policy_name
role = aws_iam_role.codebuild.id
policy = data.aws_iam_policy_document.codebuild_custom.json
}
data "aws_iam_policy_document" "codebuild_custom" {
statement {
effect = "Allow"
actions = [
"codeBuild:StartBuild",
]
resources = [
aws_codebuild_project.example.arn,
]
}
statement {
effect = "Allow"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = [
aws_cloudwatch_log_group.codebuild.arn,
"${aws_cloudwatch_log_group.codebuild.arn}:log-stream:*",
]
}
}
Amazon CloudWatch Logsの設定
Amazon CloudWatch Logsは今回はとりあえず設定しているだけなので、あまり細かいことは気にしていない。
実際に利用する際は組織のポリシーに合わせて設定を変更しよう。
resource "aws_cloudwatch_log_group" "codebuild" {
name = local.clouddwatch_loggroup_name
retention_in_days = 3
}
GitHubの設定
リポジトリ本体の設定
リポジトリはintegrations/github
プロバイダのgithub_repository
のリソースで作成する。
特段難しいことはない。
ブランチの作成は、mainブランチに何かしらのソースコードが入っていないと行えないため、下記の作成後、適当なファイルをPUSHしておこう。
resource "github_repository" "example" {
name = "example"
description = "GitHubとCodeBuildのインテグレーションのサンプル"
visibility = "private"
}
ブランチの作成
ブランチの作成も特に難しいことはない。
source_branch
は省略した場合はmain
が自動的に選択される。
resource "github_branch" "example_feature_test1" {
repository = github_repository.example.name
source_branch = "main"
branch = "feature/test1"
}
GitHub Actionsがプルリクエストを更新する権限を付与
設定はしなくても最悪メイン動作には影響がないが、GitHub Actionsが検査した結果等をプルリクエストのコメントとして書き戻すには必要な設定となる。
この設定はまだTerraformに対応していない。プルリクエストは発行されているようなので、マージされるのを待とう。
resource "terraform_data" "github_workflow_permission_change" {
provisioner "local-exec" {
command = <<-EOF
curl -s -L \
-X PUT \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${var.github_token}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${github_repository.example.full_name}/actions/permissions/workflow \
-d '{"default_workflow_permissions":"write","can_approve_pull_request_reviews":false}'
EOF
on_failure = fail
}
}
AWS CodeBuildの設定
AWS CodeBuild本体
AWS CodeBuildは以下のように設定する。
AWS CodeBuild自体は特に凝った設定はない。今回、GitHub Actions内でMarketplaceからコンテナ動作するActionを引っ張ってくるため、privileged_mode = "true"
で特権モードで動作するようにしておこう。
resource "aws_codebuild_project" "example" {
name = local.codebuild_project_name
service_role = aws_iam_role.codebuild.arn
source {
type = "GITHUB"
location = github_repository.example.http_clone_url
}
artifacts {
type = "NO_ARTIFACTS"
}
environment {
type = "LINUX_CONTAINER"
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0"
privileged_mode = "true"
}
logs_config {
cloudwatch_logs {
group_name = aws_cloudwatch_log_group.codebuild.name
}
}
cache {
type = "LOCAL"
modes = [
"LOCAL_CUSTOM_CACHE",
]
}
}
AWS CodeBuildのWebHook
AWS CodeBuildのWebHookでは、WORKFLOW_JOB_QUEUED
のイベントパターンを設定する。
プルリクエストやPUSH時といったトリガは、GitHub Actions側で設定するため、こちらでは細かい設定は不要だ。
AWSがトークンを参照できるようにするために、aws_codebuild_source_credential
で入力したトークンを渡そう。
resource "aws_codebuild_source_credential" "example" {
auth_type = "PERSONAL_ACCESS_TOKEN"
server_type = "GITHUB"
token = var.github_token
}
resource "aws_codebuild_webhook" "gitlab_example" {
project_name = aws_codebuild_project.example.name
build_type = "BUILD_BATCH"
filter_group {
filter {
type = "EVENT"
pattern = "WORKFLOW_JOB_QUEUED"
}
}
}
GitHub Actionsに渡す変数の作成
後の処理を楽にするために、以下のGitHub Actions変数を作成しておく。
resource "github_actions_variable" "example_variable" {
repository = github_repository.example.name
variable_name = "CODEBUILD_RUNS_ON_NAME"
value = "codebuild-${aws_codebuild_project.example.name}"
}
GitHub Actionsの設定
ここまで来たらやっとGitHub Actionsの設定だ。
リポジトリトップからのパスで.github/workflows/main.yml
に以下のファイルを置こう。
今回、GitHub Actionsの設定内容についてがメインではないので詳細は割愛する。
なるべくこのYAMLファイルをテンプレート化して再利用性を高めた方が良いため、AWS CodeBuildのプロジェクト名を外部から注入できるようにした。これまでの準備で作成した、CODEBUILD_RUNS_ON_NAME
を、${{ vars.CODEBUILD_RUNS_ON_NAME }}
で参照することで、リポジトリを増やした場合もこの部分を変更することなく使い回せるようになる。
なお、jobs.build
のブロックは、GitHub Actionsがデフォルトで用意するHello World的なものだ。
これだけだと味気ないので、HadolintをMarketplaceから入れて実行してみる。
jobs.hadolint.steps.name
がUpdate Pull Request
のブロックは、上記リンクにも記載されている、プルリクエストに対する書き戻しの処理だ。
ただし、Hadolintは正常終了時に何も結果を出力しないため、ifのブロックに&& steps.hadolint_execution.outcome == 'failure'
を追加して、エラー時のみコメントを書くように変更している。
また、これにより、エラーの場合もjobs.hadolint
が正常終了してしまうため、最後に再度判定を入れて判定エラー時はexit 1
をするようにしている。
# This is a basic workflow to help you get started with Actions
name: CI-on-CodeBuild
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the "main" branch
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ${{ vars.CODEBUILD_RUNS_ON_NAME }}-${{ github.run_id }}-${{ github.run_attempt }}
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v4
# Runs a single command using the runners shell
- name: Run a one-line script
run: echo Hello, world!
# Runs a set of commands using the runners shell
- name: Run a multi-line script
run: |
echo Add other actions to build,
echo test, and deploy your project.
ls -a -1
hadolint:
runs-on: ${{ vars.CODEBUILD_RUNS_ON_NAME }}-${{ github.run_id }}-${{ github.run_attempt }}
steps:
- uses: actions/checkout@v4
- name: Hadolint Execution
id: hadolint_execution
uses: hadolint/hadolint-action@v3.1.0
continue-on-error: true
- name: Update Pull Request
uses: actions/github-script@v6
if: github.event_name == 'pull_request' && steps.hadolint_execution.outcome == 'failure'
with:
script: |
const output = `
#### Hadolint: \`${{ steps.hadolint.outcome }}\`
\`\`\`
${process.env.HADOLINT_RESULTS}
\`\`\`
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
- name: Error exit actions
if: steps.hadolint_execution.outcome == 'failure'
run: exit 1
いざ、動かす!
さて、ここまでやってみたら、プルリクエストを上げてみよう。
今回、Dockerfileの先頭を
FROM public.ecr.aws/nginx/nginx
のようにして、Hadolintがエラーになるようにしてみた。
実際に、GitHub Actionsのコンソール画面では、
といった感じコメントでエラー内容が表示されている。
これで、AWS CodeBuildを使ってお手軽にGitHub Actionsを動作させられるようになった!