-
Notifications
You must be signed in to change notification settings - Fork 380
Description
Introduction
TFLint cannot statically evaluate dynamic values like aws_instance.test.id
, so these values are ignored. Similarly, module outputs (e.g. module.aws_instance.id
) are also marked as dynamic values.
resource "aws_lb_target_group_attachment" "test" {
target_id = module.aws_instance.id # => ignored
}
However, module outputs are not always dynamic values. Modules that are used to share variables across multiple modules may have outputs that are statically determinable.
resource "aws_instance" "test" {
tags = module.shared.tags # => This is a static value, but it cannot be evaluated by TFLint.
}
Proposal
Add support for evaluating static module outputs.
Currently, all module.*
named values resolve to unknown values.
Line 214 in a7179f0
vals["module"] = cty.UnknownVal(cty.DynamicPseudoType) |
If call_module_type
is other than none
and the child module's sources are accessible, then the output of the child module that corresponds to the address reference will be evaluated when generating the evaluation context. This might be similar to evaluating local values.
Lines 227 to 268 in a7179f0
func (d *evaluationData) GetLocalValue(addr addrs.LocalValue, rng hcl.Range) (cty.Value, hcl.Diagnostics) { | |
var diags hcl.Diagnostics | |
// First we'll make sure the requested value is declared in configuration, | |
// so we can produce a nice message if not. | |
moduleConfig := d.Evaluator.Config.DescendentForInstance(d.ModulePath) | |
if moduleConfig == nil { | |
// should never happen, since we can't be evaluating in a module | |
// that wasn't mentioned in configuration. | |
panic(fmt.Sprintf("local value read from %s, which has no configuration", d.ModulePath)) | |
} | |
config := moduleConfig.Module.Locals[addr.Name] | |
if config == nil { | |
var suggestions []string | |
for k := range moduleConfig.Module.Locals { | |
suggestions = append(suggestions, k) | |
} | |
suggestion := nameSuggestion(addr.Name, suggestions) | |
if suggestion != "" { | |
suggestion = fmt.Sprintf(" Did you mean %q?", suggestion) | |
} | |
diags = diags.Append(&hcl.Diagnostic{ | |
Severity: hcl.DiagError, | |
Summary: `Reference to undeclared local value`, | |
Detail: fmt.Sprintf(`A local value with the name %q has not been declared.%s`, addr.Name, suggestion), | |
Subject: rng.Ptr(), | |
}) | |
return cty.DynamicVal, diags | |
} | |
// Build a call stack for circular reference detection only when getting a local value. | |
if diags := d.Evaluator.CallStack.Push(addrs.Reference{Subject: addr, SourceRange: rng}); diags.HasErrors() { | |
return cty.UnknownVal(cty.DynamicPseudoType), diags | |
} | |
val, diags := d.Evaluator.EvaluateExpr(config.Expr, cty.DynamicPseudoType) | |
d.Evaluator.CallStack.Pop() | |
return val, diags | |
} |
If the value cannot be evaluated at any point, for example because the source of the grandchild module is inaccessible, an unknown value will be returned as before.
References
- https://github.com/terraform-linters/tflint/blob/v0.51.2/docs/user-guide/compatibility.md
- Rule documentation (`aws_provider_missing_default_tags`) tflint-ruleset-aws#649
- https://github.com/terraform-linters/tflint/blob/v0.51.2/terraform/lang/eval.go#L95-L218
- https://github.com/terraform-linters/tflint/blob/v0.51.2/terraform/evaluator.go#L227-L268