利用 GitHub Actions 的 Workload Identity 并结合 Terraform 运行

这篇文章是 MicroAd Advent Calendar 2022 的第13天的文章。

 

简要概括

以下是使用 GitHub Actions 执行 Terraform 的步骤说明。预期 Terraform 要创建的对象是针对 Google Cloud 资源,认证将使用 Workload Identity 集成。虽然这些信息并不罕见,但由于实际执行花费了一些时间,所以我将其作为备忘录发布。(请注意,此文档中并不包含 Terraform 或 Actions 的基本说明,请谅解)。

工作负荷识别连接(Workload Identity)是一种将不同组织和系统间的工作负荷连接起来的方法。

工作负载身份互通是指利用OpenID Connect(OIDC)或AWS等Google Cloud以外的身份验证,以借用Google Cloud服务帐户权限的机制。

以前,如果要通过CI访问Google Cloud,需要将服务帐号的密钥注册到CI工具中。但是,通过使用Workload Identity协作,可以在CI端不需要注册诸如密钥等凭据信息的情况下使用服务帐号的权限。因此,可以减少密钥泄露风险和管理成本。

 

为了使用GitHub Actions实现与Workload Identity的集成,需要准备Workload Identity池和提供程序。

以下是準備的步驟,但這次我們將使用Terraform進行創建。在網站中,已經記錄了使用管理控制台和gcloud命令的步驟,因此建議您一開始可以在手動構建時同時查看這些步驟。

通过 ID 链接来获取有效期较短的认证信息。IAM 文档 | Google Cloud。

我会在下面放置用于构建的 Terraform 代码。

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

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

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的工作流,并尝试使用服务帐号权限执行Terraform。

以下是一个Workflow的示例。
参考资料如下所示,其他细节请在代码中注明。

在前一节中已经提到了与前半部分的认证相关的内容,即通过ID协作获取有效期较短的认证信息的“获取认证信息的行动”部分中提供了示例说明。

Terraform的执行部分是参考以下内容而来。在PR的评论等方面变得更易读和舒适。

 

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文件并创建Pull Request时,Actions会被执行,Terraform的计划结果将被发布在PR页面上。

pr-comment.png
广告
将在 10 秒后关闭
bannerAds