From fe554942073416fc4a7f9deacd1f281402e5563d Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Fri, 17 Apr 2020 02:39:39 -0700 Subject: [PATCH] Add `debug_mode` to PowerShell provisioner (#8996) --- provisioner/powershell/provisioner.go | 22 +++++++++++-- .../powershell/provisioner.hcl2spec.go | 2 ++ provisioner/powershell/provisioner_test.go | 31 +++++++++++++++++++ .../pages/docs/provisioners/powershell.mdx | 7 +++++ 4 files changed, 60 insertions(+), 2 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index a31f787a7..eba7142f7 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -78,6 +78,15 @@ type Config struct { remoteCleanUpScriptPath string + // If set, sets PowerShell's [PSDebug mode](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-psdebug?view=powershell-7) + // in order to make script debugging easier. For instance, setting the + // value to 1 results in adding this to the execute command: + // + // ``` powershell + // Set-PSDebug -Trace 1 + // ``` + DebugMode int `mapstructure:"debug_mode"` + ctx interpolate.Context } @@ -89,8 +98,13 @@ type Provisioner struct { func (p *Provisioner) defaultExecuteCommand() string { baseCmd := `& { if (Test-Path variable:global:ProgressPreference)` + - `{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};` + - `. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }` + `{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};` + + if p.config.DebugMode != 0 { + baseCmd += fmt.Sprintf(`Set-PsDebug -Trace %d;`, p.config.DebugMode) + } + + baseCmd += `. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }` if p.config.ExecutionPolicy == ExecutionPolicyNone { return baseCmd @@ -208,6 +222,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { `"unrestricted", "none".`)) } + if !(p.config.DebugMode >= 0 && p.config.DebugMode <= 2) { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("%d is an invalid Trace level for `debug_mode`; valid values are 0, 1, and 2", p.config.DebugMode)) + } + if errs != nil { return errs } diff --git a/provisioner/powershell/provisioner.hcl2spec.go b/provisioner/powershell/provisioner.hcl2spec.go index a8dd4cb94..557e23563 100644 --- a/provisioner/powershell/provisioner.hcl2spec.go +++ b/provisioner/powershell/provisioner.hcl2spec.go @@ -33,6 +33,7 @@ type FlatConfig struct { ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user"` ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password"` ExecutionPolicy *string `mapstructure:"execution_policy" cty:"execution_policy"` + DebugMode *int `mapstructure:"debug_mode" cty:"debug_mode"` } // FlatMapstructure returns a new FlatConfig. @@ -71,6 +72,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false}, "elevated_password": &hcldec.AttrSpec{Name: "elevated_password", Type: cty.String, Required: false}, "execution_policy": &hcldec.AttrSpec{Name: "execution_policy", Type: cty.String, Required: false}, + "debug_mode": &hcldec.AttrSpec{Name: "debug_mode", Type: cty.Number, Required: false}, } return s } diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 2e6fd1427..d556bd400 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -104,7 +104,38 @@ func TestProvisionerPrepare_Config(t *testing.T) { if p.config.ElevatedPassword != "mypassword" { t.Fatalf("Expected 'mypassword' for key `elevated_password`: %s", p.config.ElevatedPassword) } +} +func TestProvisionerPrepare_DebugMode(t *testing.T) { + config := testConfig() + config["debug_mode"] = 1 + + var p Provisioner + err := p.Prepare(config) + if err != nil { + t.Fatalf("err: %s", err) + } + + command := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};Set-PsDebug -Trace 1;. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` + if p.config.ExecuteCommand != command { + t.Fatalf(fmt.Sprintf(`Expected command should be '%s' but got '%s'`, command, p.config.ExecuteCommand)) + } +} + +func TestProvisionerPrepare_InvalidDebugMode(t *testing.T) { + config := testConfig() + config["debug_mode"] = -1 + + var p Provisioner + err := p.Prepare(config) + if err == nil { + t.Fatalf("should have error") + } + + message := "invalid Trace level for `debug_mode`; valid values are 0, 1, and 2" + if !strings.Contains(err.Error(), message) { + t.Fatalf("expected Prepare() error %q to contain %q", err.Error(), message) + } } func TestProvisionerPrepare_InvalidKey(t *testing.T) { diff --git a/website/pages/docs/provisioners/powershell.mdx b/website/pages/docs/provisioners/powershell.mdx index 93cbd2cc7..4d7fa7cec 100644 --- a/website/pages/docs/provisioners/powershell.mdx +++ b/website/pages/docs/provisioners/powershell.mdx @@ -33,6 +33,13 @@ The example below is fully functional. @include 'provisioners/shell-config.mdx' +- `debug_mode` - If set, sets PowerShell's [PSDebug mode](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-psdebug?view=powershell-7) + in order to make script debugging easier. For instance, setting the value to 1 results in adding this to the execute command: + + ``` powershell + Set-PSDebug -Trace 1 + ``` + - `elevated_execute_command` (string) - The command to use to execute the elevated script. By default this is as follows: