Skip to content

Commit 9dcd06f

Browse files
authored
fix(misconf): add missing variable as unknown (#8683)
Signed-off-by: nikpivkin <[email protected]>
1 parent 12cf218 commit 9dcd06f

File tree

5 files changed

+67
-22
lines changed

5 files changed

+67
-22
lines changed

pkg/iac/scanners/terraform/parser/evaluator.go

-3
Original file line numberDiff line numberDiff line change
@@ -464,9 +464,6 @@ func (e *evaluator) evaluateVariable(b *terraform.Block) (cty.Value, error) {
464464
}
465465

466466
attributes := b.Attributes()
467-
if attributes == nil {
468-
return cty.NilVal, errors.New("cannot resolve variable with no attributes")
469-
}
470467

471468
var valType cty.Type
472469
var defaults *typeexpr.Defaults

pkg/iac/scanners/terraform/parser/parser.go

+37-15
Original file line numberDiff line numberDiff line change
@@ -269,14 +269,7 @@ func (p *Parser) Load(ctx context.Context) (*evaluator, error) {
269269
return nil, err
270270
}
271271
p.logger.Debug("Added input variables from tfvars", log.Int("count", len(inputVars)))
272-
273-
if missingVars := missingVariableValues(blocks, inputVars); len(missingVars) > 0 {
274-
p.logger.Warn(
275-
"Variable values was not found in the environment or variable files. Evaluating may not work correctly.",
276-
log.String("variables", strings.Join(missingVars, ", ")),
277-
)
278-
setNullMissingVariableValues(inputVars, missingVars)
279-
}
272+
p.setFallbackValuesForMissingVars(inputVars, blocks)
280273
}
281274

282275
modulesMetadata, metadataPath, err := loadModuleMetadata(p.moduleFS, p.projectRoot)
@@ -314,25 +307,54 @@ func (p *Parser) Load(ctx context.Context) (*evaluator, error) {
314307
), nil
315308
}
316309

317-
func missingVariableValues(blocks terraform.Blocks, inputVars map[string]cty.Value) []string {
318-
var missing []string
310+
func missingVariableValues(blocks terraform.Blocks, inputVars map[string]cty.Value) []*terraform.Block {
311+
var missing []*terraform.Block
319312
for _, varBlock := range blocks.OfType("variable") {
320313
if varBlock.GetAttribute("default") == nil {
321314
if _, ok := inputVars[varBlock.TypeLabel()]; !ok {
322-
missing = append(missing, varBlock.TypeLabel())
315+
missing = append(missing, varBlock)
323316
}
324317
}
325318
}
326319

327320
return missing
328321
}
329322

330-
// Set null values for missing variables, to allow expressions using them to be
323+
// Set fallback values for missing variables, to allow expressions using them to be
331324
// still be possibly evaluated to a value different than null.
332-
func setNullMissingVariableValues(inputVars map[string]cty.Value, missingVars []string) {
333-
for _, missingVar := range missingVars {
334-
inputVars[missingVar] = cty.NullVal(cty.DynamicPseudoType)
325+
func (p *Parser) setFallbackValuesForMissingVars(inputVars map[string]cty.Value, blocks []*terraform.Block) {
326+
varBlocks := missingVariableValues(blocks, inputVars)
327+
if len(varBlocks) == 0 {
328+
return
329+
}
330+
331+
missingVars := make([]string, 0, len(varBlocks))
332+
for _, block := range varBlocks {
333+
varType := inputVariableType(block)
334+
if varType != cty.NilType {
335+
inputVars[block.TypeLabel()] = cty.UnknownVal(varType)
336+
} else {
337+
inputVars[block.TypeLabel()] = cty.DynamicVal
338+
}
339+
missingVars = append(missingVars, block.TypeLabel())
340+
}
341+
342+
p.logger.Warn(
343+
"Variable values were not found in the environment or variable files. Evaluating may not work correctly.",
344+
log.String("variables", strings.Join(missingVars, ", ")),
345+
)
346+
}
347+
348+
func inputVariableType(b *terraform.Block) cty.Type {
349+
typeAttr, exists := b.Attributes()["type"]
350+
if !exists {
351+
return cty.NilType
352+
}
353+
ty, _, err := typeAttr.DecodeVarType()
354+
if err != nil {
355+
return cty.NilType
335356
}
357+
return ty
336358
}
337359

338360
func (p *Parser) EvaluateAll(ctx context.Context) (terraform.Modules, cty.Value, error) {

pkg/iac/scanners/terraform/parser/parser_test.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -2042,7 +2042,7 @@ resource "test" "values" {
20422042

20432043
s_attr := resources[0].GetAttribute("s")
20442044
require.NotNil(t, s_attr)
2045-
assert.Equal(t, "foo-", s_attr.GetRawValue())
2045+
assert.Equal(t, "foo-", s_attr.Value().Range().StringPrefix())
20462046

20472047
for _, name := range []string{"l1", "l2", "d1", "d2"} {
20482048
attr := resources[0].GetAttribute(name)
@@ -2224,7 +2224,7 @@ variable "baz" {}
22242224
_, err := parser.Load(t.Context())
22252225
require.NoError(t, err)
22262226

2227-
assert.Contains(t, buf.String(), "Variable values was not found in the environment or variable files.")
2227+
assert.Contains(t, buf.String(), "Variable values were not found in the environment or variable files.")
22282228
assert.Contains(t, buf.String(), "variables=\"foo\"")
22292229
}
22302230

@@ -2454,3 +2454,29 @@ module "bar" {
24542454
_, _, err = parser.EvaluateAll(t.Context())
24552455
require.NoError(t, err)
24562456
}
2457+
2458+
func TestAttributeWithMissingVarIsUnresolvable(t *testing.T) {
2459+
fsys := fstest.MapFS{
2460+
"main.tf": &fstest.MapFile{Data: []byte(`variable "inp" {
2461+
type = string
2462+
}
2463+
2464+
resource "foo" "bar" {
2465+
attr = "${var.inp}-test"
2466+
}
2467+
`)},
2468+
}
2469+
2470+
parser := New(fsys, "", OptionStopOnHCLError(true))
2471+
require.NoError(t, parser.ParseFS(t.Context(), "."))
2472+
2473+
_, err := parser.Load(t.Context())
2474+
require.NoError(t, err)
2475+
2476+
modules, _, err := parser.EvaluateAll(t.Context())
2477+
require.NoError(t, err)
2478+
require.Len(t, modules, 1)
2479+
foo := modules[0].GetResourcesByType("foo")[0]
2480+
attr := foo.GetAttribute("attr")
2481+
assert.False(t, attr.IsResolvable())
2482+
}

pkg/iac/terraform/attribute.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func (a *Attribute) Value() (ctyVal cty.Value) {
286286
}
287287
}()
288288
ctyVal, _ = a.hclAttribute.Expr.Value(a.ctx.Inner())
289-
if !ctyVal.IsKnown() || ctyVal.IsNull() {
289+
if ctyVal.IsNull() {
290290
return cty.DynamicVal
291291
}
292292
return ctyVal

pkg/iac/terraform/presets.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func postProcessValues(b *Block, input map[string]cty.Value) map[string]cty.Valu
6464

6565
if b.TypeLabel() == "aws_s3_bucket" {
6666
var bucketName string
67-
if bucket := input["bucket"]; bucket.Type().Equals(cty.String) {
67+
if bucket := input["bucket"]; bucket.Type().Equals(cty.String) && bucket.IsKnown() {
6868
bucketName = bucket.AsString()
6969
}
7070
input["arn"] = cty.StringVal(fmt.Sprintf("arn:aws:s3:::%s", bucketName))

0 commit comments

Comments
 (0)