A workflow to automate module updates in terraform projects
Consumers should reference a stable major tag, not main.
- Recommended caller reference:
@v1 - Do not reference:
@main
- v1.x.y: no breaking changes to existing inputs, secrets or outputs
- v1 minor releases (x): additive only (new optional inputs/outputs, internal hardening)
- v1 patch releases (y): bug fixes and security fixes only
- v2.0.0+: allowed to introduce breaking contract changes
@v1tracks the latest compatible v1 release- You will receive additive improvements and fixes without contract-breaking changes
- Breaking contract changes will only be introduced in
@v2
- Default track:
@v1for automatic non-breaking updates - Strict pinning: use an exact version tag such as
@v1.1.1when change control is required - Maximum security: pin to a full commit SHA (e.g.
@abc1234...) to prevent any risk from tag mutation or force-pushes — recommended for production pipelines
Use this repository workflow as a reusable contract from another workflow.
For production use, pin to a full commit SHA. This is the most secure option as it is immutable and not affected by tag mutation or force-pushes:
uses: ONS/cloud_enablement/update-tf-modules/.github/workflows/update-tf-modules.yml@<full-commit-sha> # vX.Y.ZFor convenience, you can track a stable major tag instead. This will receive non-breaking updates automatically:
uses: ONS/cloud_enablement/update-tf-modules/.github/workflows/update-tf-modules.yml@v1- Consumer template:
examples/update-modules-manifest.example.yml - Repository runtime manifest:
.github/update-modules-manifest.yml
Use the example file as the starting point in your own repository, then set:
manifest_pathto your manifest locationterraform_rootto your Terraform root folder
Quick start for new consumers:
- Copy
examples/update-modules-manifest.example.ymlinto your repository (for example.github/update-modules-manifest.yml). - Replace placeholder module values (
repo,source_prefix,source) with your real module sources. - Call this reusable workflow and set
manifest_pathto that copied file.
This workflow currently has no required inputs.
manifest_path(string, default:.github/update-modules-manifest.yml): path to the updater manifest in the target repository.terraform_root(string, default:terraform): root folder to diff, format-check and include in PR paths.python_version(string, default:3.12): Python runtime used for the update tool.base_branch(string, default:main): PR base branch.pr_branch_name(string, default:automation/update-terraform-modules): branch name used for update commits.create_pr(boolean, default:true): whether to open a PR when changes are detected.
token(optional): token used for authenticated GitHub API calls and PR creation in the caller repository.
Token behavior:
- This repository is public, so source checkout does not require a secret.
- If
tokenis passed, it is used for GitHub API and PR operations. - If
tokenis not passed, the workflow falls back toGITHUB_TOKEN.
Permission behavior:
- To create PRs (
create_pr=true), the caller job token must havecontents: writeandpull-requests: write. - If
create_pr=false, PR permissions are not required.
changed("true"or"false"): whether Terraform files changed underterraform_root.pr_number(string, empty when not created): PR number when PR creation runs successfully.pr_url(string, empty when not created): PR URL when PR creation runs successfully.
Top-level requirements:
- The manifest must be a YAML mapping.
- It must contain a non-empty
moduleslist. - Each item in
modulesmust be a mapping.
Per-module requirements:
- Common required fields:
name(string)type(githuborregistry)
- Exactly one target selector must be provided:
glob(string)file(string)files(non-empty list of strings)
GitHub module (type: github) required fields:
repo(string, format likeowner/repo)source_prefix(string, should include?ref=suffix)
GitHub module optional fields:
lookup(releaseortag, default:release)pin(shaortag, default:sha)
Registry module (type: registry) required fields:
source(string, for exampleterraform-google-modules/network/google)
Path behavior:
glob,fileandfilespaths are resolved relative to repository root.
Pin to a full commit SHA for production use. Replace <full-commit-sha> with the SHA for the release you want to use.
name: Run shared module updater
on:
workflow_dispatch:
jobs:
update:
permissions:
contents: write
pull-requests: write
uses: ONS/cloud_enablement/update-tf-modules/.github/workflows/update-tf-modules.yml@<full-commit-sha> # vX.Y.Z
with:
manifest_path: .github/update-modules-manifest.yml
terraform_root: terraform
base_branch: main
create_pr: true
secrets:
token: ${{ secrets.UPDATE_MODULES_TOKEN }}
on-change:
if: ${{ needs.update.outputs.changed == 'true' }}
runs-on: ubuntu-latest
needs: update
steps:
- name: Print PR details
run: |
echo "PR number: ${{ needs.update.outputs.pr_number }}"
echo "PR URL: ${{ needs.update.outputs.pr_url }}"- If no files change under
terraform_root, the workflow succeeds withchanged=falseand PR creation is skipped. - If
create_pr=false, the workflow still runs updates and checks, but does not create a PR. - If Terraform formatting check fails when changes exist, the workflow fails.
- If PR creation fails (permissions, token scope, branch protection or API errors), the workflow fails.
If PR creation fails with a permission or token error, check the following.
-
Caller job permissions: Your job must declare the required permissions in its
permissionsblock:permissions: contents: write pull-requests: write
Without these, the workflow cannot create branches or PRs even if a token is supplied.
-
Token scope: If passing an explicit
tokensecret, ensure it has:- Read access to the caller repository (implicit for default GITHUB_TOKEN).
- Write access for contents and pull-requests (may require a Personal Access Token or GitHub App token with appropriate scopes).
-
Repository rules: Check that your caller repository does not have:
- Branch protection rules that block automated PRs or commits.
- Required status checks that cannot pass for automation-created branches.
- Repository-level branch creation restrictions.
-
Public source checkout: The updater source repository is public, so source checkout never requires a token. If the failure mentions source checkout, the issue is likely elsewhere in the pipeline.
If the error persists after checking the above, examine the full workflow run logs in the Actions tab for the exact GitHub API response.