2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GitHub ActionsとTerraformでGCP管理を始める

Posted at

やること

GitHub Actions + Terraform + GCP (OIDC)

ステップ

1. (GCP) Service Account 作成

GitHub Actions用のService Accountを作成 + Editor Roleをつける。(今回Console上で)

変数をセット

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

workload-identity-poolsに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"

Service Account にPolicyをバインドする

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}"

ProviderをDescribeしてworkload_identity_providerを確認

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) Cloud Storage Bucket作成

Regionasia-northeast1 (Tokyo)を選び、残りはすべてDefaultでBucketを作成する

3. terraformのコード準備

今回は、terraformのコードをgcpというDirの下に置いておく (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 automatically creates or updates the dependency lock file each time you run the terraform init command. You should include this file in your version control repository

4. GitHub Actions作成

上で設定したService Accountから必要な情報

  • workload_identity_provider: projects/<id>/locations/global/workloadIdentityPools/<pool_name>/providers/<provider_name>
  • service_account: <service_account_name>@<project_name>.iam.gserviceaccount.com

今回作るGitHub Actions:

  1. PRでPlanしてPRにコメントで結果を書く
  2. mainブランチにマージされたら,Apply
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作成&Merge

動作確認:

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

注意点&課題

1. Terraform Google Provider Version

v3.61.0 or later (The provider now supports Workload Identity Federation.)
OIDCを使うには、Provider のVersionをv3.61.0以降にする必要がある

2. lock問題

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.

3. setup-terraform#usageのexampleだとplanで失敗してもGitHub Actionsの結果はSuccess

以下のステップを追加

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

4. Enable API

APIをEnableしていないと以下のようなErrorが出る

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

参考

2
5
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
2
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?