Skip to content

Versioning the Templates

Workload teams must decide when they upgrade to a new version of a module. Therefore the library is versioned, and every release is a tag that a team can pin. The decision is in Decision summaries.

One version per module

Each module has its own version line, with semantic versioning (MAJOR.MINOR.PATCH). A git tag holds the technology, the module name and the version, for example bicep/storage_account/1.0.1 or terraform/storage_account/1.1.0.

Option Why it was not chosen
One version for the whole solution The number is misleading for a team that uses only some modules, or only Bicep or only Terraform.
One version per technology (Bicep, Terraform) Still misleading: a change in one module bumps the version of all.
One version per module Chosen. The number says something about the module a team really uses. The release process is more complex.

The strategy was chosen with limited experience of mixed Terraform and Bicep libraries in one repository, and it is tested in practice.

Tags

Technology Git tag How a consumer refers to it
Terraform terraform/<module>/<version> The full tag name as ref in the module source
Bicep bicep/<module>/<version> Only the version number, in the registry reference .../modules/bicep/<module>:<version>

Both ways are shown in Reference a module. The tags are listed in the repository, and in the read-only copy of the consumer. To read the documentation of a version, switch to its tag first.

Who changes which number

The process is semi-automatic:

Part Who changes it How
MAJOR and MINOR The contributor By hand, in the version.json file of the module
PATCH The release pipeline Automatic: the pipeline sets the number and creates the tag when required

The version.json file has two properties: version (the MAJOR.MINOR number) and pathFilters (the files that count). The version of a module changes when a file that the filter selects changes in the merged commit, or when version.json itself changes. A change outside the module (for example in the pipelines) does not bump it. Documentation-only changes do not either.

Technology Files that trigger a release
Terraform The .tf files directly in the module folder (not in subfolders), and version.json
Bicep The compiled main.json, and version.json. The main.json is generated from main.bicep by the documentation job, so a change of the Bicep code reaches a release through it.

The PATCH number is calculated: it starts at 0 when MAJOR.MINOR changed, and otherwise it is the highest existing patch number for that MAJOR.MINOR plus one. A tag that already exists for the commit is not created again.

The pipeline must run from the main branch, and changes must reach main through a squash commit, so that the changes since the previous commit can be read as one change (Release and documentation).

When to change what

Change Number
A fix that does not change the interface PATCH (automatic)
A new optional parameter or output MINOR
A change that breaks existing calls: a removed or renamed parameter, a changed default that changes the behaviour, a new fixed value that is not compatible MAJOR

This table applies the rules of semantic versioning to a module. The same principles as for the platform itself apply: announce a change of a default before you make it (Backwards compatibility).

Changes that were a MAJOR change in practice:

  • A setting that a policy requires became enforced, so its parameter was removed (for example enableAllLogs in diagnostics).
  • A default changed to the compliant value (for example public network access of the Bicep storage account module).
  • A module switched to another provider resource, or changed the shape of an object parameter.

The changelog

Each module has its own changelog file (CHANGELOG.md in the module folder), so a team can see what changed between two versions without reading the whole history of the repository. A line in the changelog is part of every change that changes the version. An entry has the version, a link that compares it with the previous tag, the date, and the headings Features and Bug Fixes. A Known limitations heading is used when a release has a caveat the team should know before upgrading, for example a provider that needs a second run after the update. The pull request template of the repository asks for the changelog, for the version file when MAJOR or MINOR changes, and for tests of a new module.

Upgrade notes

A module with a breaking change can have upgrade notes in its upgrade/ folder, one file per major version (notes-v<major>.md). They list the steps for your code, and for the Terraform state where needed. Read them before you change the tag, and test the upgrade in dev first.

Module Technology Major version What changes for you
storage_account Terraform 1 Object parameters for queue properties and static websites become maps. The single Data Lake Gen2 file system becomes a map of file systems.
storage_account Bicep 1 Public network access is disabled by default. Network rules default to deny, with the Azure services bypass. Set publicNetworkAccess explicitly when you relied on the old behaviour.
sql_server Bicep 1 Availability zone values become integers. The enableAllLogs and enableAllMetrics diagnostic properties are removed. The key parameters become a customerManagedKey object. The identity parameter is renamed. The system-assigned identity property is removed because it is enforced.
container_registry Terraform 1 retention_policy becomes retention_policy_in_days. The enable_allLogs and enable_allMetrics diagnostic properties are removed.
network_security_group Terraform and Bicep 1 The flow log parameter is removed, because flow logs are no longer supported on this level. In Terraform the diagnostic setting parameter is renamed, and a plan shows the move. In Bicep the log categories are enforced and the parameter is removed.
postgresql_flexibleserver Terraform 1 The customer-managed key object is replaced by key and identity IDs, including those for the geo backup.
container_app Terraform 1 The module moves from azurerm_container_app to the azapi provider. Declare azapi, review the state moves in the plan, rename some variables (timeouts, custom domains, IP restrictions).
container_app Terraform 2 The module owns its resource logic instead of wrapping another module. Inputs and outputs are unchanged. The plan shows moves, and the first apply can fail with an inconsistent result error that a second apply resolves.

The table is a summary. The notes in the module folder are the source.

What a consuming team does

The team pins a tag in the module call, and moves the tag when it has read the changelog and tested the new version (Use Managed Templates). The module also declares the provider versions it supports: a module that still limits a provider to an older major version blocks an upgrade of the provider in the team's own code (Provider versions). A bot can propose new versions for you (Keep modules up to date).