From 076fccd8e732ff6afb1e373ba002ff98dddd76ae Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Wed, 12 Oct 2022 12:38:27 -0600 Subject: [PATCH 1/2] fix: don't reveal nested attributes with sensitive schema --- internal/command/format/diff.go | 27 +- internal/command/format/diff_test.go | 1266 ++++++++++++++++++++++---- 2 files changed, 1090 insertions(+), 203 deletions(-) diff --git a/internal/command/format/diff.go b/internal/command/format/diff.go index 8c7d3ce84ba9..0ea6a694f588 100644 --- a/internal/command/format/diff.go +++ b/internal/command/format/diff.go @@ -398,7 +398,7 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At } if attrS.NestedType != nil { - p.writeNestedAttrDiff(name, attrS.NestedType, old, new, nameLen, indent, path, action, showJustNew) + p.writeNestedAttrDiff(name, attrS, old, new, nameLen, indent, path, action, showJustNew) return false } @@ -416,7 +416,7 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At p.buf.WriteString(" = ") if attrS.Sensitive { - p.buf.WriteString("(sensitive value)") + p.buf.WriteString("(sensitive)") if p.pathForcesNewResource(path) { p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) } @@ -441,9 +441,11 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At // writeNestedAttrDiff is responsible for formatting Attributes with NestedTypes // in the diff. func (p *blockBodyDiffPrinter) writeNestedAttrDiff( - name string, objS *configschema.Object, old, new cty.Value, + name string, attrWithNestedS *configschema.Attribute, old, new cty.Value, nameLen, indent int, path cty.Path, action plans.Action, showJustNew bool) { + objS := attrWithNestedS.NestedType + p.buf.WriteString("\n") p.writeSensitivityWarning(old, new, indent, action, false) p.buf.WriteString(strings.Repeat(" ", indent)) @@ -454,8 +456,11 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( p.buf.WriteString(p.color.Color("[reset]")) p.buf.WriteString(strings.Repeat(" ", nameLen-len(name))) - if old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) { - p.buf.WriteString(" = (sensitive value)") + // Then schema of the attribute itself can be marked sensitive, or the values assigned + sensitive := attrWithNestedS.Sensitive || old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) + if sensitive { + p.buf.WriteString(" = (sensitive)") + if p.pathForcesNewResource(path) { p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) } @@ -475,6 +480,12 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( p.buf.WriteString(strings.Repeat(" ", indent+2)) p.buf.WriteString("}") + if !new.IsKnown() { + p.buf.WriteString(" -> (known after apply)") + } else if new.IsNull() { + p.buf.WriteString(p.color.Color("[dark_gray] -> null[reset]")) + } + case configschema.NestingList: p.buf.WriteString(" = [") if action != plans.NoOp && (p.pathForcesNewResource(path) || p.pathForcesNewResource(path[:len(path)-1])) { @@ -558,6 +569,8 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( if !new.IsKnown() { p.buf.WriteString(" -> (known after apply)") + } else if new.IsNull() { + p.buf.WriteString(p.color.Color("[dark_gray] -> null[reset]")) } case configschema.NestingSet: @@ -636,6 +649,8 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( if !new.IsKnown() { p.buf.WriteString(" -> (known after apply)") + } else if new.IsNull() { + p.buf.WriteString(p.color.Color("[dark_gray] -> null[reset]")) } case configschema.NestingMap: @@ -711,6 +726,8 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( p.buf.WriteString("}") if !new.IsKnown() { p.buf.WriteString(" -> (known after apply)") + } else if new.IsNull() { + p.buf.WriteString(p.color.Color("[dark_gray] -> null[reset]")) } } } diff --git a/internal/command/format/diff_test.go b/internal/command/format/diff_test.go index 56e2eafeafed..33040380c248 100644 --- a/internal/command/format/diff_test.go +++ b/internal/command/format/diff_test.go @@ -411,11 +411,11 @@ new line ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + conn_info = { - + password = (sensitive value) + + password = (sensitive) + user = "not-secret" } + id = (known after apply) - + password = (sensitive value) + + password = (sensitive) } `, }, @@ -3048,7 +3048,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + ami = "ami-AFTER" - + disks = (sensitive value) + + disks = (sensitive) + id = "i-02ae66f368e8518a9" + root_block_device { @@ -3146,7 +3146,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ~ ami = "ami-BEFORE" -> "ami-AFTER" # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. - ~ disks = (sensitive value) + ~ disks = (sensitive) id = "i-02ae66f368e8518a9" + root_block_device { @@ -3197,7 +3197,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ~ ami = "ami-BEFORE" -> "ami-AFTER" # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. The value is unchanged. - ~ disks = (sensitive value) + ~ disks = (sensitive) id = "i-02ae66f368e8518a9" } `, @@ -4291,280 +4291,1119 @@ func TestResourceChange_nestedMap(t *testing.T) { }), }), RequiredReplace: cty.NewPathSet(), - Schema: testSchemaMultipleBlocks(configschema.NestingMap), + Schema: testSchemaMultipleBlocks(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + ~ leaf_block_device "b" { + ~ volume_type = "gp2" -> "gp3" + } + + # (1 unchanged block hidden) + } +`, + }, + "in-place update - multiple different blocks second changed": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp3"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaMultipleBlocks(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + ~ root_block_device "a" { + ~ volume_type = "gp2" -> "gp3" + } + + # (1 unchanged block hidden) + } +`, + }, + "in-place update - multiple different blocks changed": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp3"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp3"), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaMultipleBlocks(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + ~ leaf_block_device "b" { + ~ volume_type = "gp2" -> "gp3" + } + + ~ root_block_device "a" { + ~ volume_type = "gp2" -> "gp3" + } + } +`, + }, + "in-place update - mixed blocks unchanged": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaMultipleBlocks(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + # (4 unchanged blocks hidden) + } +`, + }, + "in-place update - mixed blocks changed": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + "root_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp3"), + }), + }), + "leaf_block_device": cty.MapVal(map[string]cty.Value{ + "a": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "b": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp3"), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaMultipleBlocks(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + ~ leaf_block_device "b" { + ~ volume_type = "gp2" -> "gp3" + } + + ~ root_block_device "b" { + ~ volume_type = "gp2" -> "gp3" + } + + # (2 unchanged blocks hidden) + } +`, + }, + } + runTestCases(t, testCases) +} + +func TestResourceChange_nestedSingle(t *testing.T) { + testCases := map[string]testCase{ + "in-place update - equal": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + id = "i-02ae66f368e8518a9" + # (1 unchanged attribute hidden) + + # (1 unchanged block hidden) + } +`, + }, + "in-place update - creation": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "root_block_device": cty.NullVal(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + "disk": cty.NullVal(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.NullVal(cty.String), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + + disk = { + + mount_point = "/var/diska" + + size = "50GB" + } + id = "i-02ae66f368e8518a9" + + + root_block_device {} + } +`, + }, + "force-new update (inside blocks)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("different"), + }), + }), + RequiredReplace: cty.NewPathSet( + cty.Path{ + cty.GetAttrStep{Name: "root_block_device"}, + cty.GetAttrStep{Name: "volume_type"}, + }, + cty.Path{ + cty.GetAttrStep{Name: "disk"}, + cty.GetAttrStep{Name: "mount_point"}, + }, + ), + Schema: testSchema(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disk = { + ~ mount_point = "/var/diska" -> "/var/diskb" # forces replacement + # (1 unchanged attribute hidden) + } + id = "i-02ae66f368e8518a9" + + ~ root_block_device { + ~ volume_type = "gp2" -> "different" # forces replacement + } + } +`, + }, + "force-new update (whole block)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diskb"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("different"), + }), + }), + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "root_block_device"}}, + cty.Path{cty.GetAttrStep{Name: "disk"}}, + ), + Schema: testSchema(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disk = { # forces replacement + ~ mount_point = "/var/diska" -> "/var/diskb" + # (1 unchanged attribute hidden) + } + id = "i-02ae66f368e8518a9" + + ~ root_block_device { # forces replacement + ~ volume_type = "gp2" -> "different" + } + } +`, + }, + "in-place update - deletion": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "root_block_device": cty.NullVal(cty.Object(map[string]cty.Type{ + "volume_type": cty.String, + })), + "disk": cty.NullVal(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchema(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + - disk = { + - mount_point = "/var/diska" -> null + - size = "50GB" -> null + } -> null + id = "i-02ae66f368e8518a9" + + - root_block_device { + - volume_type = "gp2" -> null + } + } +`, + }, + "with dynamically-typed attribute": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "block": cty.NullVal(cty.Object(map[string]cty.Type{ + "attr": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "block": cty.ObjectVal(map[string]cty.Value{ + "attr": cty.StringVal("foo"), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: &configschema.Block{ + BlockTypes: map[string]*configschema.NestedBlock{ + "block": { + Block: configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "attr": {Type: cty.DynamicPseudoType, Optional: true}, + }, + }, + Nesting: configschema.NestingSingle, + }, + }, + }, + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + + block { + + attr = "foo" + } + } +`, + }, + "in-place update - unknown": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + "new_field": cty.StringVal("new_value"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disk": cty.UnknownVal(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + "new_field": cty.StringVal("new_value"), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaPlus(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disk = { + ~ mount_point = "/var/diska" -> (known after apply) + ~ size = "50GB" -> (known after apply) + } -> (known after apply) + id = "i-02ae66f368e8518a9" + + # (1 unchanged block hidden) + } +`, + }, + "in-place update - modification": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + "new_field": cty.StringVal("new_value"), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disk": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("25GB"), + }), + "root_block_device": cty.ObjectVal(map[string]cty.Value{ + "volume_type": cty.StringVal("gp2"), + "new_field": cty.StringVal("new_value"), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaPlus(configschema.NestingSingle), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disk = { + ~ size = "50GB" -> "25GB" + # (1 unchanged attribute hidden) + } + id = "i-02ae66f368e8518a9" + + # (1 unchanged block hidden) + } +`, + }, + } + runTestCases(t, testCases) +} + +func TestResourceChange_nestedMapSensitiveSchema(t *testing.T) { + testCases := map[string]testCase{ + "creation from null": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.NullVal(cty.String), + "ami": cty.NullVal(cty.String), + "disks": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + + ami = "ami-AFTER" + + disks = (sensitive value) + + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + "force-new update (whole block)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("100GB"), + }), + }), + }), + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchemaSensitive(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) # forces replacement + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update - deletion": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.NullVal(cty.Map(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + - disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update - unknown": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.MapVal(map[string]cty.Value{ + "disk_a": cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.UnknownVal(cty.Map(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingMap), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + } + runTestCases(t, testCases) +} + +func TestResourceChange_nestedListSensitiveSchema(t *testing.T) { + testCases := map[string]testCase{ + "creation from null": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.NullVal(cty.String), + "ami": cty.NullVal(cty.String), + "disks": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingList), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + + ami = "ami-AFTER" + + disks = (sensitive value) + + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingList), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + "force-new update (whole block)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("100GB"), + }), + }), + }), + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchemaSensitive(configschema.NestingList), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) # forces replacement + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update - deletion": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingList), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + - disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + "in-place update - unknown": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-BEFORE"), + "disks": cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.StringVal("50GB"), + }), + }), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingList), + ExpectedOutput: ` # test_instance.example will be updated in-place + ~ resource "test_instance" "example" { + ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) + id = "i-02ae66f368e8518a9" + } +`, + }, + } + runTestCases(t, testCases) +} + +func TestResourceChange_nestedSetSensitiveSchema(t *testing.T) { + testCases := map[string]testCase{ + "creation from null": { + Action: plans.Update, + Mode: addrs.ManagedResourceMode, + Before: cty.ObjectVal(map[string]cty.Value{ + "id": cty.NullVal(cty.String), + "ami": cty.NullVal(cty.String), + "disks": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), + }), + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("i-02ae66f368e8518a9"), + "ami": cty.StringVal("ami-AFTER"), + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "mount_point": cty.StringVal("/var/diska"), + "size": cty.NullVal(cty.String), + }), + }), + }), + RequiredReplace: cty.NewPathSet(), + Schema: testSchemaSensitive(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = "ami-BEFORE" -> "ami-AFTER" - id = "i-02ae66f368e8518a9" - # (1 unchanged attribute hidden) - - ~ leaf_block_device "b" { - ~ volume_type = "gp2" -> "gp3" - } - - # (1 unchanged block hidden) + + ami = "ami-AFTER" + + disks = (sensitive value) + + id = "i-02ae66f368e8518a9" } `, }, - "in-place update - multiple different blocks second changed": { + "in-place update": { Action: plans.Update, Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ - "mount_point": cty.StringVal("/var/diska"), - "size": cty.StringVal("50GB"), - }), - }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), + "disks": cty.SetValEmpty(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + })), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ "mount_point": cty.StringVal("/var/diska"), - "size": cty.StringVal("50GB"), - }), - }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp3"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), + "size": cty.NullVal(cty.String), }), }), }), RequiredReplace: cty.NewPathSet(), - Schema: testSchemaMultipleBlocks(configschema.NestingMap), + Schema: testSchemaSensitive(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) id = "i-02ae66f368e8518a9" - # (1 unchanged attribute hidden) - - ~ root_block_device "a" { - ~ volume_type = "gp2" -> "gp3" - } - - # (1 unchanged block hidden) } `, }, - "in-place update - multiple different blocks changed": { - Action: plans.Update, - Mode: addrs.ManagedResourceMode, + "force-new update (whole block)": { + Action: plans.DeleteThenCreate, + ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate, + Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ "mount_point": cty.StringVal("/var/diska"), "size": cty.StringVal("50GB"), }), }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ "mount_point": cty.StringVal("/var/diska"), - "size": cty.StringVal("50GB"), - }), - }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp3"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp3"), + "size": cty.StringVal("100GB"), }), }), }), - RequiredReplace: cty.NewPathSet(), - Schema: testSchemaMultipleBlocks(configschema.NestingMap), - ExpectedOutput: ` # test_instance.example will be updated in-place - ~ resource "test_instance" "example" { + RequiredReplace: cty.NewPathSet( + cty.Path{cty.GetAttrStep{Name: "disks"}}, + ), + Schema: testSchemaSensitive(configschema.NestingSet), + ExpectedOutput: ` # test_instance.example must be replaced +-/+ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) # forces replacement id = "i-02ae66f368e8518a9" - # (1 unchanged attribute hidden) - - ~ leaf_block_device "b" { - ~ volume_type = "gp2" -> "gp3" - } - - ~ root_block_device "a" { - ~ volume_type = "gp2" -> "gp3" - } } `, }, - "in-place update - mixed blocks unchanged": { + "in-place update - deletion": { Action: plans.Update, Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ "mount_point": cty.StringVal("/var/diska"), "size": cty.StringVal("50GB"), }), }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ - "mount_point": cty.StringVal("/var/diska"), - "size": cty.StringVal("50GB"), - }), - }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), + "disks": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), }), RequiredReplace: cty.NewPathSet(), - Schema: testSchemaMultipleBlocks(configschema.NestingMap), + Schema: testSchemaSensitive(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" + - disks = (sensitive value) id = "i-02ae66f368e8518a9" - # (1 unchanged attribute hidden) - - # (4 unchanged blocks hidden) } `, }, - "in-place update - mixed blocks changed": { + "in-place update - unknown": { Action: plans.Update, Mode: addrs.ManagedResourceMode, Before: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-BEFORE"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ + "disks": cty.SetVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ "mount_point": cty.StringVal("/var/diska"), "size": cty.StringVal("50GB"), }), }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - }), }), After: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-02ae66f368e8518a9"), "ami": cty.StringVal("ami-AFTER"), - "disks": cty.MapVal(map[string]cty.Value{ - "disk_a": cty.ObjectVal(map[string]cty.Value{ - "mount_point": cty.StringVal("/var/diska"), - "size": cty.StringVal("50GB"), - }), - }), - "root_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp3"), - }), - }), - "leaf_block_device": cty.MapVal(map[string]cty.Value{ - "a": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp2"), - }), - "b": cty.ObjectVal(map[string]cty.Value{ - "volume_type": cty.StringVal("gp3"), - }), - }), + "disks": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{ + "mount_point": cty.String, + "size": cty.String, + }))), }), RequiredReplace: cty.NewPathSet(), - Schema: testSchemaMultipleBlocks(configschema.NestingMap), + Schema: testSchemaSensitive(configschema.NestingSet), ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { ~ ami = "ami-BEFORE" -> "ami-AFTER" + ~ disks = (sensitive value) id = "i-02ae66f368e8518a9" - # (1 unchanged attribute hidden) - - ~ leaf_block_device "b" { - ~ volume_type = "gp2" -> "gp3" - } - - ~ root_block_device "b" { - ~ volume_type = "gp2" -> "gp3" - } - - # (2 unchanged blocks hidden) } `, }, @@ -5301,7 +6140,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, ExpectedOutput: ` # test_instance.example will be updated in-place ~ resource "test_instance" "example" { - ~ ami = (sensitive) + ~ ami = (sensitive value) id = "i-02ae66f368e8518a9" ~ list_field = [ - (sensitive), @@ -5685,7 +6524,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = (sensitive value) # forces replacement + ~ ami = (sensitive) # forces replacement id = "i-02ae66f368e8518a9" } `, @@ -5728,7 +6567,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ conn_info = { # forces replacement - ~ password = (sensitive value) + ~ password = (sensitive) # (1 unchanged attribute hidden) } id = "i-02ae66f368e8518a9" @@ -5985,7 +6824,7 @@ func TestOutputChanges(t *testing.T) { }, ` ~ a = 1 -> 2 - ~ b = (sensitive value) + ~ b = (sensitive) ~ c = false -> true`, }, } @@ -6023,11 +6862,16 @@ func outputChange(name string, before, after cty.Value, sensitive bool) *plans.O // A basic test schema using a configurable NestingMode for one (NestedType) attribute and one block func testSchema(nesting configschema.NestingMode) *configschema.Block { + var diskKey = "disks" + if nesting == configschema.NestingSingle { + diskKey = "disk" + } + return &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, "ami": {Type: cty.String, Optional: true}, - "disks": { + diskKey: { NestedType: &configschema.Object{ Attributes: map[string]*configschema.Attribute{ "mount_point": {Type: cty.String, Optional: true}, @@ -6054,6 +6898,27 @@ func testSchema(nesting configschema.NestingMode) *configschema.Block { } } +// A basic test schema using a configurable NestingMode for one (NestedType) +// attribute marked sensitive. +func testSchemaSensitive(nesting configschema.NestingMode) *configschema.Block { + return &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Optional: true, Computed: true}, + "ami": {Type: cty.String, Optional: true}, + "disks": { + Sensitive: true, + NestedType: &configschema.Object{ + Attributes: map[string]*configschema.Attribute{ + "mount_point": {Type: cty.String, Optional: true}, + "size": {Type: cty.String, Optional: true}, + }, + Nesting: nesting, + }, + }, + }, + } +} + func testSchemaMultipleBlocks(nesting configschema.NestingMode) *configschema.Block { return &configschema.Block{ Attributes: map[string]*configschema.Attribute{ @@ -6100,11 +6965,16 @@ func testSchemaMultipleBlocks(nesting configschema.NestingMode) *configschema.Bl // similar to testSchema with the addition of a "new_field" block func testSchemaPlus(nesting configschema.NestingMode) *configschema.Block { + var diskKey = "disks" + if nesting == configschema.NestingSingle { + diskKey = "disk" + } + return &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": {Type: cty.String, Optional: true, Computed: true}, "ami": {Type: cty.String, Optional: true}, - "disks": { + diskKey: { NestedType: &configschema.Object{ Attributes: map[string]*configschema.Attribute{ "mount_point": {Type: cty.String, Optional: true}, From bd744ad4e911daa60ccb0b7af51a45652b2b0743 Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Thu, 20 Oct 2022 10:09:21 -0600 Subject: [PATCH 2/2] Unify all sensitive value plan output as "(sensitive value)" Previously, there was mixed usage of "(sensitive)" and "(sensitive value)" and even though it was more common to see "(sensitive)", the thought is that it's a value we are hiding rather than describing something already shown. --- internal/command/console_test.go | 4 +- internal/command/format/diff.go | 20 +++-- internal/command/format/diff_test.go | 74 +++++++++---------- internal/repl/format.go | 2 +- internal/repl/format_test.go | 4 +- .../language/expressions/function-calls.mdx | 4 +- .../docs/language/expressions/references.mdx | 2 +- .../docs/language/functions/nonsensitive.mdx | 6 +- website/docs/language/functions/sensitive.mdx | 6 +- website/docs/language/values/outputs.mdx | 2 +- website/docs/language/values/variables.mdx | 6 +- 11 files changed, 67 insertions(+), 63 deletions(-) diff --git a/internal/command/console_test.go b/internal/command/console_test.go index 13b743547e23..de8060c5b587 100644 --- a/internal/command/console_test.go +++ b/internal/command/console_test.go @@ -172,8 +172,8 @@ func TestConsole_variables(t *testing.T) { commands := map[string]string{ "var.foo\n": "\"bar\"\n", "var.snack\n": "\"popcorn\"\n", - "var.secret_snack\n": "(sensitive)\n", - "local.snack_bar\n": "[\n \"popcorn\",\n (sensitive),\n]\n", + "var.secret_snack\n": "(sensitive value)\n", + "local.snack_bar\n": "[\n \"popcorn\",\n (sensitive value),\n]\n", } args := []string{} diff --git a/internal/command/format/diff.go b/internal/command/format/diff.go index 0ea6a694f588..8b4e65aed8d1 100644 --- a/internal/command/format/diff.go +++ b/internal/command/format/diff.go @@ -274,7 +274,10 @@ type blockBodyDiffResult struct { skippedBlocks int } -const forcesNewResourceCaption = " [red]# forces replacement[reset]" +const ( + forcesNewResourceCaption = " [red]# forces replacement[reset]" + sensitiveCaption = "(sensitive value)" +) // writeBlockBodyDiff writes attribute or block differences // and returns true if any differences were found and written @@ -416,7 +419,7 @@ func (p *blockBodyDiffPrinter) writeAttrDiff(name string, attrS *configschema.At p.buf.WriteString(" = ") if attrS.Sensitive { - p.buf.WriteString("(sensitive)") + p.buf.WriteString(sensitiveCaption) if p.pathForcesNewResource(path) { p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) } @@ -459,7 +462,8 @@ func (p *blockBodyDiffPrinter) writeNestedAttrDiff( // Then schema of the attribute itself can be marked sensitive, or the values assigned sensitive := attrWithNestedS.Sensitive || old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) if sensitive { - p.buf.WriteString(" = (sensitive)") + p.buf.WriteString(" = ") + p.buf.WriteString(sensitiveCaption) if p.pathForcesNewResource(path) { p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) @@ -742,7 +746,7 @@ func (p *blockBodyDiffPrinter) writeNestedBlockDiffs(name string, blockS *config // If either the old or the new value is marked, // Display a special diff because it is irrelevant - // to list all obfuscated attributes as (sensitive) + // to list all obfuscated attributes as (sensitive value) if old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) { p.writeSensitiveNestedBlockDiff(name, old, new, indent, blankBefore, path) return 0 @@ -1025,7 +1029,7 @@ func (p *blockBodyDiffPrinter) writeNestedBlockDiff(name string, label *string, func (p *blockBodyDiffPrinter) writeValue(val cty.Value, action plans.Action, indent int) { // Could check specifically for the sensitivity marker if val.HasMark(marks.Sensitive) { - p.buf.WriteString("(sensitive)") + p.buf.WriteString(sensitiveCaption) return } @@ -1193,7 +1197,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa // values are known and non-null. if old.IsKnown() && new.IsKnown() && !old.IsNull() && !new.IsNull() && typesEqual { if old.HasMark(marks.Sensitive) || new.HasMark(marks.Sensitive) { - p.buf.WriteString("(sensitive)") + p.buf.WriteString(sensitiveCaption) if p.pathForcesNewResource(path) { p.buf.WriteString(p.color.Color(forcesNewResourceCaption)) } @@ -1564,7 +1568,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa case plans.Create, plans.NoOp: v := new.Index(kV) if v.HasMark(marks.Sensitive) { - p.buf.WriteString("(sensitive)") + p.buf.WriteString(sensitiveCaption) } else { p.writeValue(v, action, indent+4) } @@ -1574,7 +1578,7 @@ func (p *blockBodyDiffPrinter) writeValueDiff(old, new cty.Value, indent int, pa p.writeValueDiff(oldV, newV, indent+4, path) default: if oldV.HasMark(marks.Sensitive) || newV.HasMark(marks.Sensitive) { - p.buf.WriteString("(sensitive)") + p.buf.WriteString(sensitiveCaption) } else { p.writeValueDiff(oldV, newV, indent+4, path) } diff --git a/internal/command/format/diff_test.go b/internal/command/format/diff_test.go index 33040380c248..5ab9502c37b1 100644 --- a/internal/command/format/diff_test.go +++ b/internal/command/format/diff_test.go @@ -411,11 +411,11 @@ new line ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + conn_info = { - + password = (sensitive) + + password = (sensitive value) + user = "not-secret" } + id = (known after apply) - + password = (sensitive) + + password = (sensitive value) } `, }, @@ -3048,7 +3048,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { + ami = "ami-AFTER" - + disks = (sensitive) + + disks = (sensitive value) + id = "i-02ae66f368e8518a9" + root_block_device { @@ -3146,7 +3146,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ~ ami = "ami-BEFORE" -> "ami-AFTER" # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. - ~ disks = (sensitive) + ~ disks = (sensitive value) id = "i-02ae66f368e8518a9" + root_block_device { @@ -3197,7 +3197,7 @@ func TestResourceChange_nestedSet(t *testing.T) { ~ ami = "ami-BEFORE" -> "ami-AFTER" # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. The value is unchanged. - ~ disks = (sensitive) + ~ disks = (sensitive value) id = "i-02ae66f368e8518a9" } `, @@ -3965,7 +3965,7 @@ func TestResourceChange_nestedMap(t *testing.T) { ~ ami = "ami-BEFORE" -> "ami-AFTER" ~ disks = { + "disk_a" = { - + mount_point = (sensitive) + + mount_point = (sensitive value) + size = "50GB" }, } @@ -5728,18 +5728,18 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, ExpectedOutput: ` # test_instance.example will be created + resource "test_instance" "example" { - + ami = (sensitive) + + ami = (sensitive value) + id = "i-02ae66f368e8518a9" + list_field = [ + "hello", - + (sensitive), + + (sensitive value), + "!", ] + map_key = { + "breakfast" = 800 - + "dinner" = (sensitive) + + "dinner" = (sensitive value) } - + map_whole = (sensitive) + + map_whole = (sensitive value) + nested_block_list { # At least one attribute in this block is (or was) sensitive, @@ -5882,29 +5882,29 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ~ resource "test_instance" "example" { # Warning: this attribute value will no longer be marked as sensitive # after applying this change. - ~ ami = (sensitive) + ~ ami = (sensitive value) id = "i-02ae66f368e8518a9" ~ list_field = [ # (1 unchanged element hidden) "friends", - - (sensitive), + - (sensitive value), + ".", ] ~ map_key = { # Warning: this attribute value will no longer be marked as sensitive # after applying this change. - ~ "dinner" = (sensitive) + ~ "dinner" = (sensitive value) # (1 unchanged element hidden) } # Warning: this attribute value will no longer be marked as sensitive # after applying this change. - ~ map_whole = (sensitive) + ~ map_whole = (sensitive value) # Warning: this attribute value will no longer be marked as sensitive # after applying this change. - ~ some_number = (sensitive) + ~ some_number = (sensitive value) # Warning: this attribute value will no longer be marked as sensitive # after applying this change. - ~ special = (sensitive) + ~ special = (sensitive value) # Warning: this block will no longer be marked as sensitive # after applying this change. @@ -6007,18 +6007,18 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { id = "i-02ae66f368e8518a9" ~ list_field = [ - "hello", - + (sensitive), + + (sensitive value), "friends", ] ~ map_key = { ~ "breakfast" = 800 -> 700 # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. - ~ "dinner" = (sensitive) + ~ "dinner" = (sensitive value) } # Warning: this attribute value will be marked as sensitive and will not # display in UI output after applying this change. - ~ map_whole = (sensitive) + ~ map_whole = (sensitive value) # Warning: this block will be marked as sensitive and will not # display in UI output after applying this change. @@ -6143,15 +6143,15 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ~ ami = (sensitive value) id = "i-02ae66f368e8518a9" ~ list_field = [ - - (sensitive), - + (sensitive), + - (sensitive value), + + (sensitive value), "friends", ] ~ map_key = { - ~ "dinner" = (sensitive) + ~ "dinner" = (sensitive value) # (1 unchanged element hidden) } - ~ map_whole = (sensitive) + ~ map_whole = (sensitive value) ~ nested_block_map { # At least one attribute in this block is (or was) sensitive, @@ -6289,29 +6289,29 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ~ resource "test_instance" "example" { # Warning: this attribute value will no longer be marked as sensitive # after applying this change. The value is unchanged. - ~ ami = (sensitive) + ~ ami = (sensitive value) id = "i-02ae66f368e8518a9" ~ list_field = [ # (1 unchanged element hidden) "friends", - - (sensitive), + - (sensitive value), + "!", ] ~ map_key = { # Warning: this attribute value will no longer be marked as sensitive # after applying this change. The value is unchanged. - ~ "dinner" = (sensitive) + ~ "dinner" = (sensitive value) # (1 unchanged element hidden) } # Warning: this attribute value will no longer be marked as sensitive # after applying this change. The value is unchanged. - ~ map_whole = (sensitive) + ~ map_whole = (sensitive value) # Warning: this attribute value will no longer be marked as sensitive # after applying this change. The value is unchanged. - ~ some_number = (sensitive) + ~ some_number = (sensitive value) # Warning: this attribute value will no longer be marked as sensitive # after applying this change. The value is unchanged. - ~ special = (sensitive) + ~ special = (sensitive value) # Warning: this block will no longer be marked as sensitive # after applying this change. @@ -6410,17 +6410,17 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { }, ExpectedOutput: ` # test_instance.example will be destroyed - resource "test_instance" "example" { - - ami = (sensitive) -> null + - ami = (sensitive value) -> null - id = "i-02ae66f368e8518a9" -> null - list_field = [ - "hello", - - (sensitive), + - (sensitive value), ] -> null - map_key = { - "breakfast" = 800 - - "dinner" = (sensitive) + - "dinner" = (sensitive value) } -> null - - map_whole = (sensitive) -> null + - map_whole = (sensitive value) -> null - nested_block_set { # At least one attribute in this block is (or was) sensitive, @@ -6492,7 +6492,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = (sensitive) # forces replacement + ~ ami = (sensitive value) # forces replacement id = "i-02ae66f368e8518a9" ~ nested_block_set { # forces replacement @@ -6524,7 +6524,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ), ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { - ~ ami = (sensitive) # forces replacement + ~ ami = (sensitive value) # forces replacement id = "i-02ae66f368e8518a9" } `, @@ -6567,7 +6567,7 @@ func TestResourceChange_sensitiveVariable(t *testing.T) { ExpectedOutput: ` # test_instance.example must be replaced -/+ resource "test_instance" "example" { ~ conn_info = { # forces replacement - ~ password = (sensitive) + ~ password = (sensitive value) # (1 unchanged attribute hidden) } id = "i-02ae66f368e8518a9" @@ -6824,7 +6824,7 @@ func TestOutputChanges(t *testing.T) { }, ` ~ a = 1 -> 2 - ~ b = (sensitive) + ~ b = (sensitive value) ~ c = false -> true`, }, } diff --git a/internal/repl/format.go b/internal/repl/format.go index fbdd44f58194..c65ad048b3b3 100644 --- a/internal/repl/format.go +++ b/internal/repl/format.go @@ -18,7 +18,7 @@ func FormatValue(v cty.Value, indent int) string { return "(known after apply)" } if v.HasMark(marks.Sensitive) { - return "(sensitive)" + return "(sensitive value)" } if v.IsNull() { ty := v.Type() diff --git a/internal/repl/format_test.go b/internal/repl/format_test.go index d7577135585c..31843bbe010b 100644 --- a/internal/repl/format_test.go +++ b/internal/repl/format_test.go @@ -171,8 +171,8 @@ EOT_`, `toset([])`, }, { - cty.StringVal("sensitive value").Mark(marks.Sensitive), - "(sensitive)", + cty.StringVal("a sensitive value").Mark(marks.Sensitive), + "(sensitive value)", }, } diff --git a/website/docs/language/expressions/function-calls.mdx b/website/docs/language/expressions/function-calls.mdx index dc870173c1e8..a590b6e87ce0 100644 --- a/website/docs/language/expressions/function-calls.mdx +++ b/website/docs/language/expressions/function-calls.mdx @@ -63,11 +63,11 @@ the `keys()` function will result in a list that is sensitive: ```shell > local.baz { - "a" = (sensitive) + "a" = (sensitive value) "b" = "dog" } > keys(local.baz) -(sensitive) +(sensitive value) ``` ## When Terraform Calls Functions diff --git a/website/docs/language/expressions/references.mdx b/website/docs/language/expressions/references.mdx index 1709b60fd23a..e3325b8c7f14 100644 --- a/website/docs/language/expressions/references.mdx +++ b/website/docs/language/expressions/references.mdx @@ -292,7 +292,7 @@ Note that unlike `count`, splat expressions are _not_ directly applicable to res When defining the schema for a resource type, a provider developer can mark certain attributes as _sensitive_, in which case Terraform will show a -placeholder marker `(sensitive)` instead of the actual value when rendering +placeholder marker `(sensitive value)` instead of the actual value when rendering a plan involving that attribute. A provider attribute marked as sensitive behaves similarly to an diff --git a/website/docs/language/functions/nonsensitive.mdx b/website/docs/language/functions/nonsensitive.mdx index df3186d18dba..7518eb2aa03f 100644 --- a/website/docs/language/functions/nonsensitive.mdx +++ b/website/docs/language/functions/nonsensitive.mdx @@ -91,11 +91,11 @@ the local value `mixed_content`, with a valid JSON string assigned to ``` > var.mixed_content_json -(sensitive) +(sensitive value) > local.mixed_content -(sensitive) +(sensitive value) > local.mixed_content["password"] -(sensitive) +(sensitive value) > nonsensitive(local.mixed_content["username"]) "zqb" > nonsensitive("clear") diff --git a/website/docs/language/functions/sensitive.mdx b/website/docs/language/functions/sensitive.mdx index b3a3bf220d8d..e974f3ec1bcc 100644 --- a/website/docs/language/functions/sensitive.mdx +++ b/website/docs/language/functions/sensitive.mdx @@ -34,9 +34,9 @@ because they may be exposed in other ways outside of Terraform's control. ``` > sensitive(1) -(sensitive) +(sensitive value) > sensitive("hello") -(sensitive) +(sensitive value) > sensitive([]) -(sensitive) +(sensitive value) ``` diff --git a/website/docs/language/values/outputs.mdx b/website/docs/language/values/outputs.mdx index 9b0fe88f0e3a..708157f0581e 100644 --- a/website/docs/language/values/outputs.mdx +++ b/website/docs/language/values/outputs.mdx @@ -159,7 +159,7 @@ Terraform will perform the following actions: # test_instance.x will be created + resource "test_instance" "x" { - + some_attribute = (sensitive) + + some_attribute = (sensitive value) } Plan: 1 to add, 0 to change, 0 to destroy. diff --git a/website/docs/language/values/variables.mdx b/website/docs/language/values/variables.mdx index 8fcc1e4962fb..9ec218a729d9 100644 --- a/website/docs/language/values/variables.mdx +++ b/website/docs/language/values/variables.mdx @@ -218,8 +218,8 @@ Terraform will perform the following actions: # some_resource.a will be created + resource "some_resource" "a" { - + name = (sensitive) - + address = (sensitive) + + name = (sensitive value) + + address = (sensitive value) } Plan: 1 to add, 0 to change, 0 to destroy. @@ -262,7 +262,7 @@ If a resource attribute is used as, or part of, the provider-defined resource id + resource "random_pet" "animal" { + id = (known after apply) + length = 2 - + prefix = (sensitive) + + prefix = (sensitive value) + separator = "-" }