Automate Terraform documentation like a pro!

Umesh Kumhar
Google Cloud - Community
7 min readSep 28, 2022

Hello Reader 👋🏽

This blog is all about running away from writing and updating frequent changes to terraform documentation! Lets learn through the way of automating the terraform documentation into our development cycle, so that code & relevant documents are always in sync!

Introduction

In this blog, we will use an open source tool called terraform-docs (https://terraform-docs.io/) to automate the documentation and also learn how to integrate with CI pipeline or git pre-commit hook.

terraform-docs is a command line tool used to autogenerate documentation around terraform modules. This tool supports multiple output format including markdown tables and asciidoc tables.

Example Output of Module README.md:

README.md in markdown table format

Why is needed?

With the development of terraform modules, the important aspect comes is the usage of those modules across different teams and terraform developers. So documentation plays an important role in understanding the structure of module and different ways to use in different use cases.

Updating the documentation get cumbersome with new features added in sprints and multiple people working on different features. What if some tool can prepare a at-least core part of documentation with every new code changes? Here comes terraform-docs

Where to start?

https://github.com/terraform-docs/terraform-docs/
  1. Install terraform-docs or download from terraform-docs github repo.
# go1.17+
go install github.com/terraform-docs/terraform-docs@v0.16.0

check other installation methods here.

2. Configure a default configuration file named .terraform-docs.yml that can be used for all README.md generation. Add below content for some better customisations:

formatter: "markdown table" # this is requiredversion: "0.16"header-from: main.tf
footer-from: ""
recursive:
enabled: false
path: ""
sections:
hide: []
show: []
hide-all: false # deprecated in v0.13.0, removed in v0.15.0
show-all: true # deprecated in v0.13.0, removed in v0.15.0
content: |-
{{ .Requirements }}
## Usage
Basic usage of this module is as follows:
```hcl
module "example" {
{{"\t"}} source = "<module-path>"
{{- if .Module.RequiredInputs }}
{{"\n\t"}} # Required variables
{{- range .Module.RequiredInputs }}
{{"\t"}} {{ .Name }} = {{ .GetValue }}
{{- end }}
{{- end }}
{{- if .Module.OptionalInputs }}
{{"\n\t"}} # Optional variables
{{- range .Module.OptionalInputs }}
{{"\t"}} {{ .Name }} = {{ .GetValue | printf "%s" }}
{{- end }}
{{- end }}
}
```
{{ .Resources }}{{ .Inputs }}{{ .Outputs }}output:
file: README.md
mode: inject
template: |-
<!-- BEGIN_AUTOMATED_TF_DOCS_BLOCK -->
{{ .Content }}
<!-- END_AUTOMATED_TF_DOCS_BLOCK -->
output-values:
enabled: false
from: ""
sort:
enabled: true
by: name
settings:
anchor: true
color: true
default: true
description: true
escape: true
hide-empty: false
html: true
indent: 2
lockfile: true
read-comments: true
required: true
sensitive: true
type: true

3. In this step, we will setup/configure README.md in the TF codebase:

  • Create a README.md inside terraform modules and provide some introductory details about modules. Refer below README.md
# TF code base## Introduction
This is sample readme for terraform module
  • Add the comment blocks where you want to auto-generate the different sections of terraform module. If required, we can keep a footer message that will not be changed.
# TF code base## Introduction
This is sample readme for terraform module
<!-- BEGIN_AUTOMATED_TF_DOCS_BLOCK -->
<!-- END_AUTOMATED_TF_DOCS_BLOCK -->
## Footer
Contributor Names

3. Now we are ready to auto generate the documentation!

terraform-docs -c .terraform-docs.yml ./tf-code/

After the execution observe the README.md file updated in the given tf-code directory

4. How the final README.md looks like:

# TF code base## Introduction
This is sample readme for terraform module
<!-- BEGIN_AUTOMATED_TF_DOCS_BLOCK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0.0 |
## Usage
Basic usage of this module is as follows:
```hcl
module "example" {
source = "<module-path>"
# Required variables
vsphere_password =
vsphere_server =
vsphere_user =
vsphere_vm_config =
}
```
## ProvidersNo providers.## ResourcesNo resources.## Inputs| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_vsphere_password"></a> [vsphere\_password](#input\_vsphere\_password) | The password for the vCenter admin user | `string` | n/a | yes |
| <a name="input_vsphere_server"></a> [vsphere\_server](#input\_vsphere\_server) | The FQDN of the vSphere vCenter server | `string` | n/a | yes |
| <a name="input_vsphere_user"></a> [vsphere\_user](#input\_vsphere\_user) | The username used to connect to the vCenter server. Must be an admin user | `string` | n/a | yes |
| <a name="input_vsphere_vm_config"></a> [vsphere\_vm\_config](#input\_vsphere\_vm\_config) | All Vsphere VM Configs | <pre>list(object({<br> name = string,<br> datacenter = string,<br> cluster = string,<br> datastore = string,<br> resource_pool = string,<br> folder = string,<br> guest_os_type = string,<br> num_cpu = number,<br> num_memory = number,<br> network = string,<br> network = string,<br> # TODO: P2: gleichda according to the tfvars example and the module this is a defined object.<br> # Should be reflected accordingly<br> disks = any<br> }))</pre> | n/a | yes |
## Outputs| Name | Description |
|------|-------------|
| <a name="output_vsphere_virtual_machine"></a> [vsphere\_virtual\_machine](#output\_vsphere\_virtual\_machine) | Vsphere VM |
<!-- END_AUTOMATED_TF_DOCS_BLOCK -->## Footer
Contributor Names

How it looks on github!

Additional Useful Features

  • If the terraform codebase has multiple modules, in that case we can utilise the recursive option which allows the configuration to generate multiple README.md in one execution. Check for more details here.
# file: .terraform-docs.yml
recursive:
enabled: true
path: submodules-folder
  • If there are custom requirements within the documentation, that can be achieved by updating the content section. Checkout the sample example config below. Check for more details here.
# file: .terraform-docs.yml
content: |-
{{ .Requirements }}
## Usage
Basic usage of this module is as follows:
Some more information can go here.```hcl
module "example" {
{{"\t"}} source = "<module-path>"
{{- if .Module.RequiredInputs }}
{{"\n\t"}} # Required variables
{{- range .Module.RequiredInputs }}
{{"\t"}} {{ .Name }} = {{ .GetValue }}
{{- end }}
{{- end }}
{{- if .Module.OptionalInputs }}
{{"\n\t"}} # Optional variables
{{- range .Module.OptionalInputs }}
{{"\t"}} {{ .Name }} = {{ .GetValue | printf "%s" }}
{{- end }}
{{- end }}
}
```
{{ .Resources }}include any relative files

{{ include "relative/path/to/file.tf" }}
{{ .Inputs }}{{ .Outputs }}

Integration in GCP Cloud Build Service

Setup a cloud build workflow to trigger the execution of terraform-docs on your github repo or GCP cloud source repositories.

This can be easily achieved in two steps:

Step 1: This step is not required for GCP cloud source repositories. Its required for the github as source code repositories. Create a github trigger in cloud build on GCP, so that with every commit or changes in source github, the workflow will be triggered.

gcloud beta builds triggers create github \
--repo-name=REPO_NAME \
--repo-owner=REPO_OWNER \
--branch-pattern=BRANCH_PATTERN \ # or --tag-pattern=TAG_PATTERN
--build-config=BUILD_CONFIG_FILE \
--include-logs-with-status

Note: The triggers has different custom options including PR trigger, commit based trigger, manual trigger, etc. This can also be done via console as well. Learn more about github trigger here.

Step 2: Write your cloudbuild.yaml in the root of your repository structure and add step to generate terraform-docs . Checkout out sample cloudbuild.yaml below.

...steps:
# Pull the terraform-docs image from container registry.
- name: 'quay.io/terraform-docs/terraform-docs'
args: [
'-c', '.terraform-docs.yml',
'./modules'
]
...

Note: There are a lot of great features available in cloudbuild GCP service, here we have just covered very basic usage of that. Refer here for more details.

Integration in Github Action

Setup a workflow in github to run terraform-docs and push the documentation changes back to PR(Pull Request).

Create a file in github, .github/workflows/documentation.yaml with given content:

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"

Check more about terraform-docs GitHub Action and its configuration.

Integration in pre-commit Hooks

Enforce the developers to run terraform-docs before committing it to repo, then your friend is pre-commit hooks (https://pre-commit.com/). This utility allows to configure a set of rules/action that are needed to be executed before going ahead with commit.

  1. Create a file .pre-commit-config.yaml in the root of your repo with given content:
repos:
- repo: https://github.com/terraform-docs/terraform-docs
rev: "v0.12.0"
hooks:
- id: terraform-docs
args: ["terraform", "-c", ".terraform.yml", "."]

2. Install pre-commit and activate the hooks.

## Install
pip install pre-commit
## Activate the hooks
pre-commit install
## if already exists
pre-commit autoupdate

3. Go ahead and perform any terraform changes and try git commit . With that we can see the hooks execution before commit and updates in README.md.

4. We can also trigger pre-commit hooks manually using run option.

pre-commit run terraform-docs

Check more pre-commit details here.

Thanks for reading this blog around terraform-docs. Let me know if there is any feedback!

Don’t forget to clap and follow on Medium :-) & also connect me on Linkedin.

--

--