mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
jsonplan and jsonstate: include sensitive_values in state representations (#28889)
* jsonplan and jsonstate: include sensitive_values in state representations A sensitive_values field has been added to the resource in state and planned values which is a map of all sensitive attributes with the values set to true. It wasn't entirely clear to me if the values in state would suffice, or if we also need to consult the schema - I believe that this is sufficient for state files written since v0.15, and if that's incorrect or insufficient, I'll add in the provider schema check as well. I also updated the documentation, and, since we've considered this before, bumped the FormatVersions for both jsonstate and jsonplan.
This commit is contained in:
parent
9ca3cb4233
commit
ac03d35997
@ -22,7 +22,7 @@ import (
|
||||
// FormatVersion represents the version of the json format and will be
|
||||
// incremented for any change to this format that requires changes to a
|
||||
// consuming parser.
|
||||
const FormatVersion = "0.1"
|
||||
const FormatVersion = "0.2"
|
||||
|
||||
// Plan is the top-level representation of the json format of a plan. It includes
|
||||
// the complete config and current state.
|
||||
@ -259,8 +259,8 @@ func (p *plan) marshalResourceDrift(oldState, newState *states.State, schemas *t
|
||||
} else {
|
||||
newVal = cty.NullVal(ty)
|
||||
}
|
||||
oldSensitive := sensitiveAsBool(oldVal)
|
||||
newSensitive := sensitiveAsBool(newVal)
|
||||
oldSensitive := jsonstate.SensitiveAsBool(oldVal)
|
||||
newSensitive := jsonstate.SensitiveAsBool(newVal)
|
||||
oldVal, _ = oldVal.UnmarkDeep()
|
||||
newVal, _ = newVal.UnmarkDeep()
|
||||
|
||||
@ -367,7 +367,7 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
||||
if schema.ContainsSensitive() {
|
||||
marks = append(marks, schema.ValueMarks(changeV.Before, nil)...)
|
||||
}
|
||||
bs := sensitiveAsBool(changeV.Before.MarkWithPaths(marks))
|
||||
bs := jsonstate.SensitiveAsBool(changeV.Before.MarkWithPaths(marks))
|
||||
beforeSensitive, err = ctyjson.Marshal(bs, bs.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -396,7 +396,7 @@ func (p *plan) marshalResourceChanges(changes *plans.Changes, schemas *terraform
|
||||
if schema.ContainsSensitive() {
|
||||
marks = append(marks, schema.ValueMarks(changeV.After, nil)...)
|
||||
}
|
||||
as := sensitiveAsBool(changeV.After.MarkWithPaths(marks))
|
||||
as := jsonstate.SensitiveAsBool(changeV.After.MarkWithPaths(marks))
|
||||
afterSensitive, err = ctyjson.Marshal(as, as.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
@ -682,88 +682,6 @@ 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():
|
||||
if !val.IsKnown() {
|
||||
// If the collection is unknown we can't say anything about the
|
||||
// sensitivity of its contents
|
||||
return cty.EmptyTupleVal
|
||||
}
|
||||
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():
|
||||
if !val.IsKnown() {
|
||||
// If the map/object is unknown we can't say anything about the
|
||||
// sensitivity of its attributes
|
||||
return cty.EmptyObjectVal
|
||||
}
|
||||
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":
|
||||
|
@ -265,219 +265,6 @@ 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,
|
||||
}),
|
||||
},
|
||||
{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.UnknownVal(cty.List(cty.String)),
|
||||
"set": cty.UnknownVal(cty.Set(cty.Bool)),
|
||||
"tuple": cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Number})),
|
||||
"map": cty.UnknownVal(cty.Map(cty.String)),
|
||||
"object": cty.UnknownVal(cty.Object(map[string]cty.Type{"a": cty.String})),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.EmptyTupleVal,
|
||||
"set": cty.EmptyTupleVal,
|
||||
"tuple": cty.EmptyTupleVal,
|
||||
"map": cty.EmptyObjectVal,
|
||||
"object": 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodePaths(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Input cty.PathSet
|
||||
|
@ -1,6 +1,8 @@
|
||||
package jsonplan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
)
|
||||
|
||||
@ -33,6 +35,10 @@ type resource struct {
|
||||
// unknown values are omitted or set to null, making them indistinguishable
|
||||
// from absent values.
|
||||
AttributeValues attributeValues `json:"values,omitempty"`
|
||||
|
||||
// SensitiveValues is similar to AttributeValues, but with all sensitive
|
||||
// values replaced with true, and all non-sensitive leaf values omitted.
|
||||
SensitiveValues json.RawMessage `json:"sensitive_values,omitempty"`
|
||||
}
|
||||
|
||||
// resourceChange is a description of an individual change action that Terraform
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
ctyjson "github.com/zclconf/go-cty/cty/json"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/command/jsonstate"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
@ -198,6 +199,10 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// copy the marked After values so we can use these in marshalSensitiveValues
|
||||
markedAfter := changeV.After
|
||||
|
||||
// The values may be marked, but we must rely on the Sensitive flag
|
||||
// as the decoded value is only an intermediate step in transcoding
|
||||
// this to a json format.
|
||||
@ -213,6 +218,13 @@ func marshalPlanResources(changes *plans.Changes, ris []addrs.AbsResourceInstanc
|
||||
}
|
||||
}
|
||||
|
||||
s := jsonstate.SensitiveAsBool(markedAfter)
|
||||
v, err := ctyjson.Marshal(s, s.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resource.SensitiveValues = v
|
||||
|
||||
ret = append(ret, resource)
|
||||
}
|
||||
|
||||
@ -262,3 +274,28 @@ func marshalPlanModules(
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// marshalSensitiveValues returns a map of sensitive attributes, with the value
|
||||
// set to true. It returns nil if the value is nil or if there are no sensitive
|
||||
// vals.
|
||||
func marshalSensitiveValues(value cty.Value) map[string]bool {
|
||||
if value.RawEquals(cty.NilVal) || value.IsNull() {
|
||||
return nil
|
||||
}
|
||||
|
||||
ret := make(map[string]bool)
|
||||
|
||||
it := value.ElementIterator()
|
||||
for it.Next() {
|
||||
k, v := it.Element()
|
||||
s := jsonstate.SensitiveAsBool(v)
|
||||
if !s.RawEquals(cty.False) {
|
||||
ret[k.AsString()] = true
|
||||
}
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
||||
"woozles": cty.UnknownVal(cty.String),
|
||||
"foozles": cty.UnknownVal(cty.String),
|
||||
}),
|
||||
Want: []resource{resource{
|
||||
Want: []resource{{
|
||||
Address: "test_thing.example",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -201,6 +201,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
}},
|
||||
Err: false,
|
||||
},
|
||||
@ -234,7 +235,7 @@ func TestMarshalPlanResources(t *testing.T) {
|
||||
"woozles": cty.StringVal("baz"),
|
||||
"foozles": cty.StringVal("bat"),
|
||||
}),
|
||||
Want: []resource{resource{
|
||||
Want: []resource{{
|
||||
Address: "test_thing.example",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -243,10 +244,10 @@ func TestMarshalPlanResources(t *testing.T) {
|
||||
ProviderName: "registry.terraform.io/hashicorp/test",
|
||||
SchemaVersion: 1,
|
||||
AttributeValues: attributeValues{
|
||||
|
||||
"woozles": json.RawMessage(`"baz"`),
|
||||
"foozles": json.RawMessage(`"bat"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
}},
|
||||
Err: false,
|
||||
},
|
||||
|
@ -17,7 +17,7 @@ import (
|
||||
// FormatVersion represents the version of the json format and will be
|
||||
// incremented for any change to this format that requires changes to a
|
||||
// consuming parser.
|
||||
const FormatVersion = "0.1"
|
||||
const FormatVersion = "0.2"
|
||||
|
||||
// state is the top-level representation of the json format of a terraform
|
||||
// state.
|
||||
@ -84,6 +84,10 @@ type resource struct {
|
||||
// from absent values.
|
||||
AttributeValues attributeValues `json:"values,omitempty"`
|
||||
|
||||
// SensitiveValues is similar to AttributeValues, but with all sensitive
|
||||
// values replaced with true, and all non-sensitive leaf values omitted.
|
||||
SensitiveValues json.RawMessage `json:"sensitive_values,omitempty"`
|
||||
|
||||
// DependsOn contains a list of the resource's dependencies. The entries are
|
||||
// addresses relative to the containing module.
|
||||
DependsOn []string `json:"depends_on,omitempty"`
|
||||
@ -193,10 +197,11 @@ func marshalRootModule(s *states.State, schemas *terraform.Schemas) (module, err
|
||||
var err error
|
||||
|
||||
ret.Address = ""
|
||||
ret.Resources, err = marshalResources(s.RootModule().Resources, addrs.RootModuleInstance, schemas)
|
||||
rs, err := marshalResources(s.RootModule().Resources, addrs.RootModuleInstance, schemas)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
ret.Resources = rs
|
||||
|
||||
// build a map of module -> set[child module addresses]
|
||||
moduleChildSet := make(map[string]map[string]struct{})
|
||||
@ -324,6 +329,15 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
||||
|
||||
current.AttributeValues = marshalAttributeValues(riObj.Value)
|
||||
|
||||
// Mark the resource instance value with any marks stored in AttrSensitivePaths so we can build the SensitiveValues object
|
||||
markedVal := riObj.Value.MarkWithPaths(ri.Current.AttrSensitivePaths)
|
||||
s := SensitiveAsBool(markedVal)
|
||||
v, err := ctyjson.Marshal(s, s.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
current.SensitiveValues = v
|
||||
|
||||
if len(riObj.Dependencies) > 0 {
|
||||
dependencies := make([]string, len(riObj.Dependencies))
|
||||
for i, v := range riObj.Dependencies {
|
||||
@ -356,6 +370,15 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
||||
|
||||
deposed.AttributeValues = marshalAttributeValues(riObj.Value)
|
||||
|
||||
// Mark the resource instance value with any marks stored in AttrSensitivePaths so we can build the SensitiveValues object
|
||||
markedVal := riObj.Value.MarkWithPaths(rios.AttrSensitivePaths)
|
||||
s := SensitiveAsBool(markedVal)
|
||||
v, err := ctyjson.Marshal(s, s.Type())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deposed.SensitiveValues = v
|
||||
|
||||
if len(riObj.Dependencies) > 0 {
|
||||
dependencies := make([]string, len(riObj.Dependencies))
|
||||
for i, v := range riObj.Dependencies {
|
||||
@ -379,3 +402,75 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
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():
|
||||
if !val.IsKnown() {
|
||||
// If the collection is unknown we can't say anything about the
|
||||
// sensitivity of its contents
|
||||
return cty.EmptyTupleVal
|
||||
}
|
||||
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():
|
||||
if !val.IsKnown() {
|
||||
// If the map/object is unknown we can't say anything about the
|
||||
// sensitivity of its attributes
|
||||
return cty.EmptyObjectVal
|
||||
}
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
@ -180,7 +181,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
},
|
||||
testSchemas(),
|
||||
[]resource{
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -191,6 +192,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -250,7 +252,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
},
|
||||
testSchemas(),
|
||||
[]resource{
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar[0]",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -261,6 +263,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -291,7 +294,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
},
|
||||
testSchemas(),
|
||||
[]resource{
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar[\"rockhopper\"]",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -302,6 +305,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -319,7 +323,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
|
||||
addrs.NoKey: {
|
||||
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
|
||||
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
|
||||
states.DeposedKey(deposedKey): {
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
||||
},
|
||||
@ -334,7 +338,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
},
|
||||
testSchemas(),
|
||||
[]resource{
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -346,6 +350,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -363,7 +368,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
Instances: map[addrs.InstanceKey]*states.ResourceInstance{
|
||||
addrs.NoKey: {
|
||||
Deposed: map[states.DeposedKey]*states.ResourceInstanceObjectSrc{
|
||||
states.DeposedKey(deposedKey): &states.ResourceInstanceObjectSrc{
|
||||
states.DeposedKey(deposedKey): {
|
||||
Status: states.ObjectReady,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
||||
},
|
||||
@ -382,7 +387,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
},
|
||||
testSchemas(),
|
||||
[]resource{
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -393,8 +398,9 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
resource{
|
||||
{
|
||||
Address: "test_thing.bar",
|
||||
Mode: "managed",
|
||||
Type: "test_thing",
|
||||
@ -406,6 +412,7 @@ func TestMarshalResources(t *testing.T) {
|
||||
"foozles": json.RawMessage(`null`),
|
||||
"woozles": json.RawMessage(`"confuzles"`),
|
||||
},
|
||||
SensitiveValues: json.RawMessage("{}"),
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -423,10 +430,12 @@ func TestMarshalResources(t *testing.T) {
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
eq := reflect.DeepEqual(got, test.Want)
|
||||
if !eq {
|
||||
t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want)
|
||||
|
||||
diff := cmp.Diff(got, test.Want)
|
||||
if diff != "" {
|
||||
t.Fatalf("wrong result: %s\n", diff)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -629,12 +638,12 @@ func TestMarshalModules_parent_no_resources(t *testing.T) {
|
||||
func testSchemas() *terraform.Schemas {
|
||||
return &terraform.Schemas{
|
||||
Providers: map[addrs.Provider]*terraform.ProviderSchema{
|
||||
addrs.NewDefaultProvider("test"): &terraform.ProviderSchema{
|
||||
addrs.NewDefaultProvider("test"): {
|
||||
ResourceTypes: map[string]*configschema.Block{
|
||||
"test_thing": {
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"woozles": {Type: cty.String, Optional: true, Computed: true},
|
||||
"foozles": {Type: cty.String, Optional: true},
|
||||
"foozles": {Type: cty.String, Optional: true, Sensitive: true},
|
||||
},
|
||||
},
|
||||
"test_instance": {
|
||||
@ -649,3 +658,216 @@ func testSchemas() *terraform.Schemas {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}),
|
||||
},
|
||||
{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.UnknownVal(cty.List(cty.String)),
|
||||
"set": cty.UnknownVal(cty.Set(cty.Bool)),
|
||||
"tuple": cty.UnknownVal(cty.Tuple([]cty.Type{cty.String, cty.Number})),
|
||||
"map": cty.UnknownVal(cty.Map(cty.String)),
|
||||
"object": cty.UnknownVal(cty.Object(map[string]cty.Type{"a": cty.String})),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.EmptyTupleVal,
|
||||
"set": cty.EmptyTupleVal,
|
||||
"tuple": cty.EmptyTupleVal,
|
||||
"map": cty.EmptyObjectVal,
|
||||
"object": 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -515,7 +515,7 @@ func TestShow_json_output_state(t *testing.T) {
|
||||
defer testChdir(t, td)()
|
||||
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"test": []string{"1.2.3"},
|
||||
"test": {"1.2.3"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
@ -552,6 +552,7 @@ func TestShow_json_output_state(t *testing.T) {
|
||||
FormatVersion string `json:"format_version,omitempty"`
|
||||
TerraformVersion string `json:"terraform_version"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
SensitiveValues map[string]bool `json:"sensitive_values,omitempty"`
|
||||
}
|
||||
var got, want state
|
||||
|
||||
@ -764,6 +765,7 @@ type plan struct {
|
||||
}
|
||||
|
||||
type priorState struct {
|
||||
FormatVersion string `json:"format_version,omitempty"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
FormatVersion string `json:"format_version,omitempty"`
|
||||
Values map[string]interface{} `json:"values,omitempty"`
|
||||
SensitiveValues map[string]bool `json:"sensitive_values,omitempty"`
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -25,6 +25,9 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"password": "secret"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"ami": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -38,6 +41,9 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"password": "secret"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"ami": true
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -51,13 +57,16 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"password": "secret"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"ami": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.12.0",
|
||||
"values": {
|
||||
"root_module": {
|
||||
@ -15,7 +15,8 @@
|
||||
"values": {
|
||||
"ami": null,
|
||||
"id": "621124146446964903"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.example[1]",
|
||||
@ -28,7 +29,8 @@
|
||||
"values": {
|
||||
"ami": null,
|
||||
"id": "4330206298367988603"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
{
|
||||
"format_version": "0.1"
|
||||
"format_version": "0.2"
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.12.0",
|
||||
"values": {
|
||||
"outputs": {
|
||||
@ -22,7 +22,8 @@
|
||||
"values": {
|
||||
"ami": "bar-var",
|
||||
"id": null
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
],
|
||||
"address": "module.module_test_bar"
|
||||
@ -40,7 +41,8 @@
|
||||
"values": {
|
||||
"ami": "foo-var",
|
||||
"id": null
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
],
|
||||
"address": "module.module_test_foo"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.14.0",
|
||||
"values": {
|
||||
"root_module": {
|
||||
@ -14,6 +14,9 @@
|
||||
"values": {
|
||||
"id": "621124146446964903",
|
||||
"ami": "abc"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"ami": true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -24,7 +24,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[1]",
|
||||
@ -36,7 +37,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[2]",
|
||||
@ -48,13 +50,14 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -24,7 +24,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -87,7 +88,7 @@
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
@ -107,7 +108,8 @@
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test-delete",
|
||||
@ -119,7 +121,8 @@
|
||||
"values": {
|
||||
"ami": "foo",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -24,7 +24,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -67,7 +68,7 @@
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
@ -87,7 +88,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.13.1-dev",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
@ -13,7 +13,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "foo-bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"planned_values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
@ -20,7 +20,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar-var"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
],
|
||||
"address": "module.module_test_bar"
|
||||
@ -37,7 +38,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "module.module_test_foo.test_instance.test[1]",
|
||||
@ -49,7 +51,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "module.module_test_foo.test_instance.test[2]",
|
||||
@ -61,7 +64,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "baz"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
],
|
||||
"address": "module.module_test_foo"
|
||||
@ -70,7 +74,7 @@
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.13.0",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
@ -26,7 +26,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[1]",
|
||||
@ -38,7 +39,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -104,7 +106,7 @@
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"terraform_version": "0.13.0",
|
||||
"values": {
|
||||
"outputs": {
|
||||
@ -126,7 +128,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"child_modules": [
|
||||
@ -17,7 +17,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar-var"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
],
|
||||
"address": "module.my_module.module.more"
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -24,7 +24,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[1]",
|
||||
@ -36,7 +37,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[2]",
|
||||
@ -48,13 +50,14 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "bar"
|
||||
@ -24,7 +24,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[1]",
|
||||
@ -36,7 +37,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
},
|
||||
{
|
||||
"address": "test_instance.test[2]",
|
||||
@ -48,13 +50,14 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "bar"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
@ -12,7 +12,8 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "force-replace"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -47,7 +48,7 @@
|
||||
}
|
||||
],
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
@ -61,7 +62,8 @@
|
||||
"values": {
|
||||
"ami": "bar",
|
||||
"id": "placeholder"
|
||||
}
|
||||
},
|
||||
"sensitive_values": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"variables": {
|
||||
"test_var": {
|
||||
"value": "boop"
|
||||
@ -23,6 +23,9 @@
|
||||
"schema_version": 0,
|
||||
"values": {
|
||||
"ami": "boop"
|
||||
},
|
||||
"sensitive_values": {
|
||||
"ami": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -66,7 +69,7 @@
|
||||
}
|
||||
},
|
||||
"prior_state": {
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
"values": {
|
||||
"outputs": {
|
||||
"test": {
|
||||
|
@ -60,11 +60,11 @@ For ease of consumption by callers, the plan representation includes a partial r
|
||||
|
||||
```javascript
|
||||
{
|
||||
"format_version": "0.1",
|
||||
"format_version": "0.2",
|
||||
|
||||
// "prior_state" is a representation of the state that the configuration is
|
||||
// being applied to, using the state representation described above.
|
||||
"prior_state": <state-representation>,
|
||||
"prior_state": <state-representation>,
|
||||
|
||||
// "configuration" is a representation of the configuration being applied to the
|
||||
// prior state, using the configuration representation described above.
|
||||
@ -236,6 +236,13 @@ The following example illustrates the structure of a `<values-representation>`:
|
||||
"id": "i-abc123",
|
||||
"instance_type": "t2.micro",
|
||||
// etc, etc
|
||||
},
|
||||
|
||||
// "sensitive_values" is the JSON representation of the sensitivity of
|
||||
// the resource's attribute values. Only attributes which are sensitive
|
||||
// are included in this structure.
|
||||
"values": {
|
||||
"id": true,
|
||||
}
|
||||
}
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user