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:
parent
2b73a2c080
commit
bd70bc63eb
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue