Add provider sensitivity propagation experiment

Rolls back marking attributes providers mark as sensitive
to an `experiment` and adds associated docs and adjustments
to the upgrade guide.
This commit is contained in:
Pam Selle 2020-10-26 11:48:17 -04:00
parent 2b73a2c080
commit bd70bc63eb
5 changed files with 132 additions and 15 deletions

View File

@ -13,8 +13,9 @@ type Experiment string
// Each experiment is represented by a string that must be a valid HCL // Each experiment is represented by a string that must be a valid HCL
// identifier so that it can be specified in configuration. // identifier so that it can be specified in configuration.
const ( const (
VariableValidation = Experiment("variable_validation") VariableValidation = Experiment("variable_validation")
ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs") ModuleVariableOptionalAttrs = Experiment("module_variable_optional_attrs")
SuppressProviderSensitiveAttrs = Experiment("provider_sensitive_attrs")
) )
func init() { func init() {
@ -22,6 +23,7 @@ func init() {
// a current or a concluded experiment. // a current or a concluded experiment.
registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.") registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.")
registerCurrentExperiment(ModuleVariableOptionalAttrs) registerCurrentExperiment(ModuleVariableOptionalAttrs)
registerCurrentExperiment(SuppressProviderSensitiveAttrs)
} }
// GetCurrent takes an experiment name and returns the experiment value // GetCurrent takes an experiment name and returns the experiment value

View File

@ -11869,6 +11869,60 @@ variable "sensitive_map" {
resource "test_resource" "foo" { resource "test_resource" "foo" {
value = var.sensitive_map.x value = var.sensitive_map.x
}
`,
})
p := testProvider("test")
p.ApplyResourceChangeFn = testApplyFn
p.PlanResourceChangeFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})
plan, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("plan errors: %s", diags.Err())
}
verifySensitiveValue := func(pvms []cty.PathValueMarks) {
if len(pvms) != 1 {
t.Fatalf("expected 1 sensitive path, got %d", len(pvms))
}
pvm := pvms[0]
if gotPath, wantPath := pvm.Path, cty.GetAttrPath("value"); !gotPath.Equals(wantPath) {
t.Errorf("wrong path\n got: %#v\nwant: %#v", gotPath, wantPath)
}
if gotMarks, wantMarks := pvm.Marks, cty.NewValueMarks("sensitive"); !gotMarks.Equal(wantMarks) {
t.Errorf("wrong marks\n got: %#v\nwant: %#v", gotMarks, wantMarks)
}
}
addr := mustResourceInstanceAddr("test_resource.foo")
fooChangeSrc := plan.Changes.ResourceInstance(addr)
verifySensitiveValue(fooChangeSrc.AfterValMarks)
state, diags := ctx.Apply()
if diags.HasErrors() {
t.Fatalf("apply errors: %s", diags.Err())
}
fooState := state.ResourceInstance(addr)
verifySensitiveValue(fooState.Current.AttrSensitivePaths)
}
func TestContext2Apply_variableSensitivityProviders(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
terraform {
experiments = [provider_sensitive_attrs]
}
resource "test_resource" "foo" {
sensitive_value = "should get marked" sensitive_value = "should get marked"
} }
@ -11917,10 +11971,6 @@ resource "test_resource" "baz" {
} }
} }
addr := mustResourceInstanceAddr("test_resource.foo")
fooChangeSrc := plan.Changes.ResourceInstance(addr)
verifySensitiveValue(fooChangeSrc.AfterValMarks)
// Sensitive attributes (defined by the provider) are marked // Sensitive attributes (defined by the provider) are marked
// as sensitive when referenced from another resource // as sensitive when referenced from another resource
// "bar" references sensitive resources in "foo" // "bar" references sensitive resources in "foo"
@ -11937,9 +11987,6 @@ resource "test_resource" "baz" {
t.Fatalf("apply errors: %s", diags.Err()) t.Fatalf("apply errors: %s", diags.Err())
} }
fooState := state.ResourceInstance(addr)
verifySensitiveValue(fooState.Current.AttrSensitivePaths)
barState := state.ResourceInstance(barAddr) barState := state.ResourceInstance(barAddr)
verifySensitiveValue(barState.Current.AttrSensitivePaths) verifySensitiveValue(barState.Current.AttrSensitivePaths)

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/experiments"
"github.com/hashicorp/terraform/instances" "github.com/hashicorp/terraform/instances"
"github.com/hashicorp/terraform/lang" "github.com/hashicorp/terraform/lang"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
@ -752,9 +753,14 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
continue continue
} }
// EXPERIMENTAL: Suppressing provider-defined sensitive attrs
// from Terraform output.
// If our schema contains sensitive values, mark those as sensitive // If our schema contains sensitive values, mark those as sensitive
if schema.ContainsSensitive() { if moduleConfig.Module.ActiveExperiments.Has(experiments.SuppressProviderSensitiveAttrs) {
val = markProviderSensitiveAttributes(schema, val) if schema.ContainsSensitive() {
val = markProviderSensitiveAttributes(schema, val)
}
} }
instances[key] = val.MarkWithPaths(change.AfterValMarks) instances[key] = val.MarkWithPaths(change.AfterValMarks)
continue continue
@ -774,9 +780,14 @@ func (d *evaluationStateData) GetResource(addr addrs.Resource, rng tfdiags.Sourc
} }
val := ios.Value val := ios.Value
// EXPERIMENTAL: Suppressing provider-defined sensitive attrs
// from Terraform output.
// If our schema contains sensitive values, mark those as sensitive // If our schema contains sensitive values, mark those as sensitive
if schema.ContainsSensitive() { if moduleConfig.Module.ActiveExperiments.Has(experiments.SuppressProviderSensitiveAttrs) {
val = markProviderSensitiveAttributes(schema, val) if schema.ContainsSensitive() {
val = markProviderSensitiveAttributes(schema, val)
}
} }
instances[key] = val instances[key] = val
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/experiments"
"github.com/hashicorp/terraform/plans" "github.com/hashicorp/terraform/plans"
"github.com/hashicorp/terraform/states" "github.com/hashicorp/terraform/states"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
@ -168,6 +169,8 @@ func TestEvaluatorGetResource(t *testing.T) {
ManagedResources: map[string]*configs.Resource{ ManagedResources: map[string]*configs.Resource{
"test_resource.foo": rc, "test_resource.foo": rc,
}, },
// Necessary while provider sensitive attrs are experimental
ActiveExperiments: experiments.NewSet(experiments.SuppressProviderSensitiveAttrs),
}, },
}, },
State: stateSync, State: stateSync,
@ -397,6 +400,8 @@ func TestEvaluatorGetResource_changes(t *testing.T) {
}, },
}, },
}, },
// Necessary while provider sensitive attrs are experimental
ActiveExperiments: experiments.NewSet(experiments.SuppressProviderSensitiveAttrs),
}, },
}, },
State: stateSync, State: stateSync,

View File

@ -321,12 +321,64 @@ instead of the actual value.
Terraform v0.14 introduces a more extensive version of that behavior where Terraform v0.14 introduces a more extensive version of that behavior where
Terraform will track when you write an expression whose result is derived Terraform will track when you write an expression whose result is derived
from a from a
[sensitive input variable](/docs/configuration/outputs.html#sensitive-suppressing-values-in-cli-output), [sensitive input variable](/docs/configuration/outputs.html#sensitive-suppressing-values-in-cli-output) or
[sensitive output value](/docs/configuration/variables.html#suppressing-values-in-cli-output), [sensitive output value](/docs/configuration/variables.html#suppressing-values-in-cli-output),
or an attribute defined as sensitive by a provider,
and so after upgrading to Terraform v0.14 you may find that more values are and so after upgrading to Terraform v0.14 you may find that more values are
obscured in the Terraform plan output than would have been in Terraform v0.13. obscured in the Terraform plan output than would have been in Terraform v0.13.
If a sensitive value (either derived from a sensitive input variable or a sensitive output variable) is used in another module output, that output must be marked `sensitive` as well to be explicit about this data being passed through Terraform:
```terraform
variable "foo" {
sensitive = true
}
output "bar" {
value = var.foo
# sensitive must be true when referencing a sensitive input variable
sensitive = true
}
```
There is also experimental behavior that will extend this sensitivity-awareness to attributes providers define as sensitive. You can enable this feature by activating the experiment in the `terraform` block:
```
terraform {
experiments = [provider_sensitive_attrs]
}
```
If you enable this experiment, attributes that are defined by a given _provider_ as sensitive will have the same sensitivity-tracking behavior as sensitive input values and outputs. For example, the [`vault_generic_secret`](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/generic_secret) data source has an attribute `data` that is sensitive according to this provider's schema.
```
# mod/main.tf
terraform {
experiments = [provider_sensitive_attrs]
}
data "vault_generic_secret" "foobar" {
path = "secret/foobar"
}
output "token" {
value = vault_generic_secret.foobar.data["token"]
# a error will display if sensitive = true is not here
}
```
If you do not add `sensitive = true` to the output referencing that sensitive attribute, you will get an error:
```
Error: Output refers to sensitive values
on mod/main.tf line 6:
6: output "token" {
Expressions used in outputs can only refer to sensitive values if the
sensitive attribute is true.
```
For this feature we've taken the approach that it's better to be conservative For this feature we've taken the approach that it's better to be conservative
and obscure _potentially-sensitive_ values at the expense of potentially also and obscure _potentially-sensitive_ values at the expense of potentially also
obscuring some values that aren't sensitive. Unfortunately this means that obscuring some values that aren't sensitive. Unfortunately this means that