Unmark values before showing in JSON

This prevents "sensitive" values from unintentionally
showing as nil when running terraform show -json
This commit is contained in:
Pam Selle 2020-10-28 15:11:45 -04:00
parent e6e0b6ee46
commit 66091ae36c
4 changed files with 79 additions and 51 deletions

View File

@ -9,7 +9,6 @@ import (
ctyjson "github.com/zclconf/go-cty/cty/json" ctyjson "github.com/zclconf/go-cty/cty/json"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/states/statefile" "github.com/hashicorp/terraform/states/statefile"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
@ -100,7 +99,10 @@ type resource struct {
// resource, whose structure depends on the resource type schema. // resource, whose structure depends on the resource type schema.
type attributeValues map[string]interface{} type attributeValues map[string]interface{}
func marshalAttributeValues(value cty.Value, schema *configschema.Block) attributeValues { func marshalAttributeValues(value cty.Value) attributeValues {
// unmark our value to show all values
value, _ = value.UnmarkDeep()
if value == cty.NilVal || value.IsNull() { if value == cty.NilVal || value.IsNull() {
return nil return nil
} }
@ -295,7 +297,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
return nil, err return nil, err
} }
current.AttributeValues = marshalAttributeValues(riObj.Value, schema) current.AttributeValues = marshalAttributeValues(riObj.Value)
if len(riObj.Dependencies) > 0 { if len(riObj.Dependencies) > 0 {
dependencies := make([]string, len(riObj.Dependencies)) dependencies := make([]string, len(riObj.Dependencies))
@ -327,7 +329,7 @@ func marshalResources(resources map[string]*states.Resource, module addrs.Module
return nil, err return nil, err
} }
deposed.AttributeValues = marshalAttributeValues(riObj.Value, schema) deposed.AttributeValues = marshalAttributeValues(riObj.Value)
if len(riObj.Dependencies) > 0 { if len(riObj.Dependencies) > 0 {
dependencies := make([]string, len(riObj.Dependencies)) dependencies := make([]string, len(riObj.Dependencies))

View File

@ -75,60 +75,27 @@ func TestMarshalOutputs(t *testing.T) {
func TestMarshalAttributeValues(t *testing.T) { func TestMarshalAttributeValues(t *testing.T) {
tests := []struct { tests := []struct {
Attr cty.Value Attr cty.Value
Schema *configschema.Block Want attributeValues
Want attributeValues
}{ }{
{ {
cty.NilVal, cty.NilVal,
&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {
Type: cty.String,
Optional: true,
},
},
},
nil, nil,
}, },
{ {
cty.NullVal(cty.String), cty.NullVal(cty.String),
&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {
Type: cty.String,
Optional: true,
},
},
},
nil, nil,
}, },
{ {
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"foo": cty.StringVal("bar"), "foo": cty.StringVal("bar"),
}), }),
&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {
Type: cty.String,
Optional: true,
},
},
},
attributeValues{"foo": json.RawMessage(`"bar"`)}, attributeValues{"foo": json.RawMessage(`"bar"`)},
}, },
{ {
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{
"foo": cty.NullVal(cty.String), "foo": cty.NullVal(cty.String),
}), }),
&configschema.Block{
Attributes: map[string]*configschema.Attribute{
"foo": {
Type: cty.String,
Optional: true,
},
},
},
attributeValues{"foo": json.RawMessage(`null`)}, attributeValues{"foo": json.RawMessage(`null`)},
}, },
{ {
@ -141,18 +108,22 @@ func TestMarshalAttributeValues(t *testing.T) {
cty.StringVal("moon"), cty.StringVal("moon"),
}), }),
}), }),
&configschema.Block{ attributeValues{
Attributes: map[string]*configschema.Attribute{ "bar": json.RawMessage(`{"hello":"world"}`),
"bar": { "baz": json.RawMessage(`["goodnight","moon"]`),
Type: cty.Map(cty.String),
Required: true,
},
"baz": {
Type: cty.List(cty.String),
Optional: true,
},
},
}, },
},
// Marked values
{
cty.ObjectVal(map[string]cty.Value{
"bar": cty.MapVal(map[string]cty.Value{
"hello": cty.StringVal("world"),
}),
"baz": cty.ListVal([]cty.Value{
cty.StringVal("goodnight"),
cty.StringVal("moon").Mark("sensitive"),
}),
}),
attributeValues{ attributeValues{
"bar": json.RawMessage(`{"hello":"world"}`), "bar": json.RawMessage(`{"hello":"world"}`),
"baz": json.RawMessage(`["goodnight","moon"]`), "baz": json.RawMessage(`["goodnight","moon"]`),
@ -161,7 +132,7 @@ func TestMarshalAttributeValues(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
got := marshalAttributeValues(test.Attr, test.Schema) got := marshalAttributeValues(test.Attr)
eq := reflect.DeepEqual(got, test.Want) eq := reflect.DeepEqual(got, test.Want)
if !eq { if !eq {
t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want) t.Fatalf("wrong result:\nGot: %#v\nWant: %#v\n", got, test.Want)

View File

@ -0,0 +1,22 @@
{
"format_version": "0.1",
"terraform_version": "0.14.0",
"values": {
"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": {
"id": "621124146446964903",
"ami": "abc"
}
}
]
}
}
}

View File

@ -0,0 +1,33 @@
{
"version": 4,
"terraform_version": "0.14.0",
"serial": 1,
"lineage": "d7a6880b-6875-288f-13a9-696a65c73036",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "test_instance",
"name": "test",
"provider": "provider[\"registry.terraform.io/hashicorp/test\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "621124146446964903",
"ami": "abc"
},
"sensitive_attributes": [
[
{
"type": "get_attr",
"value": "ami"
}
]
],
"private": "bnVsbA=="
}
]
}
]
}