Générer la documentation de vos modules Terraform

Voici comment automatiser la documentation associée à vos solutions Terraform afin de garantir sa cohérence quelques soient les évolutions mise en oeuvre.

Terraform Docs

Terraform Docs est un outil open source qui permet de générer une documentation pour les configurations Terraform. Il permet de créer une documentation claire et structurée pour les modules, les ressources, les variables et les sorties utilisés dans vos configurations Terraform.

L'outil Terraform Docs utilise les informations présentes dans les fichiers Terraform pour générer automatiquement la documentation. Cela permet de garantir que la documentation est à jour et cohérente avec les configurations Terraform.

Terraform Docs supporte plusieurs formats de sortie tels que Markdown, JSON et HTML, ce qui permet une intégration facile avec d'autres outils de documentation. Il permet également de personnaliser la documentation en utilisant des modèles.

En utilisant Terraform Docs, vous pouvez améliorer la documentation de vos configurations Terraform et faciliter la compréhension pour les autres membres de l'équipe.

Installation de l'outil sous Windows en utilisant Winget

Winget est un gestionnaire de packages open source pour Windows. Il permet d'installer facilement des applications en utilisant une interface en ligne de commande.

Winget est conçu pour être simple à utiliser et permet de rechercher, installer, désinstaller et mettre à jour des applications à partir d'un catalogue centralisé de packages.

Pour rechercher l'identifiant du package associé à "Terraform-docs" utiliser cette commande dans un terminal Windows :

winget search "Terraform-docs"

Une fois l'ID identifié, il vous suffit d'exécuter cette commande pour procéder à l'installation de l'outil :

winget install "Terraform-docs.Terraform-docs"

Comme le message ci-dessus l'indique penser à redémarrez votre terminal avant de d'utiliser la commande "terraform-docs":

Génération de votre documentation en local

Exemple simple

Afin de travailler sur un exemple accésible à tous, je vais réutiliser un exemple que j'avais réalisé pour un événement (Clash Session #10 Pulumi vs Terraform) dont voici le repository GitHub: https://github.com/aloizeau/Clash-TerraformVsPulumi

Sur cette solution, j'ai à la fois des modules et des scripts de ressources Azure qui me permet de déployer une solution simple. Je vais donc la cloner puis me mettre à la racine de la solution:

Comme vous pourrez le constater cette solution n'a pas de documentation technique présentant la solution Terraform implémentée, voyons maintenant comment la générer en utilisant Terraform-docs.

La commande suivante va permettre de générer l'ensemble de la documentation dans un fichier README.md de façon récurcive au format markdown.

terraform-docs markdown --recursive --output-file README.md .

Ce qui est intéressant c'est qu'il ne va pas écrasser le fichier README.md existant mais le compléter avec les informations extraites de la solution Terraform:

Voici le rendu standard sans personnalisation :

Cela étant basé sur le contenu de vos fichiers, il est très important de prendre le temps nécessaire pour ajouter des descriptions, conditions, configurations ...

Voici un exemple permettant d'avoir un niveau de détail suffisant lors de la génération :

variable "location" {
  description = "(Optional) The Azure Region where the Windows Web App should exist. Changing this forces a new Windows Web App to be created."
  type        = string
  default     = "westeurope"
  validation {
    condition     = contains(["westeurope", "northeurope", "francecentral"], var.location)
    error_message = "Sorry, but we only accept 'westeurope', 'northeurope' or 'francecentral' values."
  }
}

Utilisation d'un fichier de configuration

L'outil Terraform-docs propose des options permettant de personnaliser le fichier de sortie: https://terraform-docs.io/user-guide/configuration/

Voici un exemple d'une configuration personnalisée où je paramétre mes préférences et dans lequel j'ajoute un header et footer:

formatter: "markdown document" # this is required: https://terraform-docs.io/user-guide/configuration/formatter/

version: "= 0.16.0"

header-from: "/docs/.header.md"
footer-from: "/docs/.footer.md"

recursive:
  enabled: false
  path: modules

sections:
  show:
    - header
    - providers
    - inputs
    - outputs
    - footer

content: |-
  {{ .Header }}

  {{ .Providers }}

  {{ .Inputs }}

  {{ .Outputs }}  

  {{ .Footer }}

output:
  file: README.md
  mode: inject
  template: |-
    [//]: # (BEGIN_TF_DOCS)
    {{ .Content }}

    [//]: # (END_TF_DOCS)

output-values:
  enabled: false
  from: ""

sort:
  enabled: true
  by: required

settings:
  anchor: true
  color: true
  default: true
  description: true
  escape: true
  hide-empty: true
  html: true
  indent: 2
  lockfile: true
  read-comments: true
  required: true
  sensitive: true
  type: true

Après avoir initialisé ce fichier je peux maintenant éxécuter la génération via cette ligne:

terraform-docs -c .terraform-docs.yml .

Nous retrouvons maintenant une personnalisation du fichier de sortie :

Automatisation DevOps

Voici maintenant comment automatiser la génération de votre documentation au travers de votre CI/CD pour nos deux outils préférés :

  • GitHub

  • Azure DevOps

Workflow GitHub

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"

Pipeline Azure DevOps

name: $(BuildDefinitionName)_$(date:yyyyMMdd)$(rev:.r)
trigger:
  branches:
    include:
      - main
      - feature/*
  paths:
    exclude:
      - pipelines/*
      - environments/*
pool:
  vmImage: ubuntu-latest # This is the default if you don't specify a pool or vmImage.
variables:
  - name: Version
    value: "v0.16.0"
stages:
  - stage: "TerraformDoc"
    jobs:
      - job: "TerraformDocsJobs"
        workspace:
          clean: all
        displayName: "Generate Terraform Docs"
        steps:
          - checkout: self
            clean: true
            persistCredentials: true
          - bash: |
              # build new doc
              ./tfdocs/terraform-docs markdown . --output-file=README.md --recursive --read-comments --output-mode=inject

              # timestamp files
              files=$(find . -type f -name "README.md")
              for file in ${files}
              do
                echo
                echo 'Processing '$file

                tempFile='TEMP-README.txt'
                touch $tempFile
                rm $tempFile

                cat $file | while read line; do
                    if [[ $line =~ ^Last ]]; then
                      # ignore old timestamp line if any
                      echo
                    else
                        echo $line >> $tempFile
                    fi
                done
                echo 'Last generated by Terraform-docs '$(date '+%Y-%m-%d %H:%M:%S') >> $tempFile

                rm $file
                mv  $tempFile $file

              done
            name: "generateTerraformDoc"
            displayName: "Generate > Terraform-docs"
          - script: |
              echo $(Build.SourceBranch)
              sourceBranch=$(Build.SourceBranch)
              current_branch=$(echo "${sourceBranch:11}")
              email=$(Build.RequestedForEmail)
              name=$(echo "${email::-7}")
              git config --global user.email $email
              git config --global user.name "$name (from Azure DevOps Pipeline)"
              git checkout -b $current_branch
              # git pull $current_branch --rebase
              git add "**.md"
              git commit -m "[skip ci] Update readme.md files"
              git push --force --set-upstream origin $current_branch
            name: "gitCommit"
            displayName: "Git > Add or update readme.md files"

Did you find this article valuable?

Support Antoine LOIZEAU by becoming a sponsor. Any amount is appreciated!