diff --git a/terraform/evaluate.go b/terraform/evaluate.go index add559cbc..b403afa08 100644 --- a/terraform/evaluate.go +++ b/terraform/evaluate.go @@ -756,7 +756,7 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc if schema.ContainsSensitive() { val = markProviderSensitiveAttributes(schema, val) } - instances[key] = val + instances[key] = val.MarkWithPaths(change.AfterValMarks) continue } diff --git a/terraform/evaluate_test.go b/terraform/evaluate_test.go index eb07af314..5e838cd0d 100644 --- a/terraform/evaluate_test.go +++ b/terraform/evaluate_test.go @@ -297,6 +297,134 @@ func TestEvaluatorGetResource(t *testing.T) { } } +// GetResource will return a planned object's After value +// if there is a change for that resource instance. +func TestEvaluatorGetResource_changes(t *testing.T) { + // Set up existing state + stateSync := states.BuildState(func(ss *states.SyncState) { + ss.SetResourceInstanceCurrent( + addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), + &states.ResourceInstanceObjectSrc{ + Status: states.ObjectPlanned, + AttrsJSON: []byte(`{"id":"foo", "to_mark_val":"tacos", "sensitive_value":"abc"}`), + }, + addrs.AbsProviderConfig{ + Provider: addrs.NewDefaultProvider("test"), + Module: addrs.RootModule, + }, + ) + }).SyncWrapper() + + // Create a change for the existing state resource, + // to exercise retrieving the After value of the change + changesSync := plans.NewChanges().SyncWrapper() + change := &plans.ResourceInstanceChange{ + Addr: mustResourceInstanceAddr("test_resource.foo"), + ProviderAddr: addrs.AbsProviderConfig{ + Module: addrs.RootModule, + Provider: addrs.NewDefaultProvider("test"), + }, + Change: plans.Change{ + Action: plans.Update, + // Provide an After value that contains a marked value + After: cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + "to_mark_val": cty.StringVal("pizza").Mark("sensitive"), + "sensitive_value": cty.StringVal("abc"), + }), + }, + } + + // Set up our schemas + schemas := &Schemas{ + Providers: map[addrs.Provider]*ProviderSchema{ + addrs.NewDefaultProvider("test"): { + Provider: &configschema.Block{}, + ResourceTypes: map[string]*configschema.Block{ + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": { + Type: cty.String, + Computed: true, + }, + "to_mark_val": { + Type: cty.String, + Computed: true, + }, + "sensitive_value": { + Type: cty.String, + Computed: true, + Sensitive: true, + }, + }, + }, + }, + }, + }, + } + + // The resource we'll inspect + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + } + schema, _ := schemas.ResourceTypeConfig(addrs.NewDefaultProvider("test"), addr.Mode, addr.Type) + // This encoding separates out the After's marks into its AfterValMarks + csrc, _ := change.Encode(schema.ImpliedType()) + changesSync.AppendResourceInstanceChange(csrc) + + evaluator := &Evaluator{ + Meta: &ContextMeta{ + Env: "foo", + }, + Changes: changesSync, + Config: &configs.Config{ + Module: &configs.Module{ + ManagedResources: map[string]*configs.Resource{ + "test_resource.foo": &configs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_resource", + Name: "foo", + Provider: addrs.Provider{ + Hostname: addrs.DefaultRegistryHost, + Namespace: "hashicorp", + Type: "test", + }, + }, + }, + }, + }, + State: stateSync, + Schemas: schemas, + } + + data := &evaluationStateData{ + Evaluator: evaluator, + } + scope := evaluator.Scope(data, nil) + + want := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("foo"), + "to_mark_val": cty.StringVal("pizza").Mark("sensitive"), + "sensitive_value": cty.StringVal("abc").Mark("sensitive"), + }) + + got, diags := scope.Data.GetResource(addr, tfdiags.SourceRange{}) + + if len(diags) != 0 { + t.Errorf("unexpected diagnostics %s", spew.Sdump(diags)) + } + + if !got.RawEquals(want) { + t.Errorf("wrong result:\ngot: %#v\nwant: %#v", got, want) + } +} + func TestEvaluatorGetModule(t *testing.T) { // Create a new evaluator with an existing state stateSync := states.BuildState(func(ss *states.SyncState) {