Streamlining Terraform Module Management with GitHub Actions, Semantic Releases, and Terraform Docs

Luiz André Nunes da Silva
6 min readMay 28, 2024

Hi everyone, my name is Luiz! Today, I’m excited to guide you through the process of versioning and documenting your Terraform modules. We’ll be using GitHub Actions, Semantic Releases, and Terraform Docs to make your infrastructure management smoother and more efficient. Let’s dive in and simplify your workflow together!

Why Version and Document Your Terraform Modules?

Versioning and documentation are critical for maintaining the reliability and usability of your Terraform modules. Proper versioning allows you to:

  • Introduce new features without breaking existing implementations.
  • Provide end-of-life support for outdated versions.
  • Quickly revert changes in case of deployment mistakes.
  • Implement security updates and ensure compliance.

Setting Up GitHub Actions

GitHub Actions provides a powerful platform to automate workflows directly in your GitHub repository. We’ll set up a Release and Doc pipeline to automate the versioning and documentation process for our Terraform modules

Step 1: Creating Terraform Docs Workflow File

First, create a .github/workflows/docs.yaml file in your repository:

name: "Terraform Docs"

on:
pull_request:
paths:
- "**/*.tf"

jobs:
terraform-docs:
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- name: Pull request checkout
uses: actions/checkout@v3
id: checkout
with:
ref: ${{ github.event.pull_request.head.ref }}
- name: README.md generation
uses: terraform-docs/gh-actions@main
id: tfdocs
with:
find-dir: ./modules
output-file: readme.md
output-method: inject
git-push: "true"

This configuration will trigger the Terraform Docs CLI each time a pull request is opened, ensuring that any changes are thoroughly documented in the module folder.

Step 3: Configuring Semantic Release Next

Next, set up Semantic Release to automate versioning. Create a .releaserc.json and a blank CHANGELOG.md in your repository’s root directory:

{
"branches": ["main", "master"],
"ci": false,
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/release-notes-generator",
{
"preset": "conventionalcommits"
}
],
[
"@semantic-release/github",
{
"successComment": "This ${issue.pull_request ? 'PR is included' : 'issue has been resolved'} in version ${nextRelease.version} :tada:",
"labels": false,
"releasedLabels": false
}
],
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md",
"changelogTitle": "# Changelog\n\nAll notable changes to this project will be documented in this file."
}
],
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md"],
"message": "chore(release): version ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}

This configuration will set up the release content and CHANGELOG.md content, which stores the differences between versions. This ensures that all changes are documented clearly, making it easier to track updates and maintain a comprehensive history of modifications.

Step 4: Creating Sem-Ver Workflow File

Next, create a .github/workflows/release.yaml file in your repository:

name: Release
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- "**/*.tf"
jobs:
release:
name: Release
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0

- name: Release
uses: cycjimmy/semantic-release-action@v4
with:
semantic_version: 23.0.2
extra_plugins: |
@semantic-release/changelog@6.0.3
@semantic-release/git@10.0.1
conventional-changelog-conventionalcommits@7.0.2
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}

This file sets up a GitHub workflow that automates the release process for our Terraform Modules. It is when there is a push to the main or master branches, when .tf files are changed. If you have any questions about generating a GitHub token, please refer to this documentation.

It’s also important to note that you will need a secret in your repository containing your GitHub Token. If you have any questions about creating a secret and storing the Token, please refer to this documentation

Step 5: Creating AWS S3 Terraform Module

Now that we have our files created, let’s move forward and test the workflow while building our S3 bucket module. This step is crucial to ensure everything works as expected before deploying it into production.

First, create this repo structure with the following files

├── .github
│ └── github-files-here
├── modules
│ └── s3
│ ├── main.tf
│ ├── variables.tf
│ └── outputs.tf
├── .releaserc.json
└── CHANGELOG.md

main.tf

resource "aws_s3_bucket" "this" {
count = var.create ? 1 : 0
bucket = var.name

force_destroy = var.force_destroy
object_lock_enabled = var.object_lock_enabled
tags = var.tags
}

variables.tf

variable "create" {
description = "Controls if S3 bucket should be created"
type = bool
default = true
}

variable "name" {
type = string
description = "The name of the resource"
}

variable "force_destroy" {
description = "(Optional, Default:false ) A boolean that indicates all objects should be deleted from the bucket so that the bucket can be destroyed without error. These objects are not recoverable."
type = bool
default = false
}

variable "object_lock_enabled" {
description = "(Optional, Default:false ) Whether S3 bucket should have an Object Lock configuration enabled."
type = bool
default = false
}

variable "tags" {
description = "(Optional) A mapping of tags to assign to the bucket."
type = map(string)
default = {}
}

outputs.tf

output "id" {
value = aws_s3_bucket.this[0].id
description = "The name of the bucket."
}

output "arn" {
value = aws_s3_bucket.this[0].arn
description = "The ARN of the bucket. Will be of format arn:aws:s3:::bucketname."
}

output "bucket_domain_name" {
value = aws_s3_bucket.this[0].bucket_domain_name
description = "The bucket domain name. Will be of format bucketname.s3.amazonaws.com."
}

After constructing the module, we must utilize standardized commit messages to inform the workflow whether it involves a feature, bug fix, or breaking change. The plugin parses the commits made during development, so we’ll run the following commands from the root folder:

1- git add .

2- git commit -m “feat(s3): Creating s3 terraform module”

3- git push origin -b <branch-name>

After you create a pull request, you can see our Terraform Docs workflow in action.

If everything runs smoothly, you’ll notice that a readme.md file has been created within the S3 folder, containing the module content. To trigger the releases workflow, simply click on the merge button.

If everything runs smoothly, we’ll observe numerous changes in our repository’s master branch. This includes commits from automation bots reflected in the CHANGELOG file and readme.md within the module’s files. Additionally, we’ll notice the creation of a new release, labeled v1.0.0, containing comprehensive details about the added features.

Important things to know!

Using standardized commit messages is essential for automating semantic versioning. Here are the key points to consider:

Feature Additions (feat):

  • Example: feat(s3): Updating versioning configuration
  • Impact: Adds a new feature in a backward-compatible manner.
  • Version Bump: MINOR version.
  • Use Case: Introducing new functionalities that do not break existing code.

Bug Fixes (fix):

  • Example: fix(s3): Fixing the versioning configuration
  • Impact: Resolves a bug while maintaining backward compatibility.
  • Version Bump: PATCH version.
  • Use Case: Fixing issues or bugs in the code that do not affect the API or functionality.

Breaking Changes (BREAKING_CHANGE):

  • Example: BREAKING_CHANGE: Refactoring policy writing
  • Impact: Introduces changes that are not backward-compatible.
  • Version Bump: MAJOR version.
  • Use Case: Making significant changes that alter the API or overall functionality, requiring adjustments in dependent code.

Chore (chore):

  • Example: chore(docs): Updating the docs
  • Impact: Addresses maintenance tasks or housekeeping activities without directly impacting features or bug fixes.
  • Version Bump: Depends on the project’s versioning strategy. Usually, it triggers a PATCH version bump if it includes only non-functional changes.
  • Use Case: Performing administrative or maintenance tasks such as updating dependencies, refactoring code for better readability, optimizing build configurations, or cleaning up unused code. These changes improve code quality and project organization without introducing new features or fixing specific bugs.

Wrapping Up

With GitHub Actions, Semantic Releases, and Terraform Docs, you can automate the versioning and documentation of your Terraform modules, making your workflow more efficient and reliable.

Your team will appreciate the consistency and ease of use, allowing them to focus on what really matters: building great infrastructure.

If you have any questions about the code, feel free to explore the complete version available in this repository.

Thank you for reading this post! If you have any questions or would like to connect, feel free to reach out to me on LinkedIn. I look forward to hearing from you!

#Terraform #DevOps #CloudComputing #InfrastructureAsCode #AWS #GitHubActions #ContinuousIntegration #Automation #TechTips #DeveloperTools #VersionControl

--

--

Luiz André Nunes da Silva

Senior Site Reliability Engineer at @PicPay | DevOps | Terraform | Kubernetes | Atlantis | Python | Javascript