0
0

TerraformでGitHub Actionsのセルフホストランナーとして動作するAWS CodeBuildを自動構築する

Last updated at Posted at 2024-08-04

はじめに

以前の記事では、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.nameUpdate Pull Requestのブロックは、上記リンクにも記載されている、プルリクエストに対する書き戻しの処理だ。
ただし、Hadolintは正常終了時に何も結果を出力しないため、ifのブロックに&& steps.hadolint_execution.outcome == 'failure'を追加して、エラー時のみコメントを書くように変更している。
また、これにより、エラーの場合もjobs.hadolintが正常終了してしまうため、最後に再度判定を入れて判定エラー時はexit 1をするようにしている。

.github/workflows/main.yml
# 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のコンソール画面では、

image.png

といった感じコメントでエラー内容が表示されている。

これで、AWS CodeBuildを使ってお手軽にGitHub Actionsを動作させられるようになった!

0
0
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
0
0