terraform-docsは、Terraformモジュールのvariableとoutputを読み取ってREADMEの表を書き出すCLIです。公式サイトの説明は「a utility to generate documentation from Terraform modules in various output formats」の一文で、やることはドキュメントの生成だけに絞られています。この記事では最新版のv0.24.0(2026年5月10日リリース)を実際に手元で動かし、生成される表、READMEへの差し込み、設定ファイルの効き方、CIで「READMEが古い」を落とす方法までを実測値で示します。HashiCorpの公式ドキュメントサイト(developer.hashicorp.com)を探していた方は、別物なのでTerraformの最新バージョンとアップグレード手順の記事が近いはずです。
まとめ
- 基本の1行は
terraform-docs markdown table --output-file README.md --output-mode inject .。READMEに<!-- BEGIN_TF_DOCS -->と<!-- END_TF_DOCS -->を置いておけば、その間だけが書き換わります。 - CIでは生成ではなく
--output-checkを回します。READMEが古いと終了コード1になるので、ブランチ保護で必須チェックにすれば更新漏れのままマージされません。 - 入手経路でバージョンがずれます。HomebrewとScoopは0.24.0、Chocolateyは0.20.0(2025年4月7日が最新公開)、公式GitHub Actionのv1.4.1も同梱は0.20.0です。
- モジュール直下に
.terraform-docs.ymlがあると、どのサブコマンドで実行しても設定のoutput.fileに書き込まれます。terraform-docs tfvars hcl .でREADMEがtfvarsに置き換わる事故が起きます。 - ライセンスはMITです。Apache 2.0と書かれた解説が流通していますが、v0.24.0のLICENSEはMIT Licenseから始まります。
terraform-docsが生成するもの
入力はモジュールのHCLそのものです。Terraform本体のバイナリもstateも要りません。次はvariable4個・output1個・子モジュール1個・リソース1個のモジュールに terraform-docs markdown table . を実行した出力から、Requirements・Resources・Inputsの3セクションを抜き出したものです。行の中身は加工していません。
## Requirements
| Name | Version |
| ---- | ------- |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 6.0 |
## Resources
| Name | Type |
| ---- | ---- |
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
## Inputs
| Name | Description | Type | Default | Required |
| ---- | ----------- | ---- | ------- | :------: |
| <a name="input_bucket_name"></a> [bucket\_name](#input\_bucket\_name) | 作成する S3 バケット名 | `string` | n/a | yes |
| <a name="input_logging_enabled"></a> [logging\_enabled](#input\_logging\_enabled) | 有効化するとアクセスログを別バケットへ出力します | `bool` | `false` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | 全リソースへ付与するタグ | `map(string)` | <pre>{<br/> "Env": "dev"<br/>}</pre> | no |
表として出るのはRequirements、Providers、Modules、Resources、Inputs、Outputsの6つで、ほかにヘッダーとフッターが出力されます。--hide と --show はこれらに加えて data-sources も受け付け、指定するとResourcesの表からデータソースの行だけが消えます。Resourcesの各行はTerraform Registryのリソースページへのリンクになり、Inputsのdefaultはjsonencodeされた実値が入ります。上の logging_enabled にはdescriptionを書いていません。それでも説明文が入っているのは --read-comments が既定で有効で、変数ブロックの直前の # コメントをdescriptionとして拾うためです。コメントの書き方がそのまま公開ドキュメントに出るので、Terraformのコーディング規約とコメントの統一ルールを決めてから導入すると手戻りが減ります。
対応Terraformバージョンと.tofuの版境界
公式の互換マトリクスでは、terraform-docs 0.13以降がTerraform 0.15以上に対応します。0.8以上0.13未満はTerraform 0.12以上0.15未満、0.8未満はTerraform 0.12未満です。Terraform 0.15未満が残っている環境では、このマトリクスに合う古いterraform-docsを選ぶ必要があります。
OpenTofuを使っている場合は、terraform-docs側のバージョンが効きます。v0.20.0のリリースノートはOpenTofu対応を予備的なものと位置づけ、「we are still not able to read and parse all the .tofu files to extract all the other information, such as inputs, outputs, providers, etc.」と明記していました。variables.tofu と outputs.tofu だけを置いたディレクトリで各版を実行したところ、v0.20.0の出力は「No inputs.」「No outputs.」で、v0.21.0とv0.24.0は表を生成しました。リリースノートに記載のないまま挙動が変わっているので、OpenTofu環境では0.21.0以上を使ってください。
インストール経路ごとの提供バージョン差
どの経路でも実行ファイルは1つですが、追随の速さが揃っていません。2026年9月18日時点で各リポジトリが配っているバージョンは次のとおりです。
| 経路 | コマンド | 提供中の版 |
|---|---|---|
| Homebrew | brew install terraform-docs | 0.24.0 |
| Scoop | scoop install terraform-docs | 0.24.0 |
| Chocolatey | choco install terraform-docs | 0.20.0 |
| Go | go install …@v0.24.0 | 指定した版 |
| Snap(有志) | snap install terraform-docs | 0.24.0 |
Chocolateyの最新公開は0.20.0で、公開日は2025年4月7日です。4マイナー分の差があるため、Windowsで入れるならScoopを選びます。Scoopはバケットの追加が先に要ります。Snapは版こそ0.24.0ですが、公開者はterraform-docsプロジェクトではなく個人アカウント(表示名Andrii、検証状態はunproven)で、公式サイトのインストール手順にもSnapの節はありません。社内の配布手段に選ぶなら、公式が案内するHomebrew・Scoop・Go・バイナリ直取得のいずれかにしてください。
# Windows(Scoop)
scoop bucket add terraform-docs https://github.com/terraform-docs/scoop-bucket
scoop install terraform-docs
# macOS
brew install terraform-docs
# Go 1.25以上(v0.24.0のgo.mod は go 1.25.0)
go install github.com/terraform-docs/[email protected]
# バイナリを直接取得(Linux amd64)
curl -sLO https://github.com/terraform-docs/terraform-docs/releases/download/v0.24.0/terraform-docs-v0.24.0-linux-amd64.tar.gz
tar -xzf terraform-docs-v0.24.0-linux-amd64.tar.gz
sudo mv terraform-docs /usr/local/bin/
導入後は terraform-docs version で確認します。手元のmacOSでは terraform-docs version v0.24.0 9d44551 darwin/amd64 が返りました。コンテナで回す場合はQuayの公式イメージを使います。公式が案内する形は docker run --rm --volume "$(pwd):/terraform-docs" -u $(id -u) quay.io/terraform-docs/terraform-docs:0.24.0 markdown /terraform-docs です。-u $(id -u) はコンテナ側の実行UIDをホストに合わせる指定です。Linuxのbind mountでは省くと生成ファイルの所有者が食い違い、後続ステップの書き込みやコミットが失敗します。タグは latest(最新安定版)と edge(masterのHEAD)もありますが、CIでは上のように版数で固定してください。
READMEへの差し込み方式(injectとreplace)
実務で使うのはREADMEへの差し込みです。先にREADMEへマーカーを2行入れておきます。
# terraform-aws-s3-site
社内の静的サイト配信基盤で使うモジュールです。
<!-- BEGIN_TF_DOCS -->
<!-- END_TF_DOCS -->
## 問い合わせ先
基盤チーム
この状態で terraform-docs markdown table --output-file README.md --output-mode inject . を実行すると、標準出力には README.md updated successfully とだけ出て、マーカーの内側だけが生成結果に置き換わります。マーカーの外に書いた導入文や問い合わせ先はそのまま残ります。マーカー文字列は --output-template の既定値そのもので、変更するとマーカーも変えられます。
injectとreplaceの使い分け
--output-mode の既定はinjectです。replaceを指定すると、指定ファイルの中身を生成結果で丸ごと置き換えます。人が書いた説明を残したいREADMEにはinject、docs配下に生成物専用のファイルを置く運用にはreplaceが向きます。マーカーの無いファイルにinjectを実行した場合はファイル末尾にマーカーごと追記されるので、既存READMEに初回適用するときは差分を必ず確認してください。
ヘッダーの既定はmain.tf
--header-from の既定値は main.tf です。main.tf冒頭に /** ... */ 形式のブロックコメントを書いておくと、その中身が出力の先頭にそのまま載ります。READMEの前書きをmain.tfで管理したくない場合は --header-from doc.md のように別ファイルを指定します。
.terraform-docs.ymlの探索順と主要キー
オプションが増えたら設定ファイルへ逃がします。既定のファイル名は .terraform-docs.yml で、公式ドキュメントが示す探索順は次の5か所です。
- モジュールディレクトリの直下
- モジュールディレクトリ直下の
.config/(v0.15.0以降) - カレントディレクトリ
- カレントディレクトリの
.config/(v0.15.0以降) $HOME/.tfdocs.d/
トップレベルのキーはformatter、version、header-from、footer-from、recursive、sections、content、output、output-values、sort、settingsの11個です。よく使う形はこれくらいに収まります。
formatter: "markdown table"
header-from: "doc.md"
sections:
hide:
- providers
- requirements
sort:
enabled: true
by: required
output:
file: "README.md"
mode: inject
settings:
anchor: false
lockfile: true
この設定で実行すると、ProvidersとRequirementsの表が消え、必須のvariableが先頭に並び、settings.anchor: false によってアンカータグの付かない素の表になります。このアンカーは変数や出力の各項目へ直接リンクするためのもので、先ほどの出力例に並んでいた <a name="input_bucket_name"> がそれです。転記先のWikiがHTMLアンカーを解釈しないなら切ります。
サブコマンドをまたぐ出力先設定の継承
ここが事故になりやすい挙動です。設定ファイルに output.file が書かれていると、コマンドラインで別のサブコマンドを指定しても出力先はREADMEのままになります。設定ファイルを置いたディレクトリで terraform-docs json . を実行したところ、標準出力にJSONは出ず、README.md updated successfully とだけ表示されました。続けて terraform-docs tfvars hcl . を実行すると、READMEのマーカー内が次のtfvars形式に置き換わりました。
<!-- BEGIN_TF_DOCS -->
bucket_name = ""
force_destroy = false
logging_enabled = false
tags = {
"Env": "dev"
}
<!-- END_TF_DOCS -->
中身を確認したいだけなら --output-file "" を明示して標準出力へ戻します。設定ファイルを置いたリポジトリでは、生成コマンドをMakefileやtaskfileに固定して、手打ちのサブコマンドを許さない運用にするのが安全です。
CIでのREADME更新漏れ検知
terraform-docsの価値は生成そのものより、更新漏れを検知できる点にあります。CIで回すべきは生成コマンドではなく --output-check です。
出力チェックの終了コード
READMEが最新のときと、variableを1つ追加した直後に同じコマンドを実行した結果です。
$ terraform-docs markdown table --output-file README.md --output-check .
README.md is up to date
$ echo $?
0
# variable "force_destroy" を追加したあと
$ terraform-docs markdown table --output-file README.md --output-check .
Error: README.md is out of date
$ echo $?
1
終了コード1で失敗するので、プルリクエストのチェックに入れて対象ブランチの保護設定で必須にすれば、「コードだけ直してREADMEを忘れた」変更はマージ前に止まります。自動コミットで直す方式と違い、履歴にbot起因のコミットが混ざらないのも扱いやすい点です。
公式GitHub Actionの同梱バージョン
公式のActionはterraform-docs/gh-actionsで、最新リリースはv1.4.1(2025年5月13日)です。ここで見落としやすいのが同梱バージョンで、v1.4.1のDockerfileは FROM quay.io/terraform-docs/terraform-docs:0.20.0 です。CLIの現行版0.24.0とは4マイナー分の差があり、実害が出る組み合わせもあります。前述の .tofu がその例で、0.20.0では変数を読めないため、OpenTofuのリポジトリでこのActionを回すと「No inputs.」だけのREADMEが生成されます。この場合はActionを使わず、CLIのバージョンを固定して自分でインストールするステップに置き換えてください。
name: terraform-docs
on: [pull_request]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Render terraform docs
uses: terraform-docs/[email protected]
with:
working-dir: .
output-file: README.md
output-method: inject
fail-on-diff: "true"
入力名はCLIのフラグ名と一致しません。出力方法は output-method、フォーマットは output-format(既定 markdown table)で、既定値は working-dir: .、output-file: README.md、recursive: false、git-push: false、fail-on-diff: false です。差分検知だけならfail-on-diffをtrue、生成結果をコミットさせるならgit-pushをtrueにします。同じワークフローでplanやapplyまで流す構成は、GitHub ActionsとTerraformでAWSのCI/CDを構築する手順で扱っているOIDC認証と組み合わせてください。
pre-commitによるコミット前の更新
公式ドキュメントはpre-commitフックの例も載せています。リポジトリは https://github.com/terraform-docs/terraform-docs、フックIDは terraform-docs-go で、args には ["markdown", "table", "--output-file", "README.md", "./mymodule/path"] のようにフォーマットのサブコマンドから渡します。サブコマンドを省くとヘルプが表示されるだけで生成されません。モジュールが複数あるなら、hooksの配列にパス違いのエントリを並べます。fmtやvalidateも同じフックで回している現場なら、antonbabenko/pre-commit-terraform側の terraform_docs(terraform-docs 0.12.0以降が必要)にまとめたほうが設定が1か所で済みます。
README生成で使う主要オプション
共通フラグは20個近くあり、さらにサブコマンド固有のフラグが加わります。README生成で触るのは次の範囲です。
| フラグ | 既定値 | 用途 |
|---|---|---|
--output-mode |
inject | マーカー内差し込みか全置換か |
--recursive |
false | サブモジュールも更新 |
--recursive-path |
modules | 探索するサブモジュール置き場 |
--sort-by |
name | name / required / type |
--hide / --show |
なし | セクション単位の表示制御 |
--read-comments |
true | コメントをdescriptionに使う |
--lockfile |
true | .terraform.lock.hclを読む |
サブモジュールの再帰更新
--recursive を付けると、--recursive-path が指す配下のモジュールにも同じ処理が走ります。既定値は modules なので、modules/network/ のような一般的な構成ならパス指定は要りません。ルート側も対象に含めるかは --recursive-include-main(既定true)で制御します。READMEが無い子モジュールにはマーカー付きのREADMEが新規作成され、結果は次のように1行ずつ報告されます。
$ terraform-docs markdown table --output-file README.md --recursive .
README.md updated successfully
modules/network/README.md updated successfully
設定ファイルで header-from に独自ファイルを指定している場合は注意が必要です。設定は子モジュールにも継承されるため、同名のファイルが子側に無いと親のREADMEを更新した直後に止まります。header-from: "doc.md" の設定で再帰実行したときの実際の出力が次です。
$ terraform-docs --recursive .
README.md updated successfully
Error: stat modules/network/doc.md: no such file or directory
子モジュールごとにdoc.mdを置くか、子側の設定で header-from を既定のmain.tfへ戻してください。
フォーマット指定はフラグではなくサブコマンド
フォーマットの指定はフラグではなくサブコマンドです。markdown、asciidoc、json、yaml、toml、xml、pretty、tfvars の8種類があり、markdownとasciidocはさらに table と document に分かれます。tableは表形式、documentは「The following requirements are needed by this module:」のような説明文付きの箇条書きになります。CLIに --output-format を渡すと Error: unknown flag: --output-format で即座に失敗します。この名前はGitHub Actionの入力にだけ存在するもので、両者を混同した解説が出回っているので注意してください。
よくある質問
terraform-docsとTerraformの公式ドキュメントは何が違いますか?
公式ドキュメント(developer.hashicorp.com/terraform/docs)はHashiCorpが公開しているTerraform本体のリファレンスです。terraform-docsは自分が書いたモジュールから説明を生成するサードパーティのCLIで、MITライセンスのOSSです。GitHubの segmentio/terraform-docs は現在 terraform-docs/terraform-docs へ転送され、リポジトリの作成日は2016年6月12日です。
Homebrewでインストールするコマンドは何ですか?
brew install terraform-docs です。2026年9月18日時点でHomebrew本体のFormulaが0.24.0を配っているため、tapの追加は要りません。
.terraform-docs.ymlはどこに置けばよいですか?
対象モジュールの直下が最優先で読まれます。リポジトリ全体で共通化したい場合はカレントディレクトリか .config/ に置き、個人の既定値は $HOME/.tfdocs.d/ に置きます。別名のファイルを使うときは -c で明示します。
サブモジュールのREADMEもまとめて更新できますか?
--recursive で更新できます。探索先の既定は modules ディレクトリで、別の場所に置いているなら --recursive-path で指定します。--recursive-exclude で除外もできます。
生成結果に変数の説明が入らないのはなぜですか?
variableブロックにdescriptionが無く、直前のコメントも無い場合です。--read-comments は既定で有効なので、descriptionを書かない方針なら変数の直前に # コメントを1行置くだけでも表が埋まります。tfstateやバックエンドの設定は出力に含まれないため、その説明はTerraform stateとtfstateの構造の記事を参照してください。