利用 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页面上。
