Terraform has fundamentally shifted the paradigm of infrastructure management by treating infrastructure as code, allowing engineers to version, share, and reuse datacenter blueprints with the same rigor applied to application software. However, the operational value of a Terraform module is not derived solely from its functional correctness within .tf files; it is equally dependent on the clarity, accuracy, and accessibility of its documentation. As modules grow in complexity, integrating cloud providers, custom resources, and nested dependencies, manual documentation becomes a bottleneck prone to drift. This is where terraform-docs emerges as a critical component of the developer workflow. It is a command-line tool designed to generate documentation for Terraform modules and providers from their source code. By parsing the module structure, terraform-docs automatically extracts inputs, outputs, requirements, and resources, rendering them into human-readable formats such as Markdown or AsciiDoc. This eliminates the risk of documentation divergence, ensuring that the README.md file always reflects the current state of the code.
Understanding the Terraform Ecosystem and Documentation Challenges
To appreciate the utility of terraform-docs, one must first understand the architectural principles of Terraform itself. Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. It manages both existing popular service providers and custom in-house solutions. The platform rests on four key pillars that directly impact how documentation is generated.
First, Infrastructure as Code requires that infrastructure be described using a high-level configuration syntax. This allows a blueprint of the datacenter to be versioned and treated as standard code. Second, the Execution Plans feature introduces a planning step where Terraform generates an execution plan, showing exactly what changes will occur before the apply command is run. Third, the Resource Graph allows Terraform to build a graph of all resources and parallelize the creation or modification of non-dependent resources, optimizing efficiency. Finally, Change Automation enables complex changesets to be applied with minimal human interaction.
Because Terraform relies on a declarative model, the .tf files contain rich metadata about inputs, outputs, and provider requirements. However, this metadata is machine-readable, not human-readable. Without a translation layer, consumers of a module must manually parse HCL code to understand required variables, default values, and constraints. terraform-docs automates this translation. It parses the module root, identifies the structural components, and formats them into a consistent template. This is particularly vital for open-source modules published on registries, where clear documentation is the primary factor in adoption.
Installation and Platform Support
terraform-docs is available across major operating systems, including Linux, macOS, Windows, and FreeBSD. The installation process varies slightly depending on the platform and the preferred package management strategy. The tool version referenced in current documentation workflows is v0.24.0, which is a stable release.
For users on macOS, Homebrew provides a streamlined installation path. The command brew install terraform-docs or the more specific brew install terraform-docs/tap/terraform-docs installs the latest stable binary. Windows users have two primary package manager options: Scoop and Chocolatey. To use Scoop, users must first add the dedicated bucket and then install the package using scoop bucket add terraform-docs https://github.com/terraform-docs/scoop-bucket followed by scoop install terraform-docs. For Chocolatey, the installation is direct via choco install terraform-docs.
For Go developers, installation is handled through the Go module system. For Go 1.17 and later, the command go install github.com/terraform-docs/[email protected] places the binary in the user's GOPATH bin directory. For Go 1.16, the environment variable GO111MODULE="on" is required, and the command becomes GO111MODULE="on" go get github.com/terraform-docs/[email protected]. It is important to note a historical naming change; for versions v0.9.1 and earlier, the old module namespace under segmentio was used, requiring GO111MODULE="on" go get github.com/segmentio/[email protected].
Pre-compiled binaries are available on the GitHub Release page for all supported platforms. Users can download the appropriate asset using curl and tar. The following sequence downloads the binary for the current architecture, extracts it, sets executable permissions, and moves it to a directory in the system PATH:
bash
curl -sSLo ./terraform-docs.tar.gz https://terraform-docs.io/dl/v0.24.0/terraform-docs-v0.24.0-$(uname)-amd64.tar.gz
tar -xzf terraform-docs.tar.gz
chmod +x terraform-docs
mv terraform-docs /some-dir-in-your-PATH/terraform-docs
A common post-installation issue is the terraform-docs: command not found error. This occurs if the directory containing the binary is not included in the system's $PATH. In such cases, users can either add the directory to the $PATH or perform a manual build by cloning the repository and running make build. This manual build places the binary in a specific location determined by the Go environment and system architecture:
bash
$(go env GOPATH)/src/github.com/terraform-docs/terraform-docs/bin/$(uname | tr '[:upper:]' '[:lower:]')-amd64/terraform-docs
Additionally, code completion for bash and zsh shells can be installed to enhance the developer experience.
Docker and Containerized Execution
For environments where installing system binaries is restricted, or for CI/CD pipelines requiring isolated tooling, terraform-docs can be run as a Docker container. The container image is hosted on Quay.io. To run the tool, the current directory containing the Terraform .tf files is mounted into the container's /terraform-docs volume. The command below demonstrates how to execute the tool within the container:
bash
docker run --rm --volume "$(pwd):/terraform-docs" -u $(id -u) quay.io/terraform-docs/terraform-docs:0.24.0 markdown /terraform-docs
The tag latest in the Docker image refers to the latest stable released version, while the edge tag refers to the HEAD of the master branch at any given point in time. Named version tags are identical to the official GitHub releases without the leading v.
If the Terraform module configuration does not have output.file enabled, the generated documentation can be redirected to a file using standard shell redirection. For example, generating Markdown output into a file named doc.md is achieved as follows:
bash
docker run --rm --volume "$(pwd):/terraform-docs" -u $(id -u) quay.io/terraform-docs/terraform-docs:0.24.0 markdown /terraform-docs > doc.md
Basic Usage and Command Execution
The primary use case for terraform-docs is to generate documentation within a directory. The most common command generates a Markdown table and injects it into the README.md file. The command syntax is:
bash
terraform-docs markdown table --output-file README.md --output-mode inject /path/to/module
The --output-mode inject flag ensures that the generated content is placed between specific markers in the README file, allowing manual content to be preserved outside the generated block. The tool supports various output formats, including Markdown and AsciiDoc. The generated content can be customized further via configuration files, but the default behavior provides a robust table of inputs, outputs, and requirements.
Configuration and Customization
terraform-docs is highly configurable via a YAML file. The default name of this file is .terraform-docs.yml. The tool searches for this file in a specific path order:
1. Root of the module directory
2. .config/ folder at the root of the module directory
3. Current directory
4. .config/ folder at the current directory
5. $HOME/.tfdocs.d/
The configuration file allows deep customization of the generated output. Below is a table detailing the key configuration options and their default values:
| Configuration Key | Description | Default Value |
|---|---|---|
formatter |
The output format (e.g., markdown, asciidoc). Required. | "" |
version |
Version string for the tool output. | "" |
header-from |
File to read the header from. | main.tf |
footer-from |
File to read the footer from. | "" |
recursive.enabled |
Whether to recursively check submodules. | false |
recursive.path |
Path to recursive modules. | modules |
include-main |
Whether to include the main module in the list. | true |
sections.hide |
List of sections to hide. | [] |
sections.show |
List of sections to show. | [] |
output.file |
Target file for output. | "" |
output.mode |
Injection mode (inject, overwrite, replace). | inject |
output-values.enabled |
Whether to include output values. | false |
sort.enabled |
Whether to sort inputs/outputs. | true |
sort.by |
Field to sort by (name, description). | name |
settings.anchor |
Add HTML anchors to headings. | true |
settings.color |
Use colored output. | true |
settings.default |
Show default values. | true |
settings.description |
Show descriptions. | false |
settings.escape |
Escape HTML characters. | true |
settings.hide-empty |
Hide empty sections. | false |
settings.html |
Output HTML. | true |
settings.indent |
Indentation level for nested structures. | 2 |
settings.lockfile |
Show lockfile info. | true |
settings.read-comments |
Read comments from files. | true |
settings.required |
Show required status. | true |
settings.sensitive |
Show sensitive status. | true |
settings.type |
Show type information. | true |
The output block controls how the generated content is written. The template field allows for custom Go templates. The default template wraps the content in HTML comments to facilitate injection:
yaml
template: |-
<!-- BEGIN_TF_DOCS -->
{{ .Content }}
<!-- END_TF_DOCS -->
Advanced Templating and Content Generation
The content field in the configuration allows for advanced customization using Go templates. If the content field is empty, the default order of sections is used. However, when content is defined, it takes precedence over sections.show and sections.hide. The content is a Go template that supports various variables representing the generated sections.
The following variables are available in the template:
- {{ .Header }}
- {{ .Footer }}
- {{ .Inputs }}
- {{ .Modules }}
- {{ .Outputs }}
- {{ .Providers }}
- {{ .Requirements }}
- {{ .Resources }}
Additionally, the function {{ include "relative/path/to/file" }} allows for the inclusion of external files within the documentation. The {{ .Module }} variable is a special variable that represents the Terraform module struct itself, rather than a generated section.
An example of a custom content template demonstrates the flexibility of this system. The following configuration allows arbitrary text to be placed anywhere in the content, and sections can be reordered or interspersed with custom logic:
yaml
content: |-
Any arbitrary text can be placed anywhere in the content
{{ .Header }}
and even in between sections
{{ .Providers }}
and they don't even need to be in the default order
{{ .Outputs }}
include any relative files
{{ include "relative/path/to/file" }}
{{ .Inputs }}
# Examples
hcl
{{ include "examples/foo/main.tf" }}
## Resources
{{ range .Module.Resources }}
- {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
{{- end }}
This level of control allows engineers to create documentation that matches the style and structure of their organization's technical writing standards, embedding code examples and resource listings directly from the module source.
Automation in CI/CD Pipelines
Ensuring that documentation is always up-to-date is best achieved through automation. terraform-docs integrates seamlessly with GitHub Actions and pre-commit hooks.
GitHub Actions
To use the terraform-docs GitHub Action, a YAML workflow file must be configured, typically located at .github/workflows/documentation.yml. The action can be triggered on pull requests and configured to push changes back to the PR. The following workflow demonstrates a standard configuration:
yaml
name: Generate terraform docs
on:
- pull_request
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.ref }}
- name: Render terraform docs and push changes back to PR
uses: terraform-docs/gh-actions@main
with:
working-dir: .
output-file: README.md
output-method: inject
git-push: "true"
This workflow checks out the pull request branch, runs the documentation generator, and pushes the updated README.md back to the PR, ensuring that reviewers can see the documentation changes alongside the code changes.
Pre-commit Hooks
For local development, the pre-commit framework can enforce documentation updates before a commit is pushed. First, pre-commit must be installed. Then, a .pre-commit-config.yaml file is created in the root of the Git repository with the following content:
yaml
repos:
- repo: https://github.com/terraform-docs/terraform-docs
rev: "v0.24.0"
hooks:
- id: terraform-docs-go
args: ["markdown", "table", "--output-file", "README.md", "./mymodule/path"]
After configuring the file, the hooks are installed using:
bash
pre-commit install
pre-commit install-hooks
Once configured, any changes to the module's .tf files will automatically trigger an update to the documentation when a commit is attempted, preventing documentation drift at the source.
Library Usage and Programmatic Integration
While terraform-docs is primarily designed as a standalone binary, parts of its functionality are available as a Go library. This allows developers to integrate documentation generation into custom tools or larger infrastructure projects. The library exposes packages for formatting, printing, and Terraform module loading.
The following Go code example demonstrates how to build Terraform docs for a module root path and a provided content template:
```go
import (
"github.com/terraform-docs/terraform-docs/format"
"github.com/terraform-docs/terraform-docs/print"
"github.com/terraform-docs/terraform-docs/terraform"
)
func buildTerraformDocs(path string, tmpl string) (string, error) {
config := print.DefaultConfig()
config.ModuleRoot = path // module root path (can be relative or absolute)
module, err := terraform.LoadWithOptions(config)
if err != nil {
return "", err
}
// Generate in...
}
```
This API allows for programmatic control over the configuration, such as setting the module root and loading the module with specific options, enabling custom documentation pipelines that go beyond the standard command-line interface.
Conclusion
terraform-docs is an essential tool for modern Terraform engineering, bridging the gap between code and documentation. By automatically generating human-readable documentation from source files, it enforces a high standard of clarity and accuracy. The tool's support for multiple installation methods, including pre-compiled binaries, Go installs, Docker containers, and package managers, ensures it can be integrated into any development workflow. The robust configuration system, powered by YAML files and Go templates, allows for deep customization of the output format, enabling organizations to maintain a consistent documentation style across all modules. Furthermore, the integration with CI/CD pipelines through GitHub Actions and pre-commit hooks ensures that documentation remains synchronized with code changes, eliminating the risk of outdated or incorrect information. As infrastructure complexity continues to grow, the ability to auto-generate accurate documentation is not just a convenience but a critical requirement for scalable and maintainable infrastructure as code practices.