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:
parent
d9ac57ffae
commit
fb98fc98fa
|
@ -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")
|
||||||
|
|
|
@ -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,
|
||||||
|
|
11
terraform/testdata/plan-ignore-changes-sensitive/ignore-changes-sensitive.tf
vendored
Normal file
11
terraform/testdata/plan-ignore-changes-sensitive/ignore-changes-sensitive.tf
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
variable "foo" {
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
ami = var.foo
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
ignore_changes = [ami]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue