Terraform added the replace_triggered_by lifecycle argument in version 1.2. This argument allows you to replace a resource when another resource changes.
You probably wonder why you need this – and you’re right in doing so. Typically, Terraforms default dependency management works flawlessly. Sometimes, however, you might want to replace your VM, because even though you can update the start up script without replacement, you want a new instance for your own sanity. This blog shows how to get that done using the replace_triggered_by
lifecycle argument.
Understanding the replace_triggered_by lifecycle argument
The replace_triggered_by lifecycle argument references managed resources and/or attributes. When the referenced resource or attribute changes, the resource is replaced. In the example below, any change to the foo_value.foo
resource replaces the foo_value.bar
resource.
resource "foo_value" "foo" {
value = "foo"
}
resource "foo_value" "bar" {
value = "bar"
lifecycle {
replace_triggered_by = [
foo_value.foo,
]
}
}
Please note that variables, data sources and modules are not supported, because the feature relies on planned actions for resource addresses. If needed, you could use the terraform_data resource to add resource-like behavior. In the example below, the terraform_data.bar
resource is replaced when the local_file.foo
content changes by leveraging the terraform_data.local_file_foo
resource.
terraform {
required_version = "~> 1.4.0"
}
data "local_file" "foo" {
filename = "./foo.txt"
}
resource "terraform_data" "local_file_foo" {
input = data.local_file.foo.content_sha256
}
resource "terraform_data" "bar" {
input = "bar"
lifecycle {
replace_triggered_by = [
terraform_data.local_file_foo,
]
}
}
Find additional examples and contribute your use case to our GitHub repository.
Replacing a VM when the startup script changes
The Google Compute Instance resource has a special argument: metadata_startup_script
. When changing this value, the VM is replaced. Sadly, this feature is only implemented for the startup script, whereas Windows VMs also have a sysprep script. The next example uses replace_triggered_by
to replace a VM when the sysprep or startup script changes.
terraform {
required_version = "~> 1.2.0"
}
resource "google_compute_instance" "my_vm" {
name = "my-vm"
boot_disk {
initialize_params {
image = "projects/windows-cloud/global/images/family/windows-2022"
}
}
metadata = {
"sysprep-specialize-script-ps1" = file("${path.module}/assets/task_runner_sysprep.ps1")
"windows-startup-script-ps1" = file("${path.module}/assets/task_runner_startup.ps1")
}
lifecycle {
replace_triggered_by = [
null_resource.my_vm_replacement_trigger,
]
}
}
resource "null_resource" "my_vm_replacement_trigger" {
triggers = {
"startup_script" = filesha256("${path.module}/assets/task_runner_startup.ps1")
"sysprep_script" = filesha256("${path.module}/assets/task_runner_sysprep.ps1")
}
}
Conclusion
Terraforms builtin dependency management typically works flawlessly. Sometimes, however, you want to force resource replacement based on other changes. If so, the replace_triggered_by lifecycle argument is there to help you.