I’ve seen a few reports of this around on github, but they reference using -target, whereas we are simply generating a plan, saving it to file, then applying it.
And a output, which either reads this module’s self_link, a pre-existing subnet that was created outside of this terraform’s self_link, or returns null if neither exists.
This all works fine, other than when the subnet is deleted, by disabling it via the config (setting enable_management_test to false), the output isn’t updated on the first run of the terraform. If the terraform is tearing down the subnet, the output remains after, whereas I expect it to be deleted? Running the terraform again will cause the output to be updated back to the ‘null’ value as expected.
For the subnets created outside of the terraform (and managed via the data import) the output updates as expected.
Submodule outputs are not stored in the state, so they must always be reevaluated, and hence are always going to be updated. I think the issue here more likely hides in how you are using the data – what is the "test" assignment doing exactly and is the data.google_compute_subnetwork.test_mgmt possibly returning the same value?
The two subnets can’t exist at the same time, they have the same name. This was done this way as sometimes we have manually created networks we can’t just get rid of to allow the terraform to built it’s replacement, so we have this option to enable/disable the building of it.
The problem isn’t that it isn’t updated, it’s that it’s not cleared when the subnet no longer exists. Terraform for sure knows the subnet no longer exists, as it’s terraform that’s torn it down, so why does the output continue to exist? (The data object at this point also wouldn’t exist).
Incase it helps, this is the data module: data "google_compute_subnetwork" "test_mgmt" { count = contains([for s in data.google_compute_subnetworks.test.subnetworks : basename(s.name)], "test-management-subnet") ? 1 : 0 project = local.project name = "test-management-subnet" region = local.region }
Maybe there’s a better way to test for the subnets existence, and then build the output, but I can’t figure one out.
Terraform can’t universally test for the existence of something (though some plural data sources have that ability, it seems testing might not even work in your case). Terraform is expected to be the source of truth for how the system should be configured, so in most cases you should tell terraform whether something exists or not.
I have a feeling this is a result of your data resource. If the subnets have the same name, and you are looking it up only by name within the same region and project, have you actually confirmed that the data source does not return the same value? This is easily tested by just removing the data resource from the "test" assignment, and putting some other sentinel value there
"test" = var.enable_management_test ? try(one(module.test_management_subnet[*].self_link)) : "enable_test_management was false"
Data sources are read during the planning process, so if the data source is able to lookup the test_management_subnet, it will return the value before the test_management_subnet is destroyed. That would explain why it remains on the first run, and updates with subsequent runs.
Thank you for explaining that, I’ve tested and you’re indeed correct. I figure I can get around this by checking if the management subnet is turned on in the terraform config, and if it’s not, only then do I rely on the output from the data module (as the only other source at this point would be the manually created subnet.
Unless you have a better / more terraform-y suggestion?
I’ve realised I don’t know how to get around this, please help?
The problem here is that there are two objects in terraform which represent the same real logical object, a google_compute_subnetwork, and you’re trying to switch between those representations while simultaneously altering (deleting in this case) the real object. I don’t know if there’s a great solution for that.
Something you can try, is to set a manual dependency between the data source and the module.
data "google_compute_subnetwork" "test_mgmt" {
...
depends_on = [module.test_management_subnet]
}
What this would do is make sure anytime there is a change in a manage resource within the test_management_subnet module, the google_compute_subnetwork.test_mgmt data source is deferred until apply, which will pick up the applied changes. It’s hard to say how well this will work in your real config, because it is going to delay producing certain knowable values until after apply as well.
It would be better to be able to tell the configuration explicitly where the test-management-subnet comes from, if it’s available at all. i.e. having multiple inputs, like var.manage-test-subnet to create the subnet, and var.read-test-subnet to read it via a data source. This way your above example could tell terraform that you want to both destroy the test-management-subnet while also not reading any data from the test-management-subnet.
Thank you for your time on this, it’s given me some idea of where to go with this.
We only need to check for the manually created one, if the terraform created one doesn’t exist. I can add this as a conditional for the data object!
count = (
!var.enable_management_test &&
contains([for s in data.google_compute_subnetworks.test.subnetworks : basename(s.name)], "test-management-subnet")
) ? 1 : 0
This way (if I’m correct, haha), the data object will only ever return if the terraform configured subnet isn’t enabled, and the manually built one is found.