-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't declare empty lists of nested blocks in 0.12 SDK #20505
Comments
Hi @rileykarson! Thanks for creating an issue for this. One of the assumptions we made in shimming the old provider schema model to the new config model was that if an attribute has We usually use singular names for these because we want users to think of it as a bunch of separate objects, rather than as a single collection. We do still need to gather them into a single collection to pass to the provider, but that was intended as part of the provider's internal model, not as part of the user-facing model. The idea of using I notice that this particular cidr_blocks {
cidr_block = "10.1.0.0/16"
}
cidr_blocks {
cidr_block = "10.1.0.0/16"
} It'd read better if it appeared as a single attribute accepting a list of objects instead, so I assume this is what was intended: cidr_blocks = [
{
cidr_block = "10.1.0.0/16"
},
{
cidr_block = "10.1.0.0/16"
},
] ...which then has a more obvious reduction down into an empty list as in the example you gave, and makes more sense as syntax for a construct with a plural name. With all of this said, a solution I'm considering here is a new field in Does that seem reasonable? Are there other similar examples in the Google Cloud Platform provider that we could evaluate this solution against? |
I'm curious how this differs from The Taking a quick trawl through the provider, we've created an That includes;
The only difference between these fields and a field like |
The intent of the block syntax is that users should feel like the blocks are distinct from one another. We collect them together into some sort of collection just as a practical matter to pass them to providers in a reasonable way and to make them usable in expressions. Naturally sometimes the implementation leaks into the interface in unintended ways, such as when I think most other examples I'm familiar with in the Google Cloud Platform provider do match this user model, even though behind the scenes they are being mapped to collections for sending to the API. Taking network_interface {
network = "default"
} In the user's model, each While this is of course subjective, we think it tends to be confusing if the absense of any blocks of a particular type causes the provider to implicitly generate blocks but yet the first block added makes all of the default ones "vanish"; adding a new object should not lead to the distruction of existing objects. The attribute syntax makes that situation easier to understand because it more clearly distinguishes between unset, set to empty, and set to non-empty, and makes it clear to the user that these objects are collected together into a single value and so may affect one another. The existing Let's see how those other examples would look if we were to switch them over to using attribute syntax with object elements: # explicitly disable the creation of the default rule
rule = []
# set one or more rules explicitly, disabling the creation of the default rule
rule = [
{
action = "allow"
priority = 1
match = [{
config = [{
src_ip_ranges = ["10.1.0.0/16"]
}]
versioned_expr = "SRC_IPS_V1"
}]
}
] This one looks pretty clumsy as a list-of-objects value rather than as nested blocks 😖 ... the fact that there are more blocks nested inside, combined with the fact that the SDK provides no way to say that we just want the block as a single object rather than as a list/set, leads to the nested blocks looking particularly odd. So I think this one would require some further tweaking if we decided to take this approach with it. # Explicitly disable the four access rules created by default
access = []
# Disable the four access rules created by default and create these instead...
access = [
{
role = "OWNER"
view = [{
project_id = "a"
dataset_id = "b"
table_id = "c"
}]
}
] Again the nested block doesn't translate very well to this syntax, but otherwise it doesn't seem too bad. I'm wondering if we could extend this idea by also offering a way in the SDK to explicitly ask for a nested block to be represented as a single object rather than as a list/set of objects. This is a harder one to retrofit because it would also mean that any references to attributes in those objects elsewhere would need to omit the From some cursory initial investigation though, I think that is possible in principle... we'd leave it set as If we were able to get that working, these new examples could look more like this: rule = [
{
action = "allow"
priority = 1
match = {
config = {
src_ip_ranges = ["10.1.0.0/16"]
}
versioned_expr = "SRC_IPS_V1"
}
}
] access = [
{
role = "OWNER"
view = {
project_id = "a"
dataset_id = "b"
table_id = "c"
}
}
] Still quite a big change in appearance from how I assume these tend to look in most 0.11 configs, but at least it doesn't include any confusing additional punctuation. Let's mull this over a bit more today. So far this feels like the most compelling path in terms of minimizing the risk/impact to other parts of this provider and other providers and ensuring that things continue to work as before for 0.11 users, but perhaps someone else (or me later, with fresher eyes) will find a different angle that seems better. |
I don't think the expectations for the lifecycle of these blocks is any different than for I'm not sure what about the relationship between these sets of blocks would be considered distinguishably different; cidr_blocks {
cidr_block = "10.1.0.0/16"
}
cidr_blocks {
cidr_block = "10.1.0.0/16"
} network_interface {
subnetwork = "projects/my-project/region/us-central1/subnetworks/my-subnetwork"
alias_ip_range {
ip_cidr_range = "10.1.0.0/16"
}
}
network_interface {
subnetwork = "projects/my-project/region/us-central1/subnetworks/my-subnetwork"
alias_ip_range {
ip_cidr_range = "10.1.0.0/16"
}
} |
Indeed all of these are naturally modeled as blocks except for the strange requirement of treating unset differently from zero. I was just trying to give some context on how Terraform distinguishes between nested blocks and attributes, and why modelling these ones as attributes is the most straightforward way to support distinguishing unset from (The fact that |
We aren't ready to release a `0.12` SDK based build because of these issues: • hashicorp/terraform#20505 • hashicorp/terraform#20507 • hashicorp/terraform#20506 • hashicorp/terraform#20504
We aren't ready to release a `0.12` SDK based build because of these issues: • hashicorp/terraform#20505 • hashicorp/terraform#20507 • hashicorp/terraform#20506 • hashicorp/terraform#20504
…e150daa0 Required for new `ConfigMode` schema parameter. References: * hashicorp/terraform#20505 * hashicorp/terraform#20626 Updated via: ``` go get github.com/hashicorp/terraform@44702fa6c163391fe7295b6004a06cefe150daa0 go mod tidy go mod vendor ```
…ess configuration block attributes References: * hashicorp/terraform#20505 * hashicorp/terraform#20626 In Terraform 0.12, behaviors with configuration blocks are more explicit to allow configuration language improvements and remove ambiguities that required various undocumented workarounds in Terraform 0.11 and prior. As a consequence, configuration blocks that are marked `Optional: true` and `Computed: true` will no longer support explicitly zero-ing out the configuration without special implementation. The `ConfigMode` schema parameter on the configuration block attribute allows the schema to override certain behaviors. In particular, setting `ConfigMode: schema.SchemaConfigModeAttr` will allow practitioners to continue setting the configuration block attribute to an empty list (in the semantic sense, e.g. `attr = []`), keeping this prior behavior in Terraform 0.12. This parameter does not have any effect with Terraform 0.11. This solution may be temporary or revisited in the future. Previous output from Terraform 0.11: ``` --- PASS: TestAccAWSNetworkAcl_Egress_ConfigMode (53.47s) --- PASS: TestAccAWSNetworkAcl_Ingress_ConfigMode (51.81s) ``` Previous output from Terraform 0.12: ``` --- FAIL: TestAccAWSNetworkAcl_Egress_ConfigMode (38.74s) testing.go:568: Step 4 error: config is invalid: Unsupported argument: An argument named "egress" is not expected here. Did you mean "ingress"? --- FAIL: TestAccAWSNetworkAcl_Ingress_ConfigMode (38.89s) testing.go:568: Step 4 error: config is invalid: Unsupported argument: An argument named "ingress" is not expected here. Did you mean to define a block of type "ingress"? ``` Output from Terraform 0.11: ``` --- PASS: TestAccAWSNetworkAcl_Egress_ConfigMode (52.88s) --- PASS: TestAccAWSNetworkAcl_Ingress_ConfigMode (52.70s) ``` Output from Terraform 0.12: ``` --- PASS: TestAccAWSNetworkAcl_Egress_ConfigMode (52.43s) --- PASS: TestAccAWSNetworkAcl_Ingress_ConfigMode (53.60s) ```
…ngress configuration block attributes References: * hashicorp/terraform#20505 * hashicorp/terraform#20626 In Terraform 0.12, behaviors with configuration blocks are more explicit to allow configuration language improvements and remove ambiguities that required various undocumented workarounds in Terraform 0.11 and prior. As a consequence, configuration blocks that are marked `Optional: true` and `Computed: true` will no longer support explicitly zero-ing out the configuration without special implementation. The `ConfigMode` schema parameter on the configuration block attribute allows the schema to override certain behaviors. In particular, setting `ConfigMode: schema.SchemaConfigModeAttr` will allow practitioners to continue setting the configuration block attribute to an empty list (in the semantic sense, e.g. `attr = []`), keeping this prior behavior in Terraform 0.12. This parameter does not have any effect with Terraform 0.11. This solution may be temporary or revisited in the future. Previous output from Terraform 0.11: ``` --- PASS: TestAccAWSSecurityGroup_Egress_ConfigMode (50.28s) --- PASS: TestAccAWSSecurityGroup_Ingress_ConfigMode (49.85s) ``` Previous output from Terraform 0.12: ``` --- FAIL: TestAccAWSSecurityGroup_Egress_ConfigMode (31.74s) testing.go:568: Step 2 error: config is invalid: Unsupported argument: An argument named "egress" is not expected here. Did you mean to define a block of type "egress"? --- FAIL: TestAccAWSSecurityGroup_Ingress_ConfigMode (31.94s) testing.go:568: Step 2 error: config is invalid: Unsupported argument: An argument named "ingress" is not expected here. Did you mean to define a block of type "ingress"? ``` Output from Terraform 0.11: ``` --- PASS: TestAccAWSSecurityGroup_Egress_ConfigMode (52.27s) --- PASS: TestAccAWSSecurityGroup_Ingress_ConfigMode (53.55s) ``` Output from Terraform 0.12: ``` --- PASS: TestAccAWSSecurityGroup_Egress_ConfigMode (57.20s) --- PASS: TestAccAWSSecurityGroup_Ingress_ConfigMode (55.80s) ```
…ariable removal References: * hashicorp/terraform#20505 * #6427 The `environment` configuration block `environment_variable` configuration block came up in our `Optional: true` and `Computed: true` discovery as potentially problematic in Terraform 0.12 when the ability to use attribute syntax to zero out the configuration will require special implementation. However, it appears the functionality will actually continue to work due to the `environment` configuration block `Set` function and how updates of it are implemented. Here we still add the covering test for when the `environment` configuration block attribute is switched from `TypeSet` to `TypeList` during future simplification work. While it would probably be okay to just remove `Computed: true` now, we take the less risky approach of just leaving this as-is for now until that simplification work is completed. Output from acceptance testing: ``` --- PASS: TestAccAWSCodeBuildProject_Environment_EnvironmentVariable (37.91s) ```
…tion block attributes References: * hashicorp/terraform#20505 * hashicorp/terraform#20626 In Terraform 0.12, behaviors with configuration blocks are more explicit to allow configuration language improvements and remove ambiguities that required various undocumented workarounds in Terraform 0.11 and prior. As a consequence, configuration blocks that are marked `Optional: true` and `Computed: true` will no longer support explicitly zero-ing out the configuration without special implementation. The `ConfigMode` schema parameter on the configuration block attribute allows the schema to override certain behaviors. In particular, setting `ConfigMode: schema.SchemaConfigModeAttr` will allow practitioners to continue setting the configuration block attribute to an empty list (in the semantic sense, e.g. `attr = []`), keeping this prior behavior in Terraform 0.12. This parameter does not have any effect with Terraform 0.11. This solution may be temporary or revisited in the future. Previous output from Terraform 0.11: ``` --- PASS: TestAccAWSRouteTable_Route_ConfigMode (66.53s) ``` Previous output from Terraform 0.12: ``` --- FAIL: TestAccAWSRouteTable_Route_ConfigMode (36.11s) testing.go:568: Step 2 error: config is invalid: Unsupported argument: An argument named "route" is not expected here. Did you mean to define a block of type "route"? ``` Output from Terraform 0.11: ``` --- PASS: TestAccAWSRouteTable_Route_ConfigMode (67.27s) ``` Output from Terraform 0.12: ``` --- PASS: TestAccAWSRouteTable_Route_ConfigMode (69.03s) ```
…ion block attribute References: * hashicorp/terraform#20505 * hashicorp/terraform#20626 In Terraform 0.12, behaviors with configuration blocks are more explicit to allow configuration language improvements and remove ambiguities that required various undocumented workarounds in Terraform 0.11 and prior. As a consequence, configuration blocks that are marked `Optional: true` and `Computed: true` will no longer support explicitly zero-ing out the configuration without special implementation. The `ConfigMode` schema parameter on the configuration block attribute allows the schema to override certain behaviors. In particular, setting `ConfigMode: schema.SchemaConfigModeAttr` will allow practitioners to continue setting the configuration block attribute to an empty list (in the semantic sense, e.g. `attr = []`), keeping this prior behavior in Terraform 0.12. This parameter does not have any effect with Terraform 0.11. This solution may be temporary or revisited in the future. Previous output from Terraform 0.11: ``` --- PASS: TestAccAWSEMRCluster_Step_ConfigMode (828.01s) ``` Previous output from Terraform 0.12: ``` --- FAIL: TestAccAWSEMRCluster_Step_ConfigMode (390.69s) testing.go:568: Step 4 error: config is invalid: Unsupported argument: An argument named "step" is not expected here. Did you mean to define a block of type "step"? ``` Output from Terraform 0.11: ``` --- PASS: TestAccAWSEMRCluster_Step_ConfigMode (764.03s) ``` Output from Terraform 0.12: ``` --- PASS: TestAccAWSEMRCluster_Step_ConfigMode (833.49s) ```
The Core-side change for this was #20837, along with a few other follow-up improvements. This requires an opt-in on the provider side to consider a particular schema as being an attribute for configuration purposes, and then in turn a fixup step in Terraform Core makes it so the block syntax is still accepted for those. Work is underway to activate that opt-in mechanism where needed in the various providers, so I'm going to close this issue out for now and we'll track any further work on this mechanism in separate issues. |
Is there any guidance on what arguments/blocks will or will not be updated to use This change makes it impossible to write modules that work with both tf 0.12 and tf 0.11, where they were previously using the (apparently coincidental) feature where blocks worked as lists of maps. I was hoping to have a clean upgrade path, that looked something like:
However, now that seems plain impossible, as this attr/block change makes tf 0.12 more of an all-or-nothing upgrade. |
Indeed, it's only possible to write cross-compatible modules if they are not relying on undocumented gaps in the Terraform v0.11 language's validation. The best approach if you have a complex module that relies on such features is to create a new major version of the module and keep old configurations on the previous version until they are ready to use Terraform 0.12. |
I don't think we're talking about "complex" modules in any way. Can be a very simple module that is just exposing a variable to let the user define a block. That was the only way blocks could really be optional in tf 0.11. Worked great. Didn't realize it was a coincidence of implementation, or an "undocumented gap" in validation. |
I had absolutely the same error that author referring to and fortunately I was able find and fix it. In my case it was INCORRECT name of "variables.tf" file which was among my modules. Accidentally I misspelled it as "variables.ft". (the "tf" extenstion). So my suggestion is to doublecheck all project files for appropriate naming. |
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. |
Terraform Version
master
is in this state.Terraform Configuration Files
Specifically
Expected Behavior
Terraform should see that an empty list was defined, and I should not see an error.
Actual Behavior
Steps to Reproduce
Additional Context
This is important because of how
Optional
+Computed
interact. If I've defined list entries in my config and then remove them, Terraform will see no difference between my config and state; in effect, there's no way to remove every entry once any entry has been created.The text was updated successfully, but these errors were encountered: