やること
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作成
Region
にasia-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:
- PRでPlanしてPRにコメントで結果を書く
-
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
参考
- 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で出来てるのでこちらのほうがいい!