Merge pull request #26611 from hashicorp/alisdair/sensitive-values-provisioners
Fixes for sensitive values used as input to provisioners
This commit is contained in:
commit
5e047b0a0b
|
@ -4098,11 +4098,14 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) {
|
|||
if val != "computed_value" {
|
||||
t.Fatalf("bad value for foo: %q", val)
|
||||
}
|
||||
req.UIOutput.Output(fmt.Sprintf("Executing: %q", val))
|
||||
|
||||
return
|
||||
}
|
||||
h := new(MockHook)
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
Hooks: []Hook{h},
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
|
@ -4136,6 +4139,14 @@ func TestContext2Apply_Provisioner_compute(t *testing.T) {
|
|||
if !pr.ProvisionResourceCalled {
|
||||
t.Fatalf("provisioner not invoked")
|
||||
}
|
||||
|
||||
// Verify output was rendered
|
||||
if !h.ProvisionOutputCalled {
|
||||
t.Fatalf("ProvisionOutput hook not called")
|
||||
}
|
||||
if got, want := h.ProvisionOutputMessage, `Executing: "computed_value"`; got != want {
|
||||
t.Errorf("expected output to be %q, but was %q", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_provisionerCreateFail(t *testing.T) {
|
||||
|
@ -12052,3 +12063,73 @@ output "out" {
|
|||
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_provisionerSensitive(t *testing.T) {
|
||||
m := testModule(t, "apply-provisioner-sensitive")
|
||||
p := testProvider("aws")
|
||||
pr := testProvisioner()
|
||||
pr.ProvisionResourceFn = func(req provisioners.ProvisionResourceRequest) (resp provisioners.ProvisionResourceResponse) {
|
||||
if req.Config.ContainsMarked() {
|
||||
t.Fatalf("unexpectedly marked config value: %#v", req.Config)
|
||||
}
|
||||
command := req.Config.GetAttr("command")
|
||||
if command.IsMarked() {
|
||||
t.Fatalf("unexpectedly marked command argument: %#v", command.Marks())
|
||||
}
|
||||
req.UIOutput.Output(fmt.Sprintf("Executing: %q", command.AsString()))
|
||||
return
|
||||
}
|
||||
p.ApplyResourceChangeFn = testApplyFn
|
||||
p.PlanResourceChangeFn = testDiffFn
|
||||
|
||||
h := new(MockHook)
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Config: m,
|
||||
Hooks: []Hook{h},
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
Provisioners: map[string]ProvisionerFactory{
|
||||
"shell": testProvisionerFuncFixed(pr),
|
||||
},
|
||||
Variables: InputValues{
|
||||
"password": &InputValue{
|
||||
Value: cty.StringVal("secret"),
|
||||
SourceType: ValueFromCaller,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if _, diags := ctx.Plan(); diags.HasErrors() {
|
||||
logDiagnostics(t, diags)
|
||||
t.Fatal("plan failed")
|
||||
}
|
||||
|
||||
state, diags := ctx.Apply()
|
||||
if diags.HasErrors() {
|
||||
logDiagnostics(t, diags)
|
||||
t.Fatal("apply failed")
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(state.String())
|
||||
expected := strings.TrimSpace(testTerraformApplyProvisionerSensitiveStr)
|
||||
if actual != expected {
|
||||
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
||||
}
|
||||
|
||||
// Verify apply was invoked
|
||||
if !pr.ProvisionResourceCalled {
|
||||
t.Fatalf("provisioner was not called on apply")
|
||||
}
|
||||
|
||||
// Verify output was suppressed
|
||||
if !h.ProvisionOutputCalled {
|
||||
t.Fatalf("ProvisionOutput hook not called")
|
||||
}
|
||||
if got, doNotWant := h.ProvisionOutputMessage, "secret"; strings.Contains(got, doNotWant) {
|
||||
t.Errorf("sensitive value %q included in output:\n%s", doNotWant, got)
|
||||
}
|
||||
if got, want := h.ProvisionOutputMessage, "output suppressed"; !strings.Contains(got, want) {
|
||||
t.Errorf("expected hook to be called with %q, but was:\n%s", want, got)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -683,10 +683,30 @@ func (n *EvalApplyProvisioners) apply(ctx EvalContext, provs []*configs.Provisio
|
|||
})
|
||||
}
|
||||
|
||||
// If our config or connection info contains any marked values, ensure
|
||||
// those are stripped out before sending to the provisioner. Unlike
|
||||
// resources, we have no need to capture the marked paths and reapply
|
||||
// later.
|
||||
unmarkedConfig, configMarks := config.UnmarkDeep()
|
||||
unmarkedConnInfo, _ := connInfo.UnmarkDeep()
|
||||
|
||||
// Marks on the config might result in leaking sensitive values through
|
||||
// provisioner logging, so we conservatively suppress all output in
|
||||
// this case. This should not apply to connection info values, which
|
||||
// provisioners ought not to be logging anyway.
|
||||
if len(configMarks) > 0 {
|
||||
outputFn = func(msg string) {
|
||||
ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
h.ProvisionOutput(absAddr, prov.Type, "(output suppressed due to sensitive value in config)")
|
||||
return HookActionContinue, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
output := CallbackUIOutput{OutputFn: outputFn}
|
||||
resp := provisioner.ProvisionResource(provisioners.ProvisionResourceRequest{
|
||||
Config: config,
|
||||
Connection: connInfo,
|
||||
Config: unmarkedConfig,
|
||||
Connection: unmarkedConnInfo,
|
||||
UIOutput: &output,
|
||||
})
|
||||
applyDiags := resp.Diagnostics.InConfigBody(prov.Config)
|
||||
|
|
|
@ -866,6 +866,13 @@ aws_instance.bar:
|
|||
type = aws_instance
|
||||
`
|
||||
|
||||
const testTerraformApplyProvisionerSensitiveStr = `
|
||||
aws_instance.foo:
|
||||
ID = foo
|
||||
provider = provider["registry.terraform.io/hashicorp/aws"]
|
||||
type = aws_instance
|
||||
`
|
||||
|
||||
const testTerraformApplyDestroyStr = `
|
||||
<no state>
|
||||
`
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
variable "password" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
resource "aws_instance" "foo" {
|
||||
connection {
|
||||
host = "localhost"
|
||||
type = "telnet"
|
||||
user = "superuser"
|
||||
port = 2222
|
||||
password = var.password
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
command = "echo ${var.password} > secrets"
|
||||
}
|
||||
}
|
|
@ -186,6 +186,10 @@ that resource's attributes. For example, use `self.public_ip` to reference an
|
|||
references create dependencies. Referring to a resource by name within its own
|
||||
block would create a dependency cycle.
|
||||
|
||||
## Suppressing Provisioner Logs in CLI Output
|
||||
|
||||
The configuration for a `provisioner` block may use sensitive values, such as [`sensitive` variables](../configuration/variables.html#suppressing-values-in-cli-output) or [`sensitive` output values](../outputs.html#sensitive-suppressing-values-in-cli-output). In this case, all log output from the provider is automatically suppressed to prevent the sensitive values from being displayed.
|
||||
|
||||
## Creation-Time Provisioners
|
||||
|
||||
By default, provisioners run when the resource they are defined within is
|
||||
|
|
Loading…
Reference in New Issue