website: Document ignore_changes for individual map elements

This also includes a previously-missing test that verifies the behavior
described here, implemented as a planning context test for consistency
with how the other ignore_changes tests are handled.
This commit is contained in:
Martin Atkins 2019-06-18 16:03:33 -07:00
parent b1e92b3769
commit 1bba574fe9
3 changed files with 123 additions and 0 deletions

View File

@ -4870,6 +4870,87 @@ func TestContext2Plan_ignoreChangesWildcard(t *testing.T) {
}
}
func TestContext2Plan_ignoreChangesInMap(t *testing.T) {
p := testProvider("test")
p.GetSchemaReturn = &ProviderSchema{
ResourceTypes: map[string]*configschema.Block{
"test_ignore_changes_map": {
Attributes: map[string]*configschema.Attribute{
"tags": {Type: cty.Map(cty.String), Optional: true},
},
},
},
}
p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
return providers.PlanResourceChangeResponse{
PlannedState: req.ProposedNewState,
}
}
p.DiffFn = testDiffFn
s := states.BuildState(func(ss *states.SyncState) {
ss.SetResourceInstanceCurrent(
addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_ignore_changes_map",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"tags":{"ignored":"from state","other":"from state"}}`),
},
addrs.ProviderConfig{
Type: "test",
}.Absolute(addrs.RootModuleInstance),
)
})
m := testModule(t, "plan-ignore-changes-in-map")
ctx := testContext2(t, &ContextOpts{
Config: m,
ProviderResolver: providers.ResolverFixed(
map[string]providers.Factory{
"test": testProviderFuncFixed(p),
},
),
State: s,
})
plan, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}
schema := p.GetSchemaReturn.ResourceTypes["test_ignore_changes_map"]
ty := schema.ImpliedType()
if got, want := len(plan.Changes.Resources), 1; got != want {
t.Fatalf("wrong number of changes %d; want %d", got, want)
}
res := plan.Changes.Resources[0]
ric, err := res.Decode(ty)
if err != nil {
t.Fatal(err)
}
if res.Action != plans.Update {
t.Fatalf("resource %s should be updated, got %s", ric.Addr, res.Action)
}
if got, want := ric.Addr.String(), "test_ignore_changes_map.foo"; got != want {
t.Fatalf("unexpected resource address %s; want %s", got, want)
}
checkVals(t, objectVal(t, schema, map[string]cty.Value{
"tags": cty.MapVal(map[string]cty.Value{
"ignored": cty.StringVal("from state"),
"other": cty.StringVal("from config"),
}),
}), ric.After)
}
func TestContext2Plan_moduleMapLiteral(t *testing.T) {
m := testModule(t, "plan-module-map-literal")
p := testProvider("aws")

View File

@ -0,0 +1,13 @@
resource "test_ignore_changes_map" "foo" {
tags = {
ignored = "from config"
other = "from config"
}
lifecycle {
ignore_changes = [
tags["ignored"],
]
}
}

View File

@ -436,6 +436,35 @@ meta-arguments are supported:
}
```
You can also ignore specific map elements by writing references like
`tags["Name"]` in the `ignore_changes` list, though with an important
caveat: the ignoring applies only to in-place updates to an existing
key. Adding or removing a key is treated by Terraform as a change to the
containing map itself rather than to the individual key, and so if you
wish to ignore changes to a particular tag made by an external system
you must ensure that the Terraform configuration creates a placeholder
element for that tag name so that the external system changes will be
understood as an in-place edit of that key:
```hcl
resource "aws_instance" "example" {
# ...
tags = {
# Initial value for Name is overridden by our automatic scheduled
# re-tagging process; changes to this are ignored by ignore_changes
# below.
Name = "placeholder"
}
lifecycle {
ignore_changes = [
tags["Name"],
]
}
}
```
Instead of a list, the special keyword `all` may be used to instruct
Terraform to ignore _all_ attributes, which means that Terraform can
create and destroy the remote object but will never propose updates to it.