Skip to main content

Authoring Your First Patch

Prerequisites

  • Familiarity with Terraform or OpenTofu
  • Patcher installed either locally or as a GitHub Actions workflow in your repository

Overview

In this tutorial, we will guide you through the following steps:

Authoring a patch:

  • Identifying breaking changes in your module update and defining clear steps to resolve them.
  • Running patcher generate to template the patch
  • Completing the required patch fields
  • Modifying config.yaml to include the necessary configuration changes for your patch.

Test module

For this tutorial, we will use the patcher-test module from the gruntwork-io/terraform-aws-utilities repository. The module has a version v0.10.3, which we will intentionally mark as outdated in our infrastructure unit. We will then write a patch to upgrade the module for the v0.10.4 release. You can view the full, real-world example here, but we will walk through the steps to recreate it below.

Identifying the breaking change and its remediation steps

Suppose you need to add a new required variable to the patcher-test module. This change qualifies as a breaking change because consumers of your module must update their configurations to include the new variable. Without this update, OpenTofu will fail when planning or applying the infrastructure.

Add the new sampleinput variable to variables.tf:

<DIRECTORY>/variables.tf
variable "sampleinput" {
type = string
description = "Sample input for the module"
default = "unset-value"
}

Running patcher generate to template the patch

Next, run patcher generate from the root of the Git repository to generate the patch template, specifying the title of the patch:

$ patcher generate "Sample Breaking Change"

This command creates a templated patch in your repository at the path .patcher/patches/sample-breaking-change/patch.yaml:

.patcher/patches/sample-breaking-change/patch.yaml
name: "Sample Breaking Change"
description: <REPLACE_ME>
author: <REPLACE_ME>

# Optional list of dependencies that the patch requires.
dependencies:
- name: terrapatch
version: "0.1.0"

# List of steps that this patch will execute.
# Each step includes a `name` field (string) and a `run` field, which can specify either an OS command or an external script to execute.
# If you use external scripts, ensure they are located in the same directory as the `patch.yaml` file.
steps:
- name: <REPLACE_ME>
run: <REPLACE_ME>
- name: <REPLACE_ME>
run: <REPLACE_ME>

As the module maintainer, fill in the <REPLACE_ME> fields as needed:

  • description: Provide a full sentence that succinctly communicates the change.
  • author: Specify the author of the patch. This can be your name, an organization, or another appropriate identifier.
  • steps.name: Add a short, descriptive label for each step.
  • steps.run: Define the command to execute for that specific step.

Filling out the patch fields

Because this breaking change is straightforward, we can use terrapatch to perform the required step:

$ terrapatch add-module-argument $PATCHER_MODULE_ADDRESS sampleinput "\"samplevalue\""

$PATCHER_MODULE_ADDRESS gets populated when Patcher is run; it doesn't need to be set independently anywhere.

Once you have filled out the fields in the patch, it should look like this:

name: "Sample Breaking Change"
description: A sample breaking change that adds a new argument
author: Gruntwork

# Optional list of dependencies required for this patch.
dependencies:
- name: terrapatch
version: "0.1.0"

# List of steps that this patch will execute.
# Each step includes a `name` field (string) and a `run` field, which can specify either an OS command or an external script.
# If using external scripts, ensure they are located in the same directory as the `patch.yaml` file.
steps:
- name:
run: terrapatch add-module-argument $PATCHER_MODULE_ADDRESS sampleinput "\"samplevalue\""

Modifying config.yaml

Next, update the .patcher/config.yaml file to include the new patch in your repository. The config.yaml file acts as an index of patches for the repository. Patcher uses this file to quickly determine if dependencies need to be updated when evaluating a version bump.

The general structure of an entry in config.yaml is as follows:

  - tag: <VERSION>
patches:
- slug: "<PATCH_NAME_SLUG>"
modules_affected:
- <MODULE_NAME>

The following fields are defined as:

  • <VERSION>: The version of the module that introduces the breaking change.
  • <PATCH_NAME_SLUG>: The slug used as the directory name for the patch.
  • <MODULE_NAME>: The name of the module that includes the breaking change.

Once you have filled out these fields, the new entry in the config.yaml file will look like this:

  - tag: v0.10.4
patches:
- slug: "sample-breaking-change"
modules_affected:
- patcher-test

Include all changes to config.yaml, the new patch.yaml file, and the updates to the Terraform module in a single release. This is typically done in one pull request, though it is not a strict requirement.

Other Patcher users will receive these updates the next time they run patcher update, benefiting from the work completed here.

info

Testing patches using patcher is not currently supported. The recommended approach is to manually test the steps locally. Future updates to Patcher will introduce mechanisms to simplify testing new patches.