terraform: Fix sensitive values in ignore changes

Because ignore_changes configuration can refer to resource arguments
which are assigned sensitive values, we need to unmark the resource
object before processing.
This commit is contained in:
Alisdair McDiarmid 2020-10-20 12:20:52 -04:00
parent d9ac57ffae
commit fb98fc98fa
3 changed files with 81 additions and 11 deletions

View File

@ -4667,6 +4667,65 @@ func TestContext2Plan_ignoreChangesInMap(t *testing.T) {
}), ric.After) }), ric.After)
} }
func TestContext2Plan_ignoreChangesSensitive(t *testing.T) {
m := testModule(t, "plan-ignore-changes-sensitive")
p := testProvider("aws")
p.PlanResourceChangeFn = testDiffFn
state := states.NewState()
root := state.EnsureModule(addrs.RootModuleInstance)
root.SetResourceInstanceCurrent(
mustResourceInstanceAddr("aws_instance.foo").Resource,
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar","ami":"ami-abcd1234","type":"aws_instance"}`),
},
mustProviderConfig(`provider["registry.terraform.io/hashicorp/aws"]`),
)
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
Variables: InputValues{
"foo": &InputValue{
Value: cty.StringVal("ami-1234abcd"),
SourceType: ValueFromCaller,
},
},
State: state,
})
plan, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}
schema := p.GetSchemaReturn.ResourceTypes["aws_instance"]
ty := schema.ImpliedType()
if len(plan.Changes.Resources) != 1 {
t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
}
res := plan.Changes.Resources[0]
ric, err := res.Decode(ty)
if err != nil {
t.Fatal(err)
}
if ric.Addr.String() != "aws_instance.foo" {
t.Fatalf("unexpected resource: %s", ric.Addr)
}
checkVals(t, objectVal(t, schema, map[string]cty.Value{
"id": cty.StringVal("bar"),
"ami": cty.StringVal("ami-abcd1234"),
"type": cty.StringVal("aws_instance"),
}), ric.After)
}
func TestContext2Plan_moduleMapLiteral(t *testing.T) { func TestContext2Plan_moduleMapLiteral(t *testing.T) {
m := testModule(t, "plan-module-map-literal") m := testModule(t, "plan-module-map-literal")
p := testProvider("aws") p := testProvider("aws")

View File

@ -202,24 +202,24 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
priorVal = cty.NullVal(schema.ImpliedType()) priorVal = cty.NullVal(schema.ImpliedType())
} }
// Create an unmarked version of our config val and our prior val.
// Store the paths for the config val to re-markafter
// we've sent things over the wire.
unmarkedConfigVal, unmarkedPaths := origConfigVal.UnmarkDeepWithPaths()
unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths()
// ignore_changes is meant to only apply to the configuration, so it must // ignore_changes is meant to only apply to the configuration, so it must
// be applied before we generate a plan. This ensures the config used for // be applied before we generate a plan. This ensures the config used for
// the proposed value, the proposed value itself, and the config presented // the proposed value, the proposed value itself, and the config presented
// to the provider in the PlanResourceChange request all agree on the // to the provider in the PlanResourceChange request all agree on the
// starting values. // starting values.
configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(priorVal, origConfigVal) configValIgnored, ignoreChangeDiags := n.processIgnoreChanges(unmarkedPriorVal, unmarkedConfigVal)
diags = diags.Append(ignoreChangeDiags) diags = diags.Append(ignoreChangeDiags)
if ignoreChangeDiags.HasErrors() { if ignoreChangeDiags.HasErrors() {
return nil, diags.Err() return nil, diags.Err()
} }
// Create an unmarked version of our config val and our prior val. proposedNewVal := objchange.ProposedNewObject(schema, unmarkedPriorVal, configValIgnored)
// Store the paths for the config val to re-markafter
// we've sent things over the wire.
unmarkedConfigVal, unmarkedPaths := configValIgnored.UnmarkDeepWithPaths()
unmarkedPriorVal, priorPaths := priorVal.UnmarkDeepWithPaths()
proposedNewVal := objchange.ProposedNewObject(schema, unmarkedPriorVal, unmarkedConfigVal)
// Call pre-diff hook // Call pre-diff hook
if !n.Stub { if !n.Stub {
@ -238,7 +238,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
validateResp := provider.ValidateResourceTypeConfig( validateResp := provider.ValidateResourceTypeConfig(
providers.ValidateResourceTypeConfigRequest{ providers.ValidateResourceTypeConfigRequest{
TypeName: n.Addr.Resource.Type, TypeName: n.Addr.Resource.Type,
Config: unmarkedConfigVal, Config: configValIgnored,
}, },
) )
if validateResp.Diagnostics.HasErrors() { if validateResp.Diagnostics.HasErrors() {
@ -247,7 +247,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{ resp := provider.PlanResourceChange(providers.PlanResourceChangeRequest{
TypeName: n.Addr.Resource.Type, TypeName: n.Addr.Resource.Type,
Config: unmarkedConfigVal, Config: configValIgnored,
PriorState: unmarkedPriorVal, PriorState: unmarkedPriorVal,
ProposedNewState: proposedNewVal, ProposedNewState: proposedNewVal,
PriorPrivate: priorPrivate, PriorPrivate: priorPrivate,
@ -286,7 +286,7 @@ func (n *EvalDiff) Eval(ctx EvalContext) (interface{}, error) {
return nil, diags.Err() return nil, diags.Err()
} }
if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, unmarkedConfigVal, plannedNewVal); len(errs) > 0 { if errs := objchange.AssertPlanValid(schema, unmarkedPriorVal, configValIgnored, plannedNewVal); len(errs) > 0 {
if resp.LegacyTypeSystem { if resp.LegacyTypeSystem {
// The shimming of the old type system in the legacy SDK is not precise // The shimming of the old type system in the legacy SDK is not precise
// enough to pass this consistency check, so we'll give it a pass here, // enough to pass this consistency check, so we'll give it a pass here,

View File

@ -0,0 +1,11 @@
variable "foo" {
sensitive = true
}
resource "aws_instance" "foo" {
ami = var.foo
lifecycle {
ignore_changes = [ami]
}
}