This is Part 5 of my series on Production-Grade Terraform Patterns. I have split my repositories, enforced versioning, automated releases, and set up my consumption model.
In a traditional setup, upgrading infrastructure is painful. A developer releases vpc-v1.1.0, sends a Slack message, and… silence. Three months later, Prod is still on vpc-v0.9.0.
To build a robust infrastructure, upgrades should be pushed to the consumer, not pulled.
I achieve this with Renovate Bot.
The Workflow #
- Release: Release Please tags
vpc-v1.1.0in your Modules Repo. - Detection: Renovate scans your Live Repo, sees
vpc-v1.0.0, and detects the new tag. - Proposal: Renovate opens a Pull Request:
chore(deps): update module vpc to vpc-v1.1.0. - Validation: CI triggers
terragrunt planon that PR. - Merge: You review the plan and merge.
A real Renovate pull request looks like this in GitHub:
Configuring Renovate for Terragrunt #
Renovate is ideal because it has a dedicated Terragrunt manager. It parses HCL and finds underlying versions.
Here is the renovate.json from my live repo, terraform-patterns-live (source on GitHub):
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":dependencyDashboard"
],
"enabledManagers": [
"terragrunt",
"terraform"
],
"terragrunt": {
"managerFilePatterns": [
"/\\.hcl$/"
],
"versioning": "regex:^((?<compatibility>.*)-v|v*)(?<major>\\d+)\\.(?<minor>\\d+)\\.(?<patch>\\d+)$"
},
"packageRules": [
{
"matchDatasources": [
"git-tags",
"github-tags",
"terraform-module"
],
"groupName": "infrastructure modules",
"matchPackageNames": [
"/terraform-patterns-modules/"
]
},
{
"matchFileNames": [
"**/development-account/**"
],
"automerge": true
},
{
"matchFileNames": [
"**/production-account/**"
],
"minimumReleaseAge": "7 days"
}
]
}
config:recommendedplus:dependencyDashboardgives sensible defaults and a single GitHub Issue that lists pending updates.terragrunt.managerFilePatternslimits scanning to.hclfiles; theversioningregex matches tags likevpc-v1.2.0from this series (compatibility prefix + semver).packageRules: module updates underterraform-patterns-modulesare grouped as “infrastructure modules”; paths underdevelopment-accountmay automerge when CI passes;production-accountchanges wait 7 days after release before Renovate proposes them (minimumReleaseAge).
The Dependency Dashboard #
The ":dependencyDashboard" preset prevents PR noise. Renovate creates a single GitHub Issue listing all available updates. You verify them there before checking a box to generate the actual PR.
Validation Pipeline (CI) #
A PR updating a version number is useless if you don’t know what it changes.
I need a CI workflow (.github/workflows/plan.yaml) that runs terragrunt run-all plan on every PR.
name: Terragrunt Plan
on: [pull_request]
jobs:
plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: hashicorp/setup-terraform@v2
- uses: autero1/[email protected]
- name: Terragrunt Plan
run: |
terragrunt run-all plan --terragrunt-non-interactive -out=tfplan.binary > plan.txt
continue-on-error: true
# Step to post plan.txt as a PR comment (using github-script)
Deployment strategy: dev vs prod #
You rarely want production to move at the same pace as development. In the config above, matchFileNames ties behavior to your repo layout: anything under development-account can automerge after CI; anything under production-account must pass a 7-day minimumReleaseAge before Renovate opens a PR, which gives a soak period on new module releases.
Adjust the path globs to match your own account or folder names. The idea is the same: fast feedback in lower environments, slower, explicit promotion toward production.
Moving to execution #
You now have a pipeline that proposes updates automatically. But who approves and applies them?
If you merge a PR, does a human run terraform apply? In Part 6, I will introduce TACOS (Atlantis, Digger) to automate the final mile of execution safely.