LoginSignup
16
3

More than 1 year has passed since last update.

GitHub ActionsでWorkload Identity 連携を使ってTerraformを実行する

Last updated at Posted at 2022-12-12

この記事は MicroAd Advent Calendar 2022 の13日目の記事です。

概要

GitHub ActionsでTerraformを実行する手順について記載します。
Terraformで作成する対象はGoogle Cloud のリソースを想定し、認証にはWorkload Identity 連携を利用します。
情報としては珍しいものではありませんが、実際に動かすまでに時間がかかったので備忘録として投稿します。
(TerraformやActionsについて基本な説明もありませんので、ご承知ください。)

Workload Identity 連携とは

Workload Identity 連携とは、OpenID Connect(OIDC)やAWSなどのGoogle Cloud以外のIDを利用し、Google Cloudのサービスアカウントの権限を借用する仕組みです。

従来はCIでGoogle Cloudにアクセスするには、サービスアカウントのキーをCIツールに登録する必要がありましたが、Workload Identity 連携を利用することで、CI側にはキーなどのクレデンシャル情報を登録せずにサービスアカウントの権限を利用することができます。そのため、キーの漏洩リスクや管理コストを減らすことができます。

GitHub ActionsからWorkload Identity 連携を利用するために、Workload Identity プールとプロバイダの準備をします。

準備の手順は以下に記載されていますが、今回はTerraformを用いて作成します。
サイト内には、管理コンソールとgcloudコマンドによる手順が記載されていますので、最初は手順を確認しつつ手動での構築がおすすめです。

ID 連携により有効期間の短い認証情報を取得する | IAM のドキュメント | Google Cloud

Terraformでの構築に使ったコードを以下に載せます。

providers.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "4.45.0"
    }
  }
}

provider "google" {
  project = local.project_id
  region  = local.region
  zone    = local.zone
}
locals.tf
locals {
  project_id = "<your-project-id>"
  region     = "us-central1"
  zone       = "us-central1-a"
}
locals {
  terraform_sa = "<Actions実行時に借用したい権限を持つサービスアカウント>"
  repo_name    = "<GitHubユーザ名>/<GitHubリポジトリ名>"
}

main.tf
resource "google_project_service" "project" {
  project = local.project_id
  service = "iamcredentials.googleapis.com"
}

resource "google_iam_workload_identity_pool" "github_actions_pool" {
  provider                  = google-beta
  project                   = local.project_id
  workload_identity_pool_id = "github-oidc-pool"
  display_name              = "github-oidc-pool"
  description               = "GitHub Actionsで使用"
}

resource "google_iam_workload_identity_pool_provider" "github_actions" {
  provider                           = google-beta
  project                            = local.project_id
  workload_identity_pool_id          = google_iam_workload_identity_pool.github_actions_pool.workload_identity_pool_id
  workload_identity_pool_provider_id = "github-actions"
  display_name                       = "github-actions"
  description                        = "GitHub Actionsで使用"
  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.repository" = "assertion.repository"
  }
  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

# Actionsで借用したい権限を持つサービスアカウント
data "google_service_account" "terraform_sa" {
  account_id = local.terraform_sa
}

# principalSetでどのリポジトリで利用するかを指定している
resource "google_service_account_iam_member" "terraform_sa_workload_identity_user" {
  service_account_id = data.google_service_account.terraform_sa.id
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions_pool.name}/attribute.repository/${local.repo_name}"
}

GitHub Actions 実行例

Workload Identity プール・プロバイダの準備ができたら、ActionsのWorkflowを追加し、サービスアカウトの権限でTerraformが実行できる試します。

以下にWorkflowの例を示します。
参考にした情報は以下の通りになっており、その他細かい注意点はコード中に記載します。

前半の認証に関わる部分は、前節にも記載している「ID 連携により有効期間の短い認証情報を取得する」にActionsでの記述例が記載されています。

Terraformの実行部分は以下を参考にしました。PRへのコメントなど見やすくなっておりいい感じです。

.github/workflows/terraform.yml
name: terraform

# PR作成とマージ後のmainブランチへのPushをトリガーとする
on:
  push:
    branches:
      - main
  pull_request:

# ./infra ディレクトリ配下に tf ファイルがある想定
defaults:
  run:
    working-directory: ./infra

jobs:
  terraform-workflow:
    runs-on: ubuntu-latest
    permissions:
      # workload identity連携によるToken発行には以下権限が必要
      id-token: write
      contents: read
      # PR画面でterraform plan結果を投稿させるために以下権限が必要
      pull-requests: write

    steps:
      # https://github.com/google-github-actions/auth
      - uses: actions/checkout@v3
      - id: 'auth'
        name: 'Authenticate to Google Cloud'
        # 古いバージョンのactionを利用する例がWebで見かけたので最新になっているか注意
        uses: 'google-github-actions/auth@v1'
        with:
          # 以下、プロバイダのIDは管理コンソールから確認できる
          workload_identity_provider: 'projects/<project number>/locations/global/workloadIdentityPools/<workload Identity Poolの名前>/providers/<プロバイダ名>'
          service_account: '<サービスアカウントをE-mail形式で記載>'

      # ここから terraform 
      # https://github.com/hashicorp/setup-terraform
      - uses: hashicorp/setup-terraform@v2
      # 上記と別に古いAction(hashicorp/terraform-github-actions)があるので、そちらを使わないように注意(すでにアーカイブ済み)

      - name: Terraform fmt
        id: fmt
        run: terraform fmt -check -recursive
        continue-on-error: true

      - name: Terraform Init
        id: init
        run: terraform init

      - name: Terraform Validate
        id: validate
        run: terraform validate -no-color

      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color
        continue-on-error: true

      - name: Comment Terraform Plan
        uses: actions/github-script@v6
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\`
            #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
            #### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
            <details><summary>Validation Output</summary>

            \`\`\`\n
            ${{ steps.validate.outputs.stdout }}
            \`\`\`

            </details>

            #### Terraform Plan 📖\`${{ steps.plan.outcome }}\`

            <details><summary>Show Plan</summary>

            \`\`\`\n
            ${process.env.PLAN}
            \`\`\`

            </details>

            *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.tf_actions_working_dir }}\`, Workflow: \`${{ github.workflow }}\`*`;

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })
      # terraform status で失敗した際に workflow も止める
      - name: Terraform Plan Status
        if: steps.plan.outcome == 'failure'
        run: exit 1

      # main ブランチへの push した際のみ terraform apply を実行する
      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -input=false

上記Workflowと./infra配下にtfファイルを追加し、PR作成するとActionsが実行され、TerraformのPlan結果がPR画面に投稿されます。

pr-comment.png

16
3
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
16
3