0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraform で Dependabot を使うときのめんどくさい点 5 つ

0
Last updated at Posted at 2026-11-02

dependabot.yml の Terraform の entry は、npm や uv の entry と見た目がまったく同じだ。

- package-ecosystem: terraform
  directory: /terraform/env
  schedule:
    interval: weekly

ところが挙動は全然違う。GCP のモノレポ(デプロイ対象 4 サービス + 共有インフラ、Terraform ディレクトリは 8 個)に Dependabot を入れたとき、Terraform まわりだけで他の全 ecosystem を合わせたより時間を食った。実際に踏んだものと、その対処を書く。

1. directory は glob が効かない。モノレポだと同じ block が N 個並ぶ

単数形の directory キーはリテラルなパスを 1 つしか取らない。うちのリポジトリは Terraform の所有権を分けていて、Cloud Run 本体と observability は各サービス配下の terraform/、SA / IAM / 共有 VPC / Cloud SQL はリポジトリ直下の terraform/ が持つ。結果、対象は 8 ディレクトリになる。

/terraform/env
/terraform/common
/app-a/terraform
/services/svc-a/terraform
/services/svc-b/terraform
/services/svc-c/terraform
/app-b/terraform
/app-b/terraform/sub

ほぼ同一の 15 行 entry が 8 個。schedule も PR 上限もラベルも group もそれぞれコピーされる。更新頻度を変えたければ 8 箇所、サービスを増やしたら 9 個目を書き忘れないようにする、という話になる。

対処は複数形の directories キー。こっちは glob が効く。公式ドキュメントにも「単数形の directory ではサポートしない」と明記されている。

- package-ecosystem: terraform
  directories:
    - "/terraform/*"
    - "/*/terraform"
    - "/services/*/terraform"
  schedule:
    interval: weekly

効果が一番大きいのはこれなのだが、世の中のサンプルも自動生成される starter config もだいたい単数形なので気づきにくい。

2. google と google-beta が別 PR に割れる。しかも PR 上限のせいで必ず片方だけ来る

GCP の provider は 2 パッケージに分かれていて、両方を常に同じ version に揃える必要がある。google が 8 系で google-beta が 7 系、という状態になると、両方に存在するリソースで terraform init が落ち始める。

Dependabot はこの 2 つを独立した依存として扱う。そこに Terraform では定番の open-pull-requests-limit: 1 が乗ると、ペアの片方だけを上げる PR が 1 本届く。もう片方が取り残されていることは PR のどこにも書かれていない。うちの 1 週間分がこれ。

#1206  bump hashicorp/google      7.44.0 -> 8.8.1  in /app-a/terraform
#1207  bump hashicorp/google-beta 7.45.0 -> 8.8.1  in /services/svc-c/terraform
#1203  bump hashicorp/google      7.44.0 -> 8.8.1  in /services/svc-b/terraform

3 ディレクトリに 3 本、それぞれ担当している側が違う。どれか 1 本だけマージすれば、そのディレクトリは片肺状態になる。タイトルは普通のバージョン上げにしか見えない。

対処は catch-all な group をディレクトリごとに 1 つ置いて、そのディレクトリの provider をまとめて動かすこと。

groups:
  terraform-providers:
    patterns:
      - "*"

意図的に書いていないのが update-types だ。他の ecosystem では catch-all group を minor / patch に絞って、major は個別 PR として単独でレビューできるようにしている。Terraform provider はこれが逆になる。major こそ相方と一緒に動かないと困るので、group に major も飲ませる必要がある。

3. PR の中身は「lock file を git 管理しているか」で変わる

.terraform.lock.hcl を commit しているディレクトリと gitignore しているディレクトリが混在していると、同じ ecosystem から 3 種類の PR が飛んでくる。

ディレクトリの状態 PR が実際に変えるもの
lock を commit、制約が狭い(~> 7.0) required_providers の制約 と lock file
lock が gitignore(/terraform/env) 制約だけ。1 行 diff
lock を commit、制約が既に緩い(>= 5.0) lock file だけ。制約は触らない

危ないのは 3 つ目。うちの svc-a の Terraform は >= 5.0 なので、制約は最初から 8 系を許容している。Dependabot は versions.tf に一切触らず、lock だけを黙って新しい major に書き換える。制約の変更を探してレビューしている人の目には何も引っかからず、diff は誰も読まない zh: ハッシュ 50 行になる。

そしてこれは後述の「ディレクトリ横断グルーピング」も静かに壊す。公式ドキュメントによると、同じ依存に対してディレクトリ間で互換性のない version 制約があると、Dependabot は PR を分けるほうにフォールバックする。うちは ~> 8.8 が 5 つ、~> 8.6 が 1 つ、>= 5.0 が 1 つなので、見事にその条件を踏んでいる。

4. CI が何も検証していないのに、緑に見える

実害が一番大きいのはこれ。Terraform provider の major こそ terraform plan を走らせたい変更なのに、plan こそが Dependabot PR では走らせられない job だ。

Dependabot 起因の workflow run には、クラウドへの OIDC 認証に必要な id-token: write が付かない。毎週 job が落ちるのを放置するわけにもいかないので、普通は job を分割して actor でゲートする。

jobs:
  build:
    # 依存インストール・lint・test・docker build。クラウド認証は不要
  plan:
    needs: build
    if: github.actor != 'dependabot[bot]'
    permissions:
      contents: read
      id-token: write

設計としては妥当だ。ただし結果として、Terraform の Dependabot PR で gh pr checks を叩くとこうなる。

add-to-project      skipping
plan                skipping
request-reviewer    skipping

全部 skipping。/terraform/env にいたってはもっとひどくて、このパスはどの workflow の paths: にも一致しないので check が 0 件になる。provider の major 上げが、赤いものが何もない状態で置かれている。何も走っていないから赤くなりようがない。

正直なところ、Terraform に対する Dependabot は検証の仕組みではなく通知の仕組みだと割り切ったほうがいい。緑の Terraform PR は「新しい version が出た」であって「この version で動く」ではない。検証は人間が実 state に対して plan を回したときにしか起きないし、これは設定をどういじっても変わらない。

5. private module が 1 つ届かないだけで、update job 全体が落ちる

共有 Terraform は別 org の private repo を SSH source で参照している。

module "something" {
  source = "git::ssh://git@github.com/other-org/terraform-modules//path?ref=v1.2.3"
}

Dependabot はここに認証できない。問題は失敗の仕方で、「その module だけスキップして残りを更新」ではなく、そのディレクトリの update job が丸ごと死ぬ(git_dependencies_not_reachable)。毎週落ちるので、同じディレクトリの provider も永遠にチェックされない。別 org かつ SSH source なので、Dependabot に渡せる credential も存在しない。

回避策は除外することだが、この構文が素直に推測できない。

ignore:
  - dependency-name: "*::github::other-org/*"

*::github::owner/* というのが Terraform 固有の dependency-name の形。単一 repo ではなく org 全体に当たる pattern にしておくと、同じ org の module を後から追加で参照しても問題が再発しない。

おまけ: Dependabot PR の close は見た目より重い

Dependabot PR を close すると、こう返ってくる。

OK, I won't notify you again about this release, but will get in touch when a new version is available.

これはその version に対する恒久的な決定だ。さらに Dependabot はブランチも削除する。ブランチが消えたあとに @dependabot reopen と書いても何も起きない。コメントはそのまま無視される。まとめて close したあとに「やっぱり一部は要るな」となっても、ブランチが既に消えているものは GitHub API 経由でも復旧できず、手で作り直すしかない。

UI が軽く見えるわりに戻せないので、特に一括 close のときは慎重にやったほうがいい。

最終的にどうしたか

  • Terraform のディレクトリごとに patterns: ["*"] の group を 1 つ、update-types は付けない。google と google-beta が必ず一緒に動く
  • 到達できない private module source を org 単位で ignore
  • plan を build から分離して github.actor != 'dependabot[bot]' でゲート
  • 「Terraform の PR には検証シグナルが無い」ことを明文化して、npm / uv の PR とはレビューの仕方を変える

落ち着いてみると、Dependabot の 7 本の PR は、8 ディレクトリ 16 ファイルを触る手作りの PR 1 本に置き換わった。モノレポで GCP provider の major を上げるという作業の、正直な単位はこれくらいだと思う。

残っているのは統合のほうだ。GitHub は 2026 年 2 月にディレクトリ横断のグルーピングを出している。directories にパスを並べて group に group-by: dependency-name を付けると、ディレクトリごとに 1 本ではなく、依存ごとに全ディレクトリ分で 1 本の PR になる。

- package-ecosystem: terraform
  directories:
    - "/terraform/*"
    - "/*/terraform"
    - "/services/*/terraform"
  groups:
    gcp-providers:
      group-by: dependency-name
      patterns:
        - "hashicorp/google*"

この問題が欲しがっている形はまさにこれ。ただし 3 番目の制約互換性ルールが効くので、制約がばらついているディレクトリはまた別 PR に分かれてしまう。先にやるべきは地味な片付け、つまり全ディレクトリの制約を揃えることだ。

まとめ

問題 対処
ディレクトリごとに entry が必要 複数形の directories + glob
google / google-beta が別 PR に割れる ディレクトリ単位の group、patterns: ["*"]、update-types なし
lock だけ書き換わる PR が major を隠す 制約を揃える。.tf の diff だけでなく lock も読む
Dependabot PR では plan が動かない build / plan を分離して actor でゲート。緑を「検証済み」と読まない
private module 1 つで job が全滅 ignore: dependency-name: "*::github::org/*"
close が恒久的 一括 close は慎重に。ブランチが消えると reopen は効かない

どれもバグではない。Dependabot が Terraform provider を普通のパッケージ依存として扱っている以上、必然的にこうなる。実際には provider の major はインフラの変更であって、その唯一の本当のテストは実 state に対する plan だ、というだけの話だと思う。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?