Change Detection β
When changing your infrastructure (made up of a set of stacks) it's common to make several changes to several stacks. But now that you have multiple terraform states (per stack), how to apply the changes only to the affected resources? Keep in mind that we don't want to just blindly execute plan/apply to the unchanged stacks to reduce the blast radius.
We solve that by leveraging the power of the VCS (Version Control System) already in place. At the moment, Terramate only supports git
but other VCSs can be added in the future.
The approach is as simple as computing the changed stacks from the changed files discovered by the git diff
between the revision of the last terraform applied
change (ie. the released revision) and the current change.
For the sake of clarity, we'll refer to the released revision as baseref
, which is an abbreviation for base reference
. Usually, this term corresponds to the default branch (origin/main
or origin/default
).
By default the baseref
can have two values, depending on if you're in the default branch or in a feature branch, and they are:
origin/main
: if you're in a feature branch.HEAD^
: if you're in the default branch.
The HEAD^ syntax means the first parent of the HEAD
commit and the reasoning for using it for the default branch is that once you merged your PR you need to apply the changes in the CI or locally. If the project adopts a non-fast-forward merge strategy, every commitβaside from the first oneβon the default branch becomes a merge commit. Utilizing HEAD^
as the baseref
enables detection of modifications in the most recently merged code.
Having explained that, hopefully it becomes clear that change detection in Terramate works best if the project follows a git flow defined below (by the way, this is probably the most common git flow used by the git community):
- The default branch (commonly
main
) is considered to be the stable branch that represents the deployed state of your IaC. - Changes that should be planned and applied should be added through a feature or bugfix branch.
- The IaC project uses non fast-forwarded merge commits. (the default in GitHub and Bitbucket).
These are standard on most companies but the option 3 is controversial as it means flows depending on git rebase
would not work. If that's the case for your company, it will require a bit of manual work to apply the changes after merged but alternatively the terraform plan/apply can be run in the PR's branch just before merge using the default branch base ref (origin/main
).
The baseref
can be manually changed by the terramate command line at any given point in time using the --git-change-base
option or through the project configuration, so different strategies for computing the changes are supported.
If you you adopt the rebase merge strategy and need to apply modifications to stacks affected by the last rebase, it's crucial to first identify the base commit (the commit before the merge). You can then provide this commit hash in the --git-change-base
flag to accomplish the required changes.
$ git branch
main
$ git rev-parse HEAD
80e581a8ce8cc1394da48402cc68a1f47b3cc646
$ git pull origin main
...
$ terramate run --changed --git-change-base 80e581a8ce8cc1394da48402cc68a1f47b3cc646 \
-- terraform plan
--git-change-base
supports all git revision syntaxes, so if you know the number of parent commits you can use HEAD^n
or HEAD@{<query>}
, etc.
Module change detection β
A Terraform stack can be composed of multiple local modules and if that's the case then any changes on a module that a stack references will mark the stack as changed. The rationale is that if any module referenced by a stack changed then the stack itself changed and needs to be re-deployed.
For more details see the example below:
In order to do that, Terramate will parse all .tf
files inside the stack and check if the local modules it depends on have changed.
Arbitrary files change detection β
The stack can specify a list of files which will mark the stack as changed if they change.
Example:
stack {
watch = [
"/external/file1.txt",
"/external/file2.txt"
]
}
Then even if the stack code didn't change but any of the watched files changed, then the stack will be marked as changed.
This feature is useful if you need to integrate Terramate with other tools (eg.: Terragrunt) so you can detect when dependent code outside the scope of Terramate changed.