🌟

GCP+Terraform+GitHubActionsのテンプレートを作ってみた

2023/01/22に公開約7,600字

何を書くか

GCPのインフラ環境構成管理をTerraformで行い、GitHub Actions上でCI/CDを行うテンプレートレポジトリ、その構築過程、また最初にやっておくべき設定を書きます[1]。この技術構成であれば多くのプロジェクトでコピーして使用できるものを意識していますが、もし使う場合過不足はよしなに修正してください。

サンプルレポジトリ

https://github.com/shunsuke-tsumori/terraform_gcp_template_2023

構成要素

  • ディレクトリ構成
    • dev/stg/prdで環境を分けるためのディレクトリ
    • モジュール用のディレクトリ
  • GitHub ActionsからのCI/CD
    • tfsecによるセキュリティの静的解析
    • Workload IdentityによるGCPへの認可
    • terraformコマンド実行
  • renovateによる依存ライブラリの自動更新
  • tfstateをGCSバックエンドに保存
  • git pre-commit hookでコミット前に必要処理を実施(今回はterraform fmtのみ)
  • git-secretsにより機密情報のコミット防止(レポジトリ上では見えない要素)

ディレクトリ構成

方針

  • dev/stg/prdで環境(GCPプロジェクト)を分けて構築することを想定
  • モジュールを分け、各モジュールは環境に応じた変数(例:マシンサイズ)を持つものとする

上記の方針でまずはざっくり以下のようなディレクトリを作成します。

.
├── environments
│   ├── dev
│   ├── prd
│   └── stg
└── modules
    ├── module1
    └── module2

environments

dev/stg/prdの各環境に対して以下のファイルを作成します(サンプル)。

  • locals.tf: 各環境のローカル変数を定義
  • variables.tf: 各環境の.tfvarsや環境変数等で外部から注入する変数を定義
  • provider.tf: 各環境のプロバイダを定義
  • main.tf: 各環境のバックエンドとモジュールを定義

modules

各モジュールに対して以下のファイルを作成します(サンプル[2]

  • variables.tf: 各環境で異なる変数や依存する他のモジュールから注入する変数(モジュール構築時に動的に生成される変数等)を定義する
  • outputs.tf: 依存されるモジュールへ渡す変数を定義する
  • main.tf: モジュールを定義する(必要に応じて分割しても良い)

GitHub Actions からの CI/CD

方針

GitHub Actions workflowsを利用して、CIとCDで以下の内容を実行します。

  • CI (環境別にしない)
    • tfsec
    • terraform validate
    • terraform plan
  • CD (環境別に作成)
    • terraform apply

また、GitHub ActionsからGCPにアクセスするために、Workload Identity連携を用いて認可を行います。

CI

以下に.github/workflows/ci.yml(CI用のワークフローファイル)の例を書きます。

.github/workflows/ci.yml
name: ci

# 1
on:
  workflow_dispatch:
  pull_request:
    branches:
      - main

jobs:
  ci:
    name: ci
    runs-on: ubuntu-latest
    # 3-1
    permissions:
      contents: 'read'
      id-token: 'write'
    steps:
      - uses: actions/checkout@v3
      
      # 2
      - name: Terraform security scan
        uses: triat/terraform-security-scan@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      # 3-2
      - name: auth
        id: 'google-cloud-auth'
        uses: google-github-actions/auth@v1
        with:
          token_format: 'access_token'
          workload_identity_provider: '[workload identity providerのID]'
          service_account: '[service_account]@[project_id].iam.gserviceaccount.com'

      # 4-1
      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.3.7

      # 4-2
      - name: terraform init
        run: terraform init
        working-directory: ./environments/dev

      # 4-3
      - name: terraform validate
        run: terraform validate -no-color
        working-directory: ./environments/dev

      # 4-4
      - name: terraform plan
        run: terraform plan
        working-directory: ./environments/dev

1. CIワークフローをトリガーするイベント

workflow_dispatch(手動)での実行、及びmainブランチへのPRの際に実行されるように設定しています。

ドキュメント

2. tfsecによるセキュリティ静的解析

tfsecというTerraformのセキュリティ静的スキャンツールがあり、これをワークフローで使うtriat/terraform-security-scanというアクションがあります(後発で公式からaquasecurity/tfsec-pr-commenter-actionというアクションも出ています)。

セキュリティリスクが検出されるとワークフローがエラー終了となるので、レポートを見て対応や受容(tfsec:ignore)を行います。

3. Workload Identity連携により認可

GCP外部からGCPにアクセスする手段として、サービスアカウントキーを使うのではなく、Workload Identity連携というものを使うことがセキュリティ上の観点で推奨されている。Workload Identity連携の設定方法についてはこちらなどをご参照ください。

ワークフローでこれを用いて認可を行うには、google-github-actions/authというアクションを使えば良いです(3-2)。これを使うために、permissionsの設定が必要です(3-1)。

4. terraformコマンド実行

まずhashicorp/setup-terraformアクションでTerraformのセットアップを行います(4-1)。次にterraform init(4-2)、terraform validate(4-3)、terraform plan(4-4)を実行し、更新差分の確認をします。

CD

各環境に対してワークフローを作成します。以下で、dev環境でCDを行うワークフローの例を書きます。

.github/workflows/dev_cd.yml
name: dev_cd

# 1
on:
  workflow_dispatch:
  push:
    branches:
    - "main"

jobs:
  dev_cd:
    name: dev_cd
    runs-on: ubuntu-latest
    permissions:
      contents: 'read'
      id-token: 'write'
    steps:
      - uses: actions/checkout@v3
      
      # 2
      - name: auth
        id: 'google-cloud-auth'
        uses: google-github-actions/auth@v1
        with:
          token_format: 'access_token'
          workload_identity_provider: '[workload identity providerのID]'
          service_account: '[service_account]@[project_id].iam.gserviceaccount.com'

      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.3.7

      - name: terraform init
        run: terraform init
        working-directory: ./environments/dev

      # 3
      - name: terraform apply
        run: terraform apply -auto-approve
        working-directory: ./environments/dev

1. CDワークフローをトリガーするイベント

workflow_dispatch(手動)及びmainブランチへのpush(通常PRがマージされたこと)をトリガーにしています。なお、prd環境ではworkflow_dispatchのトリガーを削除した方が良いです。

2. Workload Identity連携により認可

CIと同じくWorkload Identity連携による認可を行います。

3. terraform plan

CIと同様にTerraformのセットアップをした後、terraform planによってデプロイを実行します。

renovateによる依存ライブラリの自動更新

renovateという依存ライブラリを自動で検出し、依存ライブラリに更新があった際にPRを出してくれる等の機能を持つツールを使うことにします[3]

このツールの導入は簡単で、こちらのページからインストールすればOKです。インストール後、renovateの設定ファイルであるrenovate.jsonファイルを追加するPRが自動で作成されます。これをマージ後、必要に応じて設定を追加します(ドキュメント)。

tfstateをGCSバックエンドに保存

環境毎に異なるtfstateファイルを、バックエンドとしてGCSに格納します[4]。以下は、environments/dev/main.tfの中に書くバックエンド定義の例です。

environments/dev/main.tf(一部)
terraform {
  backend "gcs" {
    bucket = "dev_gcp_terraform_gcp_template_2023_tfstate"
    prefix = "terraform/state"
  }
}

git pre-commit hookでコミット前に必要処理を実施

terraform fmtのようにコミット前に実行すべきだがつい忘れてしまう操作を、pre-commit hookを使って自動で実行するようにできます。

Macなら以下のコマンドをgitレポジトリのルートで実行するだけで簡単にインストールできます。

brew install pre-commit
pre-commit install

インストール後、.pre-commit-config.yamlという設定ファイルを追加することによって、コミット前hookを定義できます。terraform fmtを実行する場合の例。

.pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.77.0
    hooks:
      - id: terraform_fmt
        args:
          - --args=-write=true

git-secretsにより機密情報のコミット防止

サービスアカウントJSONキーなど、機密情報のコミットを防止するために、git-secretsというツールなどを使うべきでしょう。

Macなら以下のコマンドをgitレポジトリのルートで実行してインストールできます。

brew install git-secrets
git secrets --install

そして必要に応じて秘密情報を示す正規表現を登録しておきます。

git secrets --add '^private_key'

終わりに

Terraform for GCPのCI/CDをGitHub Actionsから行うテンプレートとその作成過程、最初にやっておくべき設定について記載しました。テンプレートは上記レポジトリにあります。何か誤記などありましたら、お気軽に教えていただけると幸いです。

脚注
  1. Terraform for GCP自体の説明等は含みません。 ↩︎

  2. firewallルールを1つだけ持つモジュールnetworkとGCEインスタンスを1つだけ持つモジュールcomputeがあるだけの最小構成で、computeモジュールがnetworkモジュールに依存しています。 ↩︎

  3. 同様のツールにdependabotというものもあります。比較はこちらなどが参考になると思います。 ↩︎

  4. tfstateファイルはTerraformが管理するリソースの状態を表すファイルです。このファイルは複数人で開発を行う場合、バックエンドと呼ばれるストレージに格納し共有することが多いです。 ↩︎

Discussion

ログインするとコメントできます