使用GitHub Actions和Terraform开始管理GCP

做的事情

GitHub Actions + Terraform + GCP(OIDC)在中国本土化后加以利用。

以下是对“ステップ”的一种中文本地化的释义:

步骤

1. 创建 (GCP) Service Account

在GitHub Actions中创建一个Service Account并授予Editor角色。(此次在控制台上进行操作。)

设定变量

export PROJECT_ID=<your project id>
export POOL_NAME=<pool name> # github-actions 
export PROVIDER_NAME=<provider name> # gha-provider
export SA_EMAIL=<service_account name>@<project_name>.iam.gserviceaccount.com
export GITHUB_REPO=<org>/<repo_name>

启用iamcredentials API

gcloud services enable iamcredentials.googleapis.com --project "${PROJECT_ID}"

将 workload-identity-pools 创建并将其 ID 存储在变量 WORKLOAD_IDENTITY_POOL_ID 中。

gcloud iam workload-identity-pools create "${POOL_NAME}" \
    --project="${PROJECT_ID}" --location="global" \
    --display-name="use from GitHub Actions"
export WORKLOAD_IDENTITY_POOL_ID=$( \
    gcloud iam workload-identity-pools describe "${POOL_NAME}" \
      --project="${PROJECT_ID}" --location="global" \
      --format="value(name)" \
  )

确认 WORKLOAD_IDENTITY_POOL_ID 的内容

echo $WORKLOAD_IDENTITY_POOL_ID

为GitHub创建OIDC providers的工作负载身份池。

gcloud iam workload-identity-pools providers create-oidc "${PROVIDER_NAME}" \
    --project="${PROJECT_ID}" --location="global" \
    --workload-identity-pool="${POOL_NAME}" \
    --display-name="use from GitHub Actions provider" \
    --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.actor=assertion.actor,attribute.aud=assertion.aud" \
    --issuer-uri="https://token.actions.githubusercontent.com"

将服务账号绑定到策略 (绑定策略到服务账号)

gcloud iam service-accounts add-iam-policy-binding "${SA_EMAIL}" \
    --project="${PROJECT_ID}" \
    --role="roles/iam.workloadIdentityUser" \
    --member="principalSet://iam.googleapis.com/${WORKLOAD_IDENTITY_POOL_ID}/attribute.repository/${GITHUB_REPO}"

描述提供者,并确认工作负载身份提供者。

gcloud iam workload-identity-pools providers describe "${PROVIDER_NAME}" \
    --project="${PROJECT_ID}" --location="global" \
    --workload-identity-pool="${POOL_NAME}" \
    --format="value(name)"

结果: workload_identity_provider(GitHub Actions配置需要记下来备忘)

projects/<id>/locations/global/workloadIdentityPools/<pool_name>/providers/<provider_name>

2. 创建(GCP)云存储存储桶

选择Region为asia-northeast1(东京),其余全部使用默认设置创建存储桶。

3. 准备terraform代码。

本次,我们将把Terraform的代码放在名为gcp的文件夹下(通过GitHub Actions的WORKING_DIR参数设置为gcp,或者确保在gcp文件夹内进行代码更改以使GitHub Actions正常运行)。

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 4.0" # OIDC https://github.com/hashicorp/terraform-provider-google/releases/tag/v3.61.0 or later
    }
  }

  backend "gcs" {
    bucket = "<bucket_name>"
    prefix = "state" # any prefix is ok.
  }
}
terraform init

请将.terraform.lock.hcl也安装好。

每次运行 terraform init 命令时,Terraform 会自动创建或更新依赖锁定文件。你应该将该文件包含在你的版本控制库中。

 

4. 创建GitHub Actions

从之前设置的服务帐户中获取所需信息。

workload_identity_provider: projects//locations/global/workloadIdentityPools//providers/

service_account: @.iam.gserviceaccount.com

我們這次要做的GitHub Actions是:

    1. 当有了计划并在公关中进行了评论后,即可在公关中写下结果。

当合并到主分支后,请应用。

name: gcp
on:
  pull_request:
    paths:
      - 'gcp/**'
      - '!gcp/**md'
      - '.github/workflows/gcp.yml'
    branches:
      - main
  push:
    paths:
      - 'gcp/**'
      - '!gcp/**md'
      - '.github/workflows/gcp.yml'
    branches:
      - main

env:
  TERRAFORM_VERSION: 1.1.8
  WORKING_DIR: gcp # Use `gcp` dir in this example.

jobs:
  terraform:
    # Add "id-token" with the intended permissions.
    permissions:
      contents: read
      id-token: write
      pull-requests: write
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ${{ env.WORKING_DIR }}
    steps:
      - uses: actions/checkout@v3

      - id: 'auth'
        name: 'Authenticate to Google Cloud'
        uses: google-github-actions/auth@v0.7.0
        with:
          create_credentials_file: 'true'
          workload_identity_provider: projects/<project_id>/locations/global/workloadIdentityPools/github-actions/providers/gha-provider
          service_account: <service_account_name>@<project_name>.iam.gserviceaccount.com

      - uses: hashicorp/setup-terraform@v1

      - name: Terraform fmt
        id: fmt
        run: terraform fmt -check
        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 -lock=false # TODO
        continue-on-error: true

      - 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
            })
      - if: steps.plan.outcome == 'failure' || steps.fmt.outcome == 'failure'
        name: make it fail when plan failed
        run: exit 1

      - name: Terraform Apply
        id: apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -input=false -lock=false # TODO

5. PR策划和合并

动作确认:

    • PRでコメントにPlan結果が書かれる

 

    MergeでApplyが走る

注意事项和问题

谷歌提供商的版本进行基础设施构建

v3.61.0 或更高版本(供应商现在支持工作负载身份联合)。
要使用 OIDC,请确保 Provider 的版本为 v3.61.0 或更高。

2. 锁的问题

如果生成了lock文件,将无法执行Plan/Apply。(暂定的解决方案是-lock=false,但不建议使用。最坏的情况下可以使用gsutil来删除lock文件。)

Terraform acquires a state lock to protect the state from being written by multiple users at the same time. Please resolve the issue above and try again. For most commands, you can disable locking with the "-lock=false" flag, but this is not recommended.

在GitHub Actions中,即使在setup-terraform#usage的示例中计划失败,结果仍然会显示为成功。

添加以下步骤

  - if: steps.plan.outcome == 'failure'
    name: make it fail when plan failed
    run: exit 1

4. 启用API API)

如果不启用API,将会出现以下错误。

Error: Error creating Address: googleapi: Error 403: Compute Engine API has not been used in project xxxxxx before or it is disabled. Enable it by visiting

请参阅

    • https://github.com/hashicorp/setup-terraform

 

    • https://engineer.retty.me/entry/2021/09/22/123000

 

    • https://scrapbox.io/pokutuna/GCP_API_%E3%82%92_Terraform_%E3%81%8B%E3%82%89%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B

https://qiita.com/mintak21/items/5232a089f3a39a71b155 <- あとから見つけたけど、Service Accountの作成も含めてTerraformで出来てるのでこちらのほうがいい!

广告
将在 10 秒后关闭
bannerAds