Skip to content
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

Ensure WriteOnly attributes are rejected as non-ephemeral output values #36171

Closed
wants to merge 15 commits into from

Conversation

radeksimko
Copy link
Member

@radeksimko radeksimko commented Dec 6, 2024

Previously the following configuration would pass validation. The value was still null so it wasn't a big deal from the perspective of broken promises but it would be confusing for the user.

This PR makes it so it is rejected the same way as other existing ephemeral values.

resource "terraform_example" "test" {
  foo     = "foo"
  wo_attr = "noot"
}

output "name" {
  value = terraform_example.test.wo_attr
}

This builds on top of #36031

Copy link
Contributor

The equivalence tests will be updated. Please verify the changes here.

@jbardin
Copy link
Member

jbardin commented Dec 10, 2024

Note that this falls into the same trap as trying to validate deprecated attributes and outputs (#36016 (comment)), a large portion of attribute use is indirect and won't be detected, or will cause false positives.

@radeksimko
Copy link
Member Author

a large portion of attribute use is indirect and won't be detected, or will cause false positives.

That explains why in the current implementation the diagnostics only come up later during plan rather than during validation phase. I'm not sure it's the same story as deprecation, which is driven entirely by schema, though?

I mean, ephemerality is driven through marks and marks get assigned to values, specifically only the values which are in fact ephemeral.

IMHO lack of detection in some cases would be fine but of course false positives would not. Can you think of some specific examples of false positives in the context of WriteOnly attributes?

@jbardin
Copy link
Member

jbardin commented Dec 10, 2024

Write-only is also only determined by the schema, just like deprecated. Parts of this PR are searching for ephemeral marks in changes, but ephemeral marks cannot make it all the way through to the plan at all (and ephemeral marks do not correspond 1-1 with write-only attributes). The only place an ephemeral value can be assigned within a resource's config is to a write-only attribute, but that is write-only, and cannot pass through any value to the state or plan. The result of evaluating a write-only attribute is always null.

The failure mode is the same as in the comment I linked. The result of evaluating any reference to something which can have multiple instances is the entire container, so a reference like instance_type.foo[0].write_only can't be detected because the evaluated reference is only resource_type.foo. You can see this more easily if you assign the resource value indirectly:

resource "instance_type" "foo" {
  write_only = "test"
}

locals {
  foo = instance_type.foo
}

output "out" {
  value = local.foo.write_only
}

The output value expression here is just calling GetAttr on an arbitrary object, it has no concept that it represents a resource (because it may not actually be a resource), and there is no way to reference the originating schema. Attempts to track the the use of a specific attribute like write_only either return false positives because they fall into a container of some sort but are never assigned directly, or they miss a large portion of assignments like the output above.

@radeksimko radeksimko force-pushed the f-builtin-write-only-attr branch from 14c36f3 to da75375 Compare December 11, 2024 15:10
Copy link
Contributor

The equivalence tests will be updated. Please verify the changes here.

@radeksimko
Copy link
Member Author

Using that example above, I can confirm that the current implementation does validate it as expected, as far as I can tell:

resource "terraform_example" "test" {
  foo     = "foo"
  wo_attr = "noot"
}

locals {
  foo = terraform_example.test
}

output "out" {
  value = local.foo.wo_attr
}
terraform plan    

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  + create

Terraform planned the following actions, but then encountered a problem:

  # terraform_example.test will be created
  + resource "terraform_example" "test" {
      + foo = "foo"
    }

Plan: 1 to add, 0 to change, 0 to destroy.
╷
│ Error: Ephemeral value not allowed
│ 
│   on main.tf line 15, in output "out":
│   15:   value = local.foo
│ 
│ This output value is not declared as returning an ephemeral value, so it cannot be set to a result derived from an ephemeral value.
╵

I also added a commit that modifies the builtin terraform provider to enable the above.

Again - I can understand this won't cover 100% of all cases and cannot be relied on but I'm still struggling to understand how it could treat valid configuration as invalid?

ephemeral marks do not correspond 1-1 with write-only attributes

I'd be fine with creating a dedicated WriteOnly mark if we deem it necessary but I take that more as an implementation detail compared to the rest.

The only place an ephemeral value can be assigned within a resource's config is to a write-only attribute, but that is write-only, and cannot pass through any value to the state or plan. The result of evaluating a write-only attribute is always null.

That is true but I do not see how that's in conflict with anything. I mean, marks can still be assigned and passed around along with a null value, can they not?

Base automatically changed from TF-18617 to main December 12, 2024 14:59
@radeksimko radeksimko closed this Dec 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants