terraform: Unmark provisioner arguments

If provisioner configuration or connection info includes sensitive
values, we need to unmark them before calling the provisioner. Failing
to do so causes serialization to error.

Unlike resources, we do not need to capture marked paths here, so we
just discard the marks.
This commit is contained in:
Alisdair McDiarmid 2020-10-16 14:52:31 -04:00
parent 31033001a8
commit 9c580335e3
4 changed files with 89 additions and 2 deletions

View File

@ -12052,3 +12052,58 @@ 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())
}
return
}
p.ApplyResourceChangeFn = testApplyFn
p.PlanResourceChangeFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Config: m,
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")
}
}

View File

@ -683,10 +683,17 @@ 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, _ := config.UnmarkDeep()
unmarkedConnInfo, _ := connInfo.UnmarkDeep()
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)

View File

@ -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>
`

View File

@ -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"
}
}