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

Support for reasoning about references in the plan file #30826

Open
kyorav opened this issue Apr 10, 2022 · 2 comments
Open

Support for reasoning about references in the plan file #30826

kyorav opened this issue Apr 10, 2022 · 2 comments
Labels
enhancement new new issue not yet triaged unknown-values Issues related to Terraform's treatment of unknown values

Comments

@kyorav
Copy link

kyorav commented Apr 10, 2022

Current Terraform Version

Terraform v1.1.7
on windows_amd64

Use-cases

I am writing policies to enforce network security and compliance requirements, and I would like to be able to enforce my policies on IaC prior to deployment.
My rules often require resolving references between related networking elements. For example, the rule "VPC flow logging should be enabled" requires examining the "vpc_id" field of all Flow-Log elements to make sure that one of them is pointing to the VPC that I am checking. The problem is that since the id of the vpc is a computed property the value for the target of the flow_log does not appear in the "planned_values" section of the plan file. For simple examples I can get the information from the "configuration" section of the plan file, but when the HCL code uses "for_each" things get more complicated. Here is an example using the IBM provider, but the exact same problem exists with the AWS provider and others.

resource "ibm_is_vpc" "my_vpc" {
  for_each = toset( ["one", "two"] )
  name = "my-vpc-${each.value}"
}

resource ibm_is_flow_log my_flow_log {
  for_each = toset( ["one", "two"] )
  name = "my-flow-log"
  target = ibm_is_vpc.my_vpc[each.value].id
  storage_bucket = "my_bucket_name"
}

In this example each vpc has a flow_log pointing to it and I want to be able to write a policy that can identify that, which means I need to identify that the value of the "target" property of ibm_is_flow_log.my_flow_log["one"] is "ibm_is_vpc.my_vpc["one"].id". However the "target" property doesn't show up at all in the "planned_values" section, while the "for_each" construct is not unrolled in the configuration section.

A similar problem will happen if the flow-log element is defined inside a module and the id of a vpc is passed through an input variable. In this case the configuration section will just give me the name of the input variable because computed values are not propegated in the configuration section.

Let me stress that this is not a problem limited to my work or the IBM provider. I have examined open source policy libraries with policies over AWS and other providers, and found similar issues with the usage of for_each. I would also note that this affects a very important class of policies -- any policy that reasons about the relationship between linked resources.

Attempted Solutions

Currently I pre-process the plan file to find references in the "configuration" section and add the "target" field to the resource definition in the "planned_values" section. This works fine when there is no usage of "for_each" or module input/output variables. For the above example without the for_each, I will find the value "ibm_is_vpc.my_vpc.id" under expressions.target.references[0] in the "configuration" section and set it as the target of my_flow_log. My policy can easily extract the name of the vpc from this value.

Proposal

I can think of two possible enhancements that would support writing policies that depend on object references, although I am sure there must be other options.

(1) Add references to the "planned_values" section. That is, propagate terraform id values in the "planned_values" section even though they are technically computed values. They should be set with some discerning syntax so it is clear that these are references and not constant values.

(2) Unwind "for_each" in the configuration section and propagate terraform id values through input and output variables. I will then be able to find references in the configuration section for my policy to use.

References

I found one possibly related issue:

@kyorav kyorav added enhancement new new issue not yet triaged labels Apr 10, 2022
@apparentlymart
Copy link
Contributor

Hi @kyorav! Thanks for reporting this.

The problem statement you made here makes sense to me and I would like to support policies like this.

I think the challenge is in the design of the mechanisms to support it, since today there are two things in direct conflict with the two improvements you suggested:

  • Expansion of a resource into its constituent instances (using count or for_each is something that happens during the plan phase, and so that information is literally not available in the configuration.
  • Conversely, creating a plan for a resource instance discards all of the references in favor of the resulting value, and so even Terraform itself doesn't retain information about what a particular value in the plan was derived from.

I don't think either of these is insurmountable but neither is straightforward either.

I think there might be some other information we could add to the JSON output to help with the sort of cross-referencing you want to do, though. For example, Terraform could in principle attempt to use static or dynamic analysis to understand which resources contributed to a particular unknown value even indirectly, and report those despite them not appearing directly in the expressions. References are always statically known and so static analysis should be possible in principle but we'd need to try it to see if it would be too conservative and thus report "too much" to be useful for the sort of policy you want to write.

Another angle here is that in v1.2 we are planning to add features to declare preconditions for resources, which will allow declaring a rule like this directly inside the Terraform configuration in principle. That feature is aimed more at verifying assumptions that are important for correctness rather than for policy -- that is, more in the category of "this wouldn't work" rather than "this would work but violates an organizational policy" -- so I don't know if that would be an appropriate answer here, but might be if you would consider this rule to be a crucial part of the abstraction the module is providing, rather than a rule imposed broadly and externally across all modules using this resource type.

@kyorav
Copy link
Author

kyorav commented Apr 21, 2022

Unfortunately the resource precondition option will not help here, since policies must be independent of the infrastructure definition.
It seems to me the easiest route is to identify references in the parse phase and then modify the plan generation logic so that it retains information about references. But since I am not familiar with the internals of how this logic works I get that it might be more complex than I imagine.

A cleaner solution might be to add a new type of property in the schema to mark properties that are references, and these would get the special treatment. These properties will have both a computed value (i.e. value known only after deployment) and a planned value (i.e. available in the planned_values section).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement new new issue not yet triaged unknown-values Issues related to Terraform's treatment of unknown values
Projects
None yet
Development

No branches or pull requests

2 participants