概要

terraformでGoogle Cloudのリソースを管理し、GitHub ActionsによるCI/CDを構築するまでの手順をまとめます。 更に以下の要件を実現します。

  • Credentialを用いず、Workload Identity連携でサービスアカウントの認証をする
  • Pull Requestをオープンした際、terraform plan結果のを自動でコメントする

1. gcloud CLIのセットアップ

1
$ gcloud auth application-default login

作業するGoogle Cloudプロジェクトの権限を持つアカウントでログインする

2. GCSバケットの作成

tfstateファイルの置き場となるバケットを作成する

3. tfファイルの配置

以下のリソースを定義したtfファイルを配置する

  • 手順2で作成したバケットのimport
  • GitHub Actionsが用いるサービスアカウント
  • GitHub Workload Identity連携の有効化
1
2
3
4
5
6
7
8
.
├── README.md
├── locals.tf
├── main.tf
├── modules
   └── workload_identity
       └── main.tf
└── provider.tf

locals.tf

1
2
3
4
5
# 定数の定義
locals {
  project_id = "your-project-name" # UPDATE HERE
  region     = "asia-northeast1"
}

provider.tf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
provider "google" {
  project = local.project_id
  region  = local.region
}

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "5.39.0"
    }
  }
  backend "gcs" {
    bucket = "tfstate-bucket-name" # UPDATE HERE
  }
}

# tfstate用bucketのimport

resource "google_storage_bucket" "tfstate" {
  name     = "tfstate-bucket-name" # UPDATE HERE
  location = "ASIA"
}

import {
  id = "${local.project_id}/tfstate-bucket-name" # UPDATE HERE
  to = google_storage_bucket.tfstate
}

modules/workload_identity/main.tf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
variable "project_id" {
  type = string
}

variable "github_repo_name" {
  type = string
}

data "google_project" "project" {
  project_id = var.project_id
}

# APIの有効化

resource "google_project_service" "iam" {
  project                    = data.google_project.project.project_id
  service                    = "iam.googleapis.com"
}

resource "google_project_service" "cloudresourcemanager" {
  project                    = data.google_project.project.project_id
  service                    = "cloudresourcemanager.googleapis.com"
}

# サービスアカウントの作成と権限付与

resource "google_service_account" "terraform" {
  account_id   = "terraform"
  display_name = "ServiceAccount for terraform apply"
}

resource "google_project_iam_member" "project_owner" {
  project = data.google_project.project.project_id
  role    = "roles/owner"
  member  = "serviceAccount:${google_service_account.terraform.email}"
}

# GitHub Workload Identity連携の有効化

resource "google_iam_workload_identity_pool" "github" {
  provider                  = google
  project                   = data.google_project.project.project_id
  workload_identity_pool_id = "github"
  display_name              = "github"
  description               = "GitHub Actions"
}

resource "google_iam_workload_identity_pool_provider" "github" {
  provider                           = google
  project                            = data.google_project.project.project_id
  workload_identity_pool_id          = google_iam_workload_identity_pool.github.workload_identity_pool_id
  workload_identity_pool_provider_id = "github"
  display_name                       = "github"
  description                        = "GitHub Actions"

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.repository" = "assertion.repository"
  }

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

resource "google_service_account_iam_member" "terraform" {
  service_account_id = google_service_account.terraform.id
  role               = "roles/iam.workloadIdentityUser"
  member             = "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github.name}/attribute.repository/${var.github_repo_name}"
}

main.tf

1
2
3
4
5
module "workload_identity" {
  source           = "./modules/workload_identity"
  project_id       = local.project_id
  github_repo_name = "your-org-name/repo-name" # UPDATE HERE
}

4. terraform apply

上記のリソースを手動でterraform applyする

1
2
3
$ terraform init
$ terraform plan
$ terraform apply

5. GitHub Actionsワークフローの追加


.github/workflows/terraform.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
name: 'Terraform'

on:
  push:
    branches:
      - main
  pull_request:

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  terraform:
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
    steps:
    - uses: actions/checkout@v3
    - uses: 'google-github-actions/auth@v1'
      with:
        workload_identity_provider: 'projects/{Google CloudプロジェクトID}/locations/global/workloadIdentityPools/github/providers/github'
        service_account: 'terraform@{Google Cloudプロジェクト名}.iam.gserviceaccount.com'
    - uses: hashicorp/setup-terraform@v2
    - run: terraform fmt -check -recursive
      continue-on-error: true
    - run: terraform init
    - run: terraform validate
    - run: terraform plan -no-color
      id: plan
      continue-on-error: true
    - uses: peter-evans/find-comment@v3
      if: github.event_name == 'pull_request'
      id: fc
      with:
        issue-number: ${{ github.event.pull_request.number }}
        comment-author: 'github-actions[bot]'
        body-includes: Production terraform plan
    - uses: peter-evans/create-or-update-comment@v4
      if: github.event_name == 'pull_request'
      with:
        issue-number: ${{ github.event.pull_request.number }}
        comment-id: ${{ steps.fc.outputs.comment-id }}
        edit-mode: replace
        body: |
          ```
          ${{ steps.plan.outputs.stdout }}
          ```          
    - run: terraform apply -auto-approve -input=false
      if: github.ref_name == 'main'

以上の手順により、

  • Pull Request作成時にterraform planが実行され、plan結果がコメントされる
  • マージ時にterraform applyが実行される
  • plan, applyに用いられるサービスアカウントはWorkload Identity連携で認証される

という環境が完成します。

以上