Skip to content

Commit efd177b

Browse files
authored
fix(misconf): populate context correctly for module instances (#8656)
Signed-off-by: nikpivkin <[email protected]>
1 parent b7dfd64 commit efd177b

File tree

2 files changed

+72
-15
lines changed

2 files changed

+72
-15
lines changed

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

+15-7
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[str
137137

138138
// expand out resources and modules via count, for-each and dynamic
139139
// (not a typo, we do this twice so every order is processed)
140+
// TODO: using a module in for_each or count does not work,
141+
// because the child module is evaluated later
140142
e.blocks = e.expandBlocks(e.blocks)
141143
e.blocks = e.expandBlocks(e.blocks)
142144

@@ -239,10 +241,17 @@ func (e *evaluator) evaluateSubmodule(ctx context.Context, sm *submodule) bool {
239241
sm.modules, sm.fsMap = sm.eval.EvaluateAll(ctx)
240242
outputs := sm.eval.exportOutputs()
241243

244+
valueMap := e.ctx.Get("module").AsValueMap()
245+
if valueMap == nil {
246+
valueMap = make(map[string]cty.Value)
247+
}
248+
242249
// lastState needs to be captured after applying outputs – so that they
243250
// don't get treated as changes – but before running post-submodule
244251
// evaluation, so that changes from that can trigger re-evaluations of
245252
// the submodule if/when they feed back into inputs.
253+
ref := sm.definition.Definition.Reference()
254+
e.ctx.Set(blockInstanceValues(sm.definition.Definition, valueMap, outputs), "module", ref.NameLabel())
246255
e.ctx.Set(outputs, "module", sm.definition.Name)
247256
sm.lastState = sm.definition.inputVars()
248257
e.evaluateSteps()
@@ -564,7 +573,7 @@ func (e *evaluator) getValuesByBlockType(blockType string) cty.Value {
564573
if valueMap == nil {
565574
valueMap = make(map[string]cty.Value)
566575
}
567-
valueMap[ref.NameLabel()] = blockInstanceValues(b, valueMap)
576+
valueMap[ref.NameLabel()] = blockInstanceValues(b, valueMap, b.Values())
568577

569578
// Update the map of all blocks with the same type.
570579
values[ref.TypeLabel()] = cty.ObjectVal(valueMap)
@@ -588,7 +597,7 @@ func (e *evaluator) getResources() map[string]cty.Value {
588597
typeValues = make(map[string]cty.Value)
589598
values[ref.TypeLabel()] = typeValues
590599
}
591-
typeValues[ref.NameLabel()] = blockInstanceValues(b, typeValues)
600+
typeValues[ref.NameLabel()] = blockInstanceValues(b, typeValues, b.Values())
592601
}
593602

594603
return lo.MapValues(values, func(v map[string]cty.Value, _ string) cty.Value {
@@ -600,14 +609,14 @@ func (e *evaluator) getResources() map[string]cty.Value {
600609
// If the count argument is used, a tuple is returned where the index corresponds to the argument index.
601610
// If the for_each argument is used, an object is returned where the key corresponds to the argument key.
602611
// In other cases, the values of the block itself are returned.
603-
func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value) cty.Value {
612+
func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value, values cty.Value) cty.Value {
604613
ref := b.Reference()
605614
key := ref.RawKey()
606615

607616
switch {
608617
case key.Type().Equals(cty.Number) && b.GetAttribute("count") != nil:
609618
idx, _ := key.AsBigFloat().Int64()
610-
return insertTupleElement(typeValues[ref.NameLabel()], int(idx), b.Values())
619+
return insertTupleElement(typeValues[ref.NameLabel()], int(idx), values)
611620
case isForEachKey(key) && b.GetAttribute("for_each") != nil:
612621
keyStr := ref.Key()
613622

@@ -621,11 +630,10 @@ func blockInstanceValues(b *terraform.Block, typeValues map[string]cty.Value) ct
621630
instances = make(map[string]cty.Value)
622631
}
623632

624-
instances[keyStr] = b.Values()
633+
instances[keyStr] = values
625634
return cty.ObjectVal(instances)
626-
627635
default:
628-
return b.Values()
636+
return values
629637
}
630638
}
631639

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

+57-8
Original file line numberDiff line numberDiff line change
@@ -1707,11 +1707,13 @@ resource "test_resource" "this" {
17071707
func TestPopulateContextWithBlockInstances(t *testing.T) {
17081708

17091709
tests := []struct {
1710-
name string
1711-
files map[string]string
1710+
name string
1711+
blockType string
1712+
files map[string]string
17121713
}{
17131714
{
1714-
name: "data blocks with count",
1715+
name: "data blocks with count",
1716+
blockType: "data",
17151717
files: map[string]string{
17161718
"main.tf": `data "d" "foo" {
17171719
count = 1
@@ -1730,7 +1732,8 @@ data "c" "foo" {
17301732
},
17311733
},
17321734
{
1733-
name: "resource blocks with count",
1735+
name: "resource blocks with count",
1736+
blockType: "resource",
17341737
files: map[string]string{
17351738
"main.tf": `resource "d" "foo" {
17361739
count = 1
@@ -1749,7 +1752,33 @@ resource "c" "foo" {
17491752
},
17501753
},
17511754
{
1752-
name: "data blocks with for_each",
1755+
name: "module block with count",
1756+
blockType: "data",
1757+
files: map[string]string{
1758+
"main.tf": `module "a" {
1759+
source = "./modules/a"
1760+
count = 2
1761+
inp = "Index ${count.index}"
1762+
}
1763+
1764+
data "b" "foo" {
1765+
count = 1
1766+
value = module.a[0].value
1767+
}
1768+
1769+
data "c" "foo" {
1770+
count = 1
1771+
value = data.b.foo[0].value
1772+
}`,
1773+
"modules/a/main.tf": `variable "inp" {}
1774+
output "value" {
1775+
value = var.inp
1776+
}`,
1777+
},
1778+
},
1779+
{
1780+
name: "data blocks with for_each",
1781+
blockType: "data",
17531782
files: map[string]string{
17541783
"main.tf": `data "d" "foo" {
17551784
for_each = toset([0])
@@ -1768,7 +1797,8 @@ data "c" "foo" {
17681797
},
17691798
},
17701799
{
1771-
name: "resource blocks with for_each",
1800+
name: "resource blocks with for_each",
1801+
blockType: "resource",
17721802
files: map[string]string{
17731803
"main.tf": `resource "d" "foo" {
17741804
for_each = toset([0])
@@ -1783,6 +1813,25 @@ resource "b" "foo" {
17831813
resource "c" "foo" {
17841814
for_each = b.foo
17851815
value = each.value.value
1816+
}`,
1817+
},
1818+
},
1819+
{
1820+
name: "module block with for_each",
1821+
blockType: "data",
1822+
files: map[string]string{
1823+
"main.tf": `module "a" {
1824+
for_each = toset([0])
1825+
source = "./modules/a"
1826+
inp = "Index ${each.key}"
1827+
}
1828+
1829+
data "b" "foo" {
1830+
value = module.a["0"].value
1831+
}`,
1832+
"modules/a/main.tf": `variable "inp" {}
1833+
output "value" {
1834+
value = var.inp
17861835
}`,
17871836
},
17881837
},
@@ -1791,8 +1840,8 @@ resource "c" "foo" {
17911840
for _, tt := range tests {
17921841
t.Run(tt.name, func(t *testing.T) {
17931842
modules := parse(t, tt.files)
1794-
require.Len(t, modules, 1)
1795-
for _, b := range modules.GetBlocks() {
1843+
require.GreaterOrEqual(t, len(modules), 1)
1844+
for _, b := range modules.GetBlocks().OfType(tt.blockType) {
17961845
attr := b.GetAttribute("value")
17971846
assert.Equal(t, "Index 0", attr.Value().AsString())
17981847
}

0 commit comments

Comments
 (0)