diff --git a/command/jsonconfig/config.go b/command/jsonconfig/config.go index d021a8b1f..8575bd25c 100644 --- a/command/jsonconfig/config.go +++ b/command/jsonconfig/config.go @@ -59,6 +59,7 @@ type variables map[string]*variable type variable struct { Default json.RawMessage `json:"default,omitempty"` Description string `json:"description,omitempty"` + Sensitive bool `json:"sensitive,omitempty"` } // Resource is the representation of a resource in the config @@ -263,6 +264,7 @@ func marshalModule(c *configs.Config, schemas *terraform.Schemas, addr string) ( vars[k] = &variable{ Default: defaultValJSON, Description: v.Description, + Sensitive: v.Sensitive, } } module.Variables = vars diff --git a/command/jsonplan/plan.go b/command/jsonplan/plan.go index ee12a408c..5a828ccce 100644 --- a/command/jsonplan/plan.go +++ b/command/jsonplan/plan.go @@ -67,9 +67,23 @@ type change struct { // or "after" is unset (respectively). For ["no-op"], the before and after // values are identical. The "after" value will be incomplete if there are // values within it that won't be known until after apply. - Before json.RawMessage `json:"before,omitempty"` - After json.RawMessage `json:"after,omitempty"` + Before json.RawMessage `json:"before,omitempty"` + After json.RawMessage `json:"after,omitempty"` + + // AfterUnknown is an object value with similar structure to After, but + // with all unknown leaf values replaced with true, and all known leaf + // values omitted. This can be combined with After to reconstruct a full + // value after the action, including values which will only be known after + // apply. AfterUnknown json.RawMessage `json:"after_unknown,omitempty"` + + // BeforeSensitive and AfterSensitive are object values with similar + // structure to Before and After, but with all sensitive leaf values + // replaced with true, and all non-sensitive leaf values omitted. These + // objects should be combined with Before and After to prevent accidental + // display of sensitive values in user interfaces. + BeforeSensitive json.RawMessage `json:"before_sensitive,omitempty"` + AfterSensitive json.RawMessage `json:"after_sensitive,omitempty"` } type output struct { @@ -192,12 +206,18 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform } var before, after []byte + var beforeSensitive, afterSensitive []byte var afterUnknown cty.Value if changeV.Before != cty.NilVal { before, err = ctyjson.Marshal(changeV.Before, changeV.Before.Type()) if err != nil { return err } + bs := sensitiveAsBool(changeV.Before.MarkWithPaths(rc.BeforeValMarks)) + beforeSensitive, err = ctyjson.Marshal(bs, bs.Type()) + if err != nil { + return err + } } if changeV.After != cty.NilVal { if changeV.After.IsWhollyKnown() { @@ -218,6 +238,11 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform } afterUnknown = unknownAsBool(changeV.After) } + as := sensitiveAsBool(changeV.After.MarkWithPaths(rc.AfterValMarks)) + afterSensitive, err = ctyjson.Marshal(as, as.Type()) + if err != nil { + return err + } } a, err := ctyjson.Marshal(afterUnknown, afterUnknown.Type()) @@ -226,10 +251,12 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform } r.Change = change{ - Actions: actionString(rc.Action.String()), - Before: json.RawMessage(before), - After: json.RawMessage(after), - AfterUnknown: a, + Actions: actionString(rc.Action.String()), + Before: json.RawMessage(before), + After: json.RawMessage(after), + AfterUnknown: a, + BeforeSensitive: json.RawMessage(beforeSensitive), + AfterSensitive: json.RawMessage(afterSensitive), } if rc.DeposedKey != states.NotDeposed { @@ -297,13 +324,28 @@ func (p *plan) marshalOutputChanges(changes *plans.Changes) error { } } + // The only information we have in the plan about output sensitivity is + // a boolean which is true if the output was or is marked sensitive. As + // a result, BeforeSensitive and AfterSensitive will be identical, and + // either false or true. + outputSensitive := cty.False + if oc.Sensitive { + outputSensitive = cty.True + } + sensitive, err := ctyjson.Marshal(outputSensitive, outputSensitive.Type()) + if err != nil { + return err + } + a, _ := ctyjson.Marshal(afterUnknown, afterUnknown.Type()) c := change{ - Actions: actionString(oc.Action.String()), - Before: json.RawMessage(before), - After: json.RawMessage(after), - AfterUnknown: a, + Actions: actionString(oc.Action.String()), + Before: json.RawMessage(before), + After: json.RawMessage(after), + AfterUnknown: a, + BeforeSensitive: json.RawMessage(sensitive), + AfterSensitive: json.RawMessage(sensitive), } p.OutputChanges[oc.Addr.OutputValue.Name] = c @@ -392,6 +434,9 @@ func omitUnknowns(val cty.Value) cty.Value { // tuple types and all mapping types are converted to object types, since we // assume the result of this is just going to be serialized as JSON (and thus // lose those distinctions) anyway. +// +// For map/object values, all known attribute values will be omitted instead of +// returning false, as this results in a more compact serialization. func unknownAsBool(val cty.Value) cty.Value { ty := val.Type() switch { @@ -439,7 +484,9 @@ func unknownAsBool(val cty.Value) cty.Value { for it.Next() { k, v := it.Element() vAsBool := unknownAsBool(v) - if !vAsBool.RawEquals(cty.False) { // all of the "false"s for known values for more compact serialization + // Omit all of the "false"s for known values for more compact + // serialization + if !vAsBool.RawEquals(cty.False) { vals[k.AsString()] = unknownAsBool(v) } } @@ -455,6 +502,78 @@ func unknownAsBool(val cty.Value) cty.Value { } } +// recursively iterate through a marked cty.Value, replacing sensitive values +// with cty.True and non-sensitive values with cty.False. +// +// The result also normalizes some types: all sequence types are turned into +// tuple types and all mapping types are converted to object types, since we +// assume the result of this is just going to be serialized as JSON (and thus +// lose those distinctions) anyway. +// +// For map/object values, all non-sensitive attribute values will be omitted +// instead of returning false, as this results in a more compact serialization. +func sensitiveAsBool(val cty.Value) cty.Value { + if val.HasMark("sensitive") { + return cty.True + } + + ty := val.Type() + switch { + case val.IsNull(), ty.IsPrimitiveType(), ty.Equals(cty.DynamicPseudoType): + return cty.False + case ty.IsListType() || ty.IsTupleType() || ty.IsSetType(): + length := val.LengthInt() + if length == 0 { + // If there are no elements then we can't have sensitive values + return cty.EmptyTupleVal + } + vals := make([]cty.Value, 0, length) + it := val.ElementIterator() + for it.Next() { + _, v := it.Element() + vals = append(vals, sensitiveAsBool(v)) + } + // The above transform may have changed the types of some of the + // elements, so we'll always use a tuple here in case we've now made + // different elements have different types. Our ultimate goal is to + // marshal to JSON anyway, and all of these sequence types are + // indistinguishable in JSON. + return cty.TupleVal(vals) + case ty.IsMapType() || ty.IsObjectType(): + var length int + switch { + case ty.IsMapType(): + length = val.LengthInt() + default: + length = len(val.Type().AttributeTypes()) + } + if length == 0 { + // If there are no elements then we can't have sensitive values + return cty.EmptyObjectVal + } + vals := make(map[string]cty.Value) + it := val.ElementIterator() + for it.Next() { + k, v := it.Element() + s := sensitiveAsBool(v) + // Omit all of the "false"s for non-sensitive values for more + // compact serialization + if !s.RawEquals(cty.False) { + vals[k.AsString()] = s + } + } + // The above transform may have changed the types of some of the + // elements, so we'll always use an object here in case we've now made + // different elements have different types. Our ultimate goal is to + // marshal to JSON anyway, and all of these mapping types are + // indistinguishable in JSON. + return cty.ObjectVal(vals) + default: + // Should never happen, since the above should cover all types + panic(fmt.Sprintf("sensitiveAsBool cannot handle %#v", val)) + } +} + func actionString(action string) []string { switch { case action == "NoOp": diff --git a/command/jsonplan/plan_test.go b/command/jsonplan/plan_test.go index 2a2e6b146..f6ee837fe 100644 --- a/command/jsonplan/plan_test.go +++ b/command/jsonplan/plan_test.go @@ -262,3 +262,200 @@ func TestUnknownAsBool(t *testing.T) { } } } + +func TestSensitiveAsBool(t *testing.T) { + sensitive := "sensitive" + tests := []struct { + Input cty.Value + Want cty.Value + }{ + { + cty.StringVal("hello"), + cty.False, + }, + { + cty.NullVal(cty.String), + cty.False, + }, + { + cty.StringVal("hello").Mark(sensitive), + cty.True, + }, + { + cty.NullVal(cty.String).Mark(sensitive), + cty.True, + }, + + { + cty.NullVal(cty.DynamicPseudoType).Mark(sensitive), + cty.True, + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})), + cty.False, + }, + { + cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})).Mark(sensitive), + cty.True, + }, + { + cty.DynamicVal, + cty.False, + }, + { + cty.DynamicVal.Mark(sensitive), + cty.True, + }, + + { + cty.ListValEmpty(cty.String), + cty.EmptyTupleVal, + }, + { + cty.ListValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.ListVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("friend").Mark(sensitive), + }), + cty.TupleVal([]cty.Value{ + cty.False, + cty.True, + }), + }, + { + cty.SetValEmpty(cty.String), + cty.EmptyTupleVal, + }, + { + cty.SetValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello")}), + cty.TupleVal([]cty.Value{cty.False}), + }, + { + cty.SetVal([]cty.Value{cty.StringVal("hello").Mark(sensitive)}), + cty.True, + }, + { + cty.EmptyTupleVal.Mark(sensitive), + cty.True, + }, + { + cty.TupleVal([]cty.Value{ + cty.StringVal("hello"), + cty.StringVal("friend").Mark(sensitive), + }), + cty.TupleVal([]cty.Value{ + cty.False, + cty.True, + }), + }, + { + cty.MapValEmpty(cty.String), + cty.EmptyObjectVal, + }, + { + cty.MapValEmpty(cty.String).Mark(sensitive), + cty.True, + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse"), + }), + cty.EmptyObjectVal, + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }), + cty.ObjectVal(map[string]cty.Value{ + "animal": cty.True, + }), + }, + { + cty.MapVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }).Mark(sensitive), + cty.True, + }, + { + cty.EmptyObjectVal, + cty.EmptyObjectVal, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse"), + }), + cty.EmptyObjectVal, + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }), + cty.ObjectVal(map[string]cty.Value{ + "animal": cty.True, + }), + }, + { + cty.ObjectVal(map[string]cty.Value{ + "greeting": cty.StringVal("hello"), + "animal": cty.StringVal("horse").Mark(sensitive), + }).Mark(sensitive), + cty.True, + }, + { + cty.ListVal([]cty.Value{ + cty.ObjectVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + cty.ObjectVal(map[string]cty.Value{ + "a": cty.StringVal("known").Mark(sensitive), + }), + }), + cty.TupleVal([]cty.Value{ + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + }), + }), + }, + { + cty.ListVal([]cty.Value{ + cty.MapValEmpty(cty.String), + cty.MapVal(map[string]cty.Value{ + "a": cty.StringVal("known").Mark(sensitive), + }), + cty.MapVal(map[string]cty.Value{ + "a": cty.UnknownVal(cty.String), + }), + }), + cty.TupleVal([]cty.Value{ + cty.EmptyObjectVal, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.True, + }), + cty.EmptyObjectVal, + }), + }, + } + + for _, test := range tests { + got := sensitiveAsBool(test.Input) + if !reflect.DeepEqual(got, test.Want) { + t.Errorf( + "wrong result\ninput: %#v\ngot: %#v\nwant: %#v", + test.Input, got, test.Want, + ) + } + } +} diff --git a/command/testdata/show-json/basic-create/output.json b/command/testdata/show-json/basic-create/output.json index ff83de3fe..017054bcc 100644 --- a/command/testdata/show-json/basic-create/output.json +++ b/command/testdata/show-json/basic-create/output.json @@ -83,7 +83,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -103,7 +105,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -123,7 +127,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -134,7 +140,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/command/testdata/show-json/basic-delete/output.json b/command/testdata/show-json/basic-delete/output.json index f9580b401..6b29d785f 100644 --- a/command/testdata/show-json/basic-delete/output.json +++ b/command/testdata/show-json/basic-delete/output.json @@ -48,7 +48,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } }, { @@ -66,7 +68,9 @@ "id": "placeholder" }, "after": null, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": false, + "before_sensitive": {} } } ], @@ -77,7 +81,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/command/testdata/show-json/basic-update/output.json b/command/testdata/show-json/basic-update/output.json index 5486bece2..a6779801f 100644 --- a/command/testdata/show-json/basic-update/output.json +++ b/command/testdata/show-json/basic-update/output.json @@ -48,7 +48,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } } ], @@ -59,7 +61,9 @@ ], "before": "bar", "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/command/testdata/show-json/module-depends-on/output.json b/command/testdata/show-json/module-depends-on/output.json index 26e7e218b..5bfb89694 100644 --- a/command/testdata/show-json/module-depends-on/output.json +++ b/command/testdata/show-json/module-depends-on/output.json @@ -35,7 +35,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], diff --git a/command/testdata/show-json/modules/output.json b/command/testdata/show-json/modules/output.json index 898763aad..445f269c2 100644 --- a/command/testdata/show-json/modules/output.json +++ b/command/testdata/show-json/modules/output.json @@ -99,7 +99,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -120,7 +122,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -141,7 +145,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -162,7 +168,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -173,7 +181,9 @@ ], "before": null, "after": "baz", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/command/testdata/show-json/multi-resource-update/output.json b/command/testdata/show-json/multi-resource-update/output.json index 6725c2546..564a4d713 100644 --- a/command/testdata/show-json/multi-resource-update/output.json +++ b/command/testdata/show-json/multi-resource-update/output.json @@ -63,7 +63,9 @@ "ami": "bar", "id": "placeholder" }, - "after_unknown": {} + "after_unknown": {}, + "after_sensitive": {}, + "before_sensitive": {} } }, { @@ -83,7 +85,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -94,7 +98,9 @@ ], "before": "bar", "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "prior_state": { diff --git a/command/testdata/show-json/nested-modules/output.json b/command/testdata/show-json/nested-modules/output.json index 369a58a4d..96e6b39e9 100644 --- a/command/testdata/show-json/nested-modules/output.json +++ b/command/testdata/show-json/nested-modules/output.json @@ -45,7 +45,9 @@ }, "after_unknown": { "id": true - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], diff --git a/command/testdata/show-json/provider-version-no-config/output.json b/command/testdata/show-json/provider-version-no-config/output.json index bef6f9b00..7e0b841f8 100644 --- a/command/testdata/show-json/provider-version-no-config/output.json +++ b/command/testdata/show-json/provider-version-no-config/output.json @@ -83,7 +83,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -103,7 +105,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -123,7 +127,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -134,7 +140,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/command/testdata/show-json/provider-version/output.json b/command/testdata/show-json/provider-version/output.json index 33807107d..eef936ec3 100644 --- a/command/testdata/show-json/provider-version/output.json +++ b/command/testdata/show-json/provider-version/output.json @@ -83,7 +83,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -103,7 +105,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } }, { @@ -123,7 +127,9 @@ }, "after": { "ami": "bar" - } + }, + "after_sensitive": {}, + "before_sensitive": false } } ], @@ -134,7 +140,9 @@ ], "before": null, "after": "bar", - "after_unknown": false + "after_unknown": false, + "before_sensitive": false, + "after_sensitive": false } }, "configuration": { diff --git a/command/testdata/show-json/sensitive-values/main.tf b/command/testdata/show-json/sensitive-values/main.tf new file mode 100644 index 000000000..3f8ba824c --- /dev/null +++ b/command/testdata/show-json/sensitive-values/main.tf @@ -0,0 +1,13 @@ +variable "test_var" { + default = "boop" + sensitive = true +} + +resource "test_instance" "test" { + ami = var.test_var +} + +output "test" { + value = test_instance.test.ami + sensitive = true +} diff --git a/command/testdata/show-json/sensitive-values/output.json b/command/testdata/show-json/sensitive-values/output.json new file mode 100644 index 000000000..51105382a --- /dev/null +++ b/command/testdata/show-json/sensitive-values/output.json @@ -0,0 +1,117 @@ +{ + "format_version": "0.1", + "variables": { + "test_var": { + "value": "boop" + } + }, + "planned_values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": { + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/test", + "schema_version": 0, + "values": { + "ami": "boop" + } + } + ] + } + }, + "resource_changes": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "provider_name": "registry.terraform.io/hashicorp/test", + "name": "test", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "boop" + }, + "after_unknown": { + "id": true + }, + "after_sensitive": { + "ami": true + }, + "before_sensitive": false + } + } + ], + "output_changes": { + "test": { + "actions": [ + "create" + ], + "before": null, + "after": "boop", + "after_unknown": false, + "before_sensitive": true, + "after_sensitive": true + } + }, + "prior_state": { + "format_version": "0.1", + "values": { + "outputs": { + "test": { + "sensitive": true, + "value": "boop" + } + }, + "root_module": {} + } + }, + "configuration": { + "root_module": { + "outputs": { + "test": { + "expression": { + "references": [ + "test_instance.test" + ] + }, + "sensitive": true + } + }, + "resources": [ + { + "address": "test_instance.test", + "mode": "managed", + "type": "test_instance", + "name": "test", + "provider_config_key": "test", + "schema_version": 0, + "expressions": { + "ami": { + "references": [ + "var.test_var" + ] + } + } + } + ], + "variables": { + "test_var": { + "default": "boop", + "sensitive": true + } + } + } + } +} diff --git a/plans/changes_src.go b/plans/changes_src.go index 683c9f174..055f222db 100644 --- a/plans/changes_src.go +++ b/plans/changes_src.go @@ -37,9 +37,6 @@ type ResourceInstanceChangeSrc struct { // RequiredReplace is a set of paths that caused the change action to be // Replace rather than Update. Always nil if the change action is not // Replace. - // - // This is retained only for UI-plan-rendering purposes and so it does not - // currently survive a round-trip through a saved plan file. RequiredReplace cty.PathSet // Private allows a provider to stash any extra data that is opaque to diff --git a/plans/internal/planproto/planfile.pb.go b/plans/internal/planproto/planfile.pb.go index 9fc4daf59..1631da460 100644 --- a/plans/internal/planproto/planfile.pb.go +++ b/plans/internal/planproto/planfile.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0 -// protoc v3.14.0 +// protoc-gen-go v1.26.0-devel +// protoc v3.15.6 // source: planfile.proto package planproto import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -21,10 +20,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - // Action describes the type of action planned for an object. // Not all action values are valid for all object types. type Action int32 @@ -347,6 +342,14 @@ type Change struct { // respectively. // - For no-op, one value is provided that is left unmodified by this non-change. Values []*DynamicValue `protobuf:"bytes,2,rep,name=values,proto3" json:"values,omitempty"` + // An unordered set of paths into the old value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for create. + BeforeSensitivePaths []*Path `protobuf:"bytes,3,rep,name=before_sensitive_paths,json=beforeSensitivePaths,proto3" json:"before_sensitive_paths,omitempty"` + // An unordered set of paths into the new value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for delete. + AfterSensitivePaths []*Path `protobuf:"bytes,4,rep,name=after_sensitive_paths,json=afterSensitivePaths,proto3" json:"after_sensitive_paths,omitempty"` } func (x *Change) Reset() { @@ -395,6 +398,20 @@ func (x *Change) GetValues() []*DynamicValue { return nil } +func (x *Change) GetBeforeSensitivePaths() []*Path { + if x != nil { + return x.BeforeSensitivePaths + } + return nil +} + +func (x *Change) GetAfterSensitivePaths() []*Path { + if x != nil { + return x.AfterSensitivePaths + } + return nil +} + type ResourceInstanceChange struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -941,74 +958,82 @@ var file_planfile_proto_rawDesc = []byte{ 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x22, 0x5e, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x26, - 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x73, 0x22, 0xb9, 0x03, 0x0a, 0x16, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, - 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, - 0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x03, 0x73, 0x74, 0x72, 0x12, 0x12, 0x0a, - 0x03, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, - 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, - 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, - 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, - 0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, - 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, - 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x22, 0x25, 0x0a, 0x0c, 0x52, 0x65, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x61, 0x6e, - 0x61, 0x67, 0x65, 0x64, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x10, 0x01, - 0x42, 0x0e, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, - 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, - 0x70, 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, - 0x61, 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, - 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, - 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, - 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, - 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, - 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x2a, 0x70, 0x0a, 0x06, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, - 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, - 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, - 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, - 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, - 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, - 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x10, 0x07, 0x42, 0x39, - 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, - 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, - 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x70, 0x61, 0x63, 0x65, 0x22, 0xe4, 0x01, 0x0a, 0x06, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, + 0x26, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, + 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x16, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, + 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, + 0x61, 0x74, 0x68, 0x52, 0x14, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x53, 0x65, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x12, 0x40, 0x0a, 0x15, 0x61, 0x66, 0x74, + 0x65, 0x72, 0x5f, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x74, + 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, + 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, 0x13, 0x61, 0x66, 0x74, 0x65, 0x72, 0x53, 0x65, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xb9, 0x03, 0x0a, 0x16, + 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x03, 0x73, 0x74, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x03, 0x73, 0x74, 0x72, 0x12, 0x12, 0x0a, 0x03, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x00, 0x52, 0x03, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x26, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x10, 0x72, 0x65, 0x71, 0x75, 0x69, + 0x72, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x52, + 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x22, 0x25, 0x0a, 0x0c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x64, 0x10, 0x00, 0x12, 0x08, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x10, 0x01, 0x42, 0x0e, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0x68, 0x0a, 0x0c, 0x4f, 0x75, 0x74, 0x70, 0x75, + 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x26, 0x0a, 0x06, 0x63, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x66, + 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x06, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x22, 0x28, 0x0a, 0x0c, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x73, 0x67, 0x70, 0x61, 0x63, 0x6b, 0x22, 0x1e, 0x0a, 0x04, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x22, 0xa5, 0x01, 0x0a, 0x04, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x27, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x50, 0x61, 0x74, + 0x68, 0x2e, 0x53, 0x74, 0x65, 0x70, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x1a, 0x74, 0x0a, + 0x04, 0x53, 0x74, 0x65, 0x70, 0x12, 0x27, 0x0a, 0x0e, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, + 0x0d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x37, + 0x0a, 0x0b, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x74, 0x66, 0x70, 0x6c, 0x61, 0x6e, 0x2e, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x2a, 0x70, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, + 0x04, 0x4e, 0x4f, 0x4f, 0x50, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, + 0x45, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x0a, 0x0a, + 0x06, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x10, 0x05, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x5f, + 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x06, 0x12, 0x16, 0x0a, + 0x12, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x10, 0x07, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x74, 0x65, + 0x72, 0x72, 0x61, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x61, 0x6e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1049,19 +1074,21 @@ var file_planfile_proto_depIdxs = []int32{ 7, // 5: tfplan.Backend.config:type_name -> tfplan.DynamicValue 0, // 6: tfplan.Change.action:type_name -> tfplan.Action 7, // 7: tfplan.Change.values:type_name -> tfplan.DynamicValue - 1, // 8: tfplan.ResourceInstanceChange.mode:type_name -> tfplan.ResourceInstanceChange.ResourceMode - 4, // 9: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change - 9, // 10: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path - 4, // 11: tfplan.OutputChange.change:type_name -> tfplan.Change - 12, // 12: tfplan.Path.steps:type_name -> tfplan.Path.Step - 7, // 13: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue - 8, // 14: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash - 7, // 15: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue - 16, // [16:16] is the sub-list for method output_type - 16, // [16:16] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 9, // 8: tfplan.Change.before_sensitive_paths:type_name -> tfplan.Path + 9, // 9: tfplan.Change.after_sensitive_paths:type_name -> tfplan.Path + 1, // 10: tfplan.ResourceInstanceChange.mode:type_name -> tfplan.ResourceInstanceChange.ResourceMode + 4, // 11: tfplan.ResourceInstanceChange.change:type_name -> tfplan.Change + 9, // 12: tfplan.ResourceInstanceChange.required_replace:type_name -> tfplan.Path + 4, // 13: tfplan.OutputChange.change:type_name -> tfplan.Change + 12, // 14: tfplan.Path.steps:type_name -> tfplan.Path.Step + 7, // 15: tfplan.Plan.VariablesEntry.value:type_name -> tfplan.DynamicValue + 8, // 16: tfplan.Plan.ProviderHashesEntry.value:type_name -> tfplan.Hash + 7, // 17: tfplan.Path.Step.element_key:type_name -> tfplan.DynamicValue + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_planfile_proto_init() } diff --git a/plans/internal/planproto/planfile.proto b/plans/internal/planproto/planfile.proto index f45c3005a..fe5ef8e43 100644 --- a/plans/internal/planproto/planfile.proto +++ b/plans/internal/planproto/planfile.proto @@ -85,6 +85,16 @@ message Change { // respectively. // - For no-op, one value is provided that is left unmodified by this non-change. repeated DynamicValue values = 2; + + // An unordered set of paths into the old value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for create. + repeated Path before_sensitive_paths = 3; + + // An unordered set of paths into the new value which are marked as + // sensitive. Values at these paths should be obscured in human-readable + // output. This set is always empty for delete. + repeated Path after_sensitive_paths = 4; } message ResourceInstanceChange { diff --git a/plans/planfile/tfplan.go b/plans/planfile/tfplan.go index 5162aae75..1c47de5f5 100644 --- a/plans/planfile/tfplan.go +++ b/plans/planfile/tfplan.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/version" + "github.com/zclconf/go-cty/cty" ) const tfplanFormatVersion = 3 @@ -190,6 +191,15 @@ func resourceChangeFromTfplan(rawChange *planproto.ResourceInstanceChange) (*pla ret.DeposedKey = states.DeposedKey(rawChange.DeposedKey) } + ret.RequiredReplace = cty.NewPathSet() + for _, p := range rawChange.RequiredReplace { + path, err := pathFromTfplan(p) + if err != nil { + return nil, fmt.Errorf("invalid path in required replace: %s", err) + } + ret.RequiredReplace.Add(path) + } + change, err := changeFromTfplan(rawChange.Change) if err != nil { return nil, fmt.Errorf("invalid plan for resource %s: %s", ret.Addr, err) @@ -273,6 +283,22 @@ func changeFromTfplan(rawChange *planproto.Change) (*plans.ChangeSrc, error) { } } + sensitive := cty.NewValueMarks("sensitive") + beforeValMarks, err := pathValueMarksFromTfplan(rawChange.BeforeSensitivePaths, sensitive) + if err != nil { + return nil, fmt.Errorf("failed to decode before sensitive paths: %s", err) + } + afterValMarks, err := pathValueMarksFromTfplan(rawChange.AfterSensitivePaths, sensitive) + if err != nil { + return nil, fmt.Errorf("failed to decode after sensitive paths: %s", err) + } + if len(beforeValMarks) > 0 { + ret.BeforeValMarks = beforeValMarks + } + if len(afterValMarks) > 0 { + ret.AfterValMarks = afterValMarks + } + return ret, nil } @@ -414,6 +440,16 @@ func resourceChangeToTfplan(change *plans.ResourceInstanceChangeSrc) (*planproto ret.DeposedKey = string(change.DeposedKey) ret.Provider = change.ProviderAddr.String() + requiredReplace := change.RequiredReplace.List() + ret.RequiredReplace = make([]*planproto.Path, 0, len(requiredReplace)) + for _, p := range requiredReplace { + path, err := pathToTfplan(p) + if err != nil { + return nil, fmt.Errorf("invalid path in required replace: %s", err) + } + ret.RequiredReplace = append(ret.RequiredReplace, path) + } + valChange, err := changeToTfplan(&change.ChangeSrc) if err != nil { return nil, fmt.Errorf("failed to serialize resource %s change: %s", relAddr, err) @@ -433,6 +469,17 @@ func changeToTfplan(change *plans.ChangeSrc) (*planproto.Change, error) { before := valueToTfplan(change.Before) after := valueToTfplan(change.After) + beforeSensitivePaths, err := pathValueMarksToTfplan(change.BeforeValMarks) + if err != nil { + return nil, err + } + afterSensitivePaths, err := pathValueMarksToTfplan(change.AfterValMarks) + if err != nil { + return nil, err + } + ret.BeforeSensitivePaths = beforeSensitivePaths + ret.AfterSensitivePaths = afterSensitivePaths + switch change.Action { case plans.NoOp: ret.Action = planproto.Action_NOOP @@ -472,3 +519,86 @@ func valueToTfplan(val plans.DynamicValue) *planproto.DynamicValue { Msgpack: []byte(val), } } + +func pathValueMarksFromTfplan(paths []*planproto.Path, marks cty.ValueMarks) ([]cty.PathValueMarks, error) { + ret := make([]cty.PathValueMarks, 0, len(paths)) + for _, p := range paths { + path, err := pathFromTfplan(p) + if err != nil { + return nil, err + } + ret = append(ret, cty.PathValueMarks{ + Path: path, + Marks: marks, + }) + } + return ret, nil +} + +func pathValueMarksToTfplan(pvm []cty.PathValueMarks) ([]*planproto.Path, error) { + ret := make([]*planproto.Path, 0, len(pvm)) + for _, p := range pvm { + path, err := pathToTfplan(p.Path) + if err != nil { + return nil, err + } + ret = append(ret, path) + } + return ret, nil +} + +func pathFromTfplan(path *planproto.Path) (cty.Path, error) { + ret := make([]cty.PathStep, 0, len(path.Steps)) + for _, step := range path.Steps { + switch s := step.Selector.(type) { + case *planproto.Path_Step_ElementKey: + dynamicVal, err := valueFromTfplan(s.ElementKey) + if err != nil { + return nil, fmt.Errorf("error decoding path index step: %s", err) + } + ty, err := dynamicVal.ImpliedType() + if err != nil { + return nil, fmt.Errorf("error determining path index type: %s", err) + } + val, err := dynamicVal.Decode(ty) + if err != nil { + return nil, fmt.Errorf("error decoding path index value: %s", err) + } + ret = append(ret, cty.IndexStep{Key: val}) + case *planproto.Path_Step_AttributeName: + ret = append(ret, cty.GetAttrStep{Name: s.AttributeName}) + default: + return nil, fmt.Errorf("Unsupported path step %t", step.Selector) + } + } + return ret, nil +} + +func pathToTfplan(path cty.Path) (*planproto.Path, error) { + steps := make([]*planproto.Path_Step, 0, len(path)) + for _, step := range path { + switch s := step.(type) { + case cty.IndexStep: + value, err := plans.NewDynamicValue(s.Key, s.Key.Type()) + if err != nil { + return nil, fmt.Errorf("Error encoding path step: %s", err) + } + steps = append(steps, &planproto.Path_Step{ + Selector: &planproto.Path_Step_ElementKey{ + ElementKey: valueToTfplan(value), + }, + }) + case cty.GetAttrStep: + steps = append(steps, &planproto.Path_Step{ + Selector: &planproto.Path_Step_AttributeName{ + AttributeName: s.Name, + }, + }) + default: + return nil, fmt.Errorf("Unsupported path step %#v (%t)", step, step) + } + } + return &planproto.Path{ + Steps: steps, + }, nil +} diff --git a/plans/planfile/tfplan_test.go b/plans/planfile/tfplan_test.go index ffd6d1c49..f396338ef 100644 --- a/plans/planfile/tfplan_test.go +++ b/plans/planfile/tfplan_test.go @@ -64,11 +64,27 @@ func TestTFPlanRoundTrip(t *testing.T) { Action: plans.DeleteThenCreate, Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("foo-bar-baz"), + "boop": cty.ListVal([]cty.Value{ + cty.StringVal("beep"), + }), }), objTy), After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{ "id": cty.UnknownVal(cty.String), + "boop": cty.ListVal([]cty.Value{ + cty.StringVal("beep"), + cty.StringVal("honk"), + }), }), objTy), + AfterValMarks: []cty.PathValueMarks{ + { + Path: cty.GetAttrPath("boop").IndexInt(1), + Marks: cty.NewValueMarks("sensitive"), + }, + }, }, + RequiredReplace: cty.NewPathSet( + cty.GetAttrPath("boop"), + ), }, { Addr: addrs.Resource{