Hi @monkelis0,
Terraform’s model is that each resource
block entirely manages the object it’s describing. There is no mechanism to override one module’s object from outside of that module.
However, since your a.tf
and b.tf
files seem to just be two files in the same module, and therefore considered by Terraform as a single unit, you could in principle use Override Files as @maxb suggested. I would recommend treating that as a last resort because Override Files are a historical feature we don’t typically recommend for new designs; it tends to make configuration hard to understand for future maintainers, and the merging behavior is complicated and somewhat arbitrary. We preserved that behavior in v0.12 for compatibility with prior versions but do not intend to invest in it further in future Terraform versions.
A more Terraform-idiomatic way to address the situation you described would be to factor out the parts you want to set elsewhere into either input variables (if you want to set them from outside the module) or local values (if you just need to set them elsewhere in the same module).
Here’s an example with local values.
First, an updated version of your a.tf
file:
resource "google_storage_bucket" "auto_expire" {
name = "auto-expiring-bucket"
location = "US"
force_destroy = true
uniform_bucket_level_access = local.uniform_bucket_level_access
lifecycle_rule {
condition {
age = local.lifecycle_age
}
action {
type = "Delete"
}
}
}
This includes references to local.uniform_bucket_level_access
and local.lifecycle_age
, which you can then declare and define in your b.tf
:
locals {
uniform_bucket_level_access = true
lifecycle_age = 100
}
The readability/maintainability benefits of this strategy compared to override files are:
- Each value is defined in only one place, rather than there being multiple definitions and requiring the reader to read carefully to understand which one takes effect.
- A reader looking at
resource "google_storage_bucket" "auto_expire"
can clearly see the references to local.uniform_bucket_level_access
and local.lifecycle_age
and know that they should look elsewhere in the module to find those definitions if they need to update those values, rather than trying to update the inline declarations and being confused that the change doesn’t take effect (because it’s shadowed by an override file).
Here’s an equivalent example with input variables, if the actual overridden value should be provided from outside of the module rather than inside it:
variable "uniform_bucket_level_access" {
type = bool
# this makes the variable optional to set
# without providing a default
default = null
}
variable "lifecycle_age" {
type = number
# this makes the variable optional to set
# and uses is default value when it isn't
default = 3
nullable = false
}
resource "google_storage_bucket" "auto-expire" {
name = "auto-expiring-bucket"
location = "US"
force_destroy = true
# NOTE: Setting a resource argument to null is exactly
# equivalent to not setting it at all, so this will behave
# as if this argument were not specified at all if
# var.uniform_bucket_level_access was not set.
# The provider itself will choose the default behavior
# in that case.
uniform_bucket_level_access = var.uniform_bucket_level_access
lifecycle_rule {
condition {
age = var.lifecycle_age
}
action {
type = "Delete"
}
}
}
Similar advantages over override files here too, but the additional capability here compared to the local values approach is that if this is a shared reusable module then each call to the module can potentially set a different value for each of the variables, allowing you to use the same module in different ways for different situations.