🚧

TerraformのPR失敗をpre-commitで手前に寄せた:fmt/tflint/gitleaksの品質ゲート

に公開

対象読者

  • Terraform を触っていて、PR を出してから fmtlint で落ちる手戻りがつらい人
  • 「CI はあるがローカルが弱い」と言われがちな構成を、仕組みで改善したい
  • 秘密情報(トークン/キー)の混入を、コミット前に止めたい人

先に結論

  • pre-commit を使うと、git commit の直前に **Terraform の整形(fmt)・lint(tflint)・秘密検知(gitleaks)**を自動実行できます
  • ローカルに品質ゲートを置くことで、PR を赤くする前に落とせるようになります
  • ただし gitleaks は 作業ツリー全体のスキャンと staged 差分のスキャンで挙動が違うため、最終防衛線は CI で gitleaks git もしくは GitHub Action を回すのが堅いです(後述)

なぜローカル品質ゲートが必要だったか

DevOps の原則に「Shift Left(左にシフトせよ)」があります。問題を「できるだけ早いフェーズで検出する」ほど、修正コストが下がるという考え方です。CI でのチェックは PR を出した後、つまり「右側」にある品質ゲートです。

CI(GitHub Actions)で terraform fmt -checktflint を回すのは正しい一方で、次の痛みがありました。

  • PR を出してから落ちるので、手戻りが遅い
  • 「ローカルでは気づけたはずのミス」が CI を赤くしてしまう
  • 秘密情報の混入は、リモートに出る前に止めたい

そこで「コミットの直前」を関所にして、チェックをより左側に置くようにしました。

用語の整理(最小限)

  • Git hook: git commit など特定タイミングで自動実行されるスクリプト
  • pre-commit: hook を管理して、複数チェックをまとめて実行する仕組み
  • 品質ゲート: これを通らないと先に進めない関所(ここでは「コミットできない」)

やったこと(最終形)

追加したファイル

  • .pre-commit-config.yaml(pre-commit の設定)
  • .tflint.hcl(tflint の設定)
  • .gitleaks.toml(gitleaks の設定。既存なら修正点あり)

.pre-commit-config.yaml

ポイントは以下です。

  • terraform fmt自動修正-write=true)に寄せる
  • tflintgitleaks落ちたらコミットを止める
  • tflint.tflint.hcl を明示的に参照する
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.99.4
    hooks:
      - id: terraform_fmt
        args:
          - --args=-diff
          - --args=-write=true
      - id: terraform_tflint
        args:
          - --args=--config=__GIT_WORKING_DIR__/.tflint.hcl

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks

.tflint.hcl

AWS の ruleset を入れた上で、今回は ローカルモジュール運用ではノイズになったルールを無効化しました。

このリポジトリでは、root module から呼ぶ child module にも terraform_required_version / terraform_required_providers が出ていました。すべてのローカルモジュールに同じ定義を強制すると、今回の目的である「コミット前に直すべきものを素早く見つける」にはノイズが増えます。

ただし、Terraform 公式では各 module が provider requirements を宣言する前提です。再利用する shared module では、required_providers を明示するほうが安全です。今回は ローカルモジュール運用でのノイズ削減を優先して、この2ルールを無効化しました。

plugin "aws" {
  enabled = true
  version = "0.40.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

config {
  call_module_type = "local"
}

rule "terraform_required_version" {
  enabled = false
}

rule "terraform_required_providers" {
  enabled = false
}

.gitleaks.toml(詰まった点)

gitleaks v8.30.1 で確認しています。v8.25.0 以降、global allowlist の書き方が [allowlist] から [[allowlists]] に変わっています。最初に古い書き方を置くとエラーになります。

  • NG(例): [allowlists] files = [...]
  • OK: [[allowlists]] paths = [...] のように 配列で定義し、paths/regexes/commits/stopwords のいずれかを必ず指定する
[[allowlists]]
description = "dist成果物は検査対象外"
paths = [
  '''client/dist/assets/.*''',
]

使い方

初回だけやること

事前に、少なくとも pre-committerraformtflint がローカルで実行できる状態にしておきます。

pre-commit --version
terraform version
tflint --version

pre-commit install で hook を登録します。

pre-commit install

これで .git/hooks/pre-commit が作られ、git commit 時に自動実行されます。

手動で全チェックしたいとき

pre-commit run --all-files

検証(正常系・異常系)

正常系

pre-commit run --all-files がすべて Passed になることを確認しました。

Terraform fmt............................................................Passed
Terraform validate with tflint............................................Passed
Detect hardcoded secrets..................................................Passed

異常系(ここが重要)

1. terraform fmt は「落とす」より「直す」

整形が崩れている .tf を用意すると、terraform fmt自動修正して Passed になります。

  • 良い点: 整形の議論が減る(人間が直さなくていい)
  • 注意点: 直されたファイルは再 git add が必要(コミットは一度止まる)

自動修正後は、以下のように再追加してコミットします。

# fmt に整形されたファイルを再追加してコミット
git add -u
git commit -m "..."

2. tflint はちゃんと止める

未使用の variable / local を意図的に作ると、terraform_unused_declarationsFailed になりコミットを止められます。

Terraform validate with tflint...........................................Failed
- hook id: terraform_tflint
- exit code: 2

Warning: [Fixable] variable "unused_for_test" is declared but not used
  on test.tf line 1:
   1: variable "unused_for_test" {

[Fixable] とあるものは tflint --fix で自動修正できます。そうでないものは手動で対応が必要です。

3. gitleaks は「全体スキャンでは落ちるが、staged 差分だと落ちない」ケースがあった

ここがいちばん詰まったポイントです。

  • gitleaks dir(作業ツリー上のファイルを直接スキャン)では leak を検知できる
  • しかし pre-commit hook 経由では gitleaks git --pre-commit --staged 相当の差分検査になり、スルーするケースがありました

このケースでは、作業ツリー上のファイルを直接読むスキャンと、staged 差分を対象にする pre-commit hook で検知結果が分かれました。差分スキャンだけに頼ると、検査対象や入力形式の違いで見逃しが残る可能性があります。

捨てた選択肢と理由

捨てた案: 「ローカルだけで secrets を完結させる」

  • ねらい: リモートに出る前に全部止める
  • 捨てた理由: staged 差分の検査だけだと、ローカルだけでは 見逃しが残る可能性がある

最終的な落とし所(恒久策)

Shift Left の文脈では、一段目(ローカル)で速く落とし、二段目(CI)で確実に落とすという多層防御が基本です。

  • 一段目(ローカル): fmt/tflint を強くして手戻りを最小化。gitleaks はベストエフォートで動かす
  • 二段目(CI): gitleaks git もしくは gitleaks/gitleaks-action を回して、ローカルで漏れた secrets を確実に止める

どちらかの層だけに頼る設計は、片方が機能しなかったときにノーガードになります。

学び

  • 品質ゲートは CI だけでなく、コミット前に置ける。手戻りの体感が変わる
  • terraform fmt は「落とす」より「直す」に寄せると運用が楽
  • secrets 検知は スキャン対象の差分を理解して設計しないと、安心を買い損ねる

今後の改善(今回のスコープ外)

  • GitHub Actions に gitleaks git もしくは gitleaks/gitleaks-action を追加して、リモート側でも確実に止める
  • checkov を追加して IaC のセキュリティルールを増やす(ただしローカル実行は重いので、CI 優先でもよい)
  • pre-commit の導入手順(PATH、初回セットアップ)を README に明記して、再現性を上げる

参考資料

(いずれも 2026年4月時点)

GitHubで編集を提案

Discussion