From f6a61e25117f013886c0c3e753318878fc249c0b Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Wed, 18 Mar 2020 18:05:36 -0400 Subject: [PATCH 1/5] provisioner/powershell: Add post clean up step to remove temp script files --- provisioner/powershell/provisioner.go | 53 +++++++++++++++++-- .../powershell/provisioner.hcl2spec.go | 2 + 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index fb8083134..e5d28ba56 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -52,6 +52,10 @@ type Config struct { // can be used to inject the environment_vars into the environment. ElevatedExecuteCommand string `mapstructure:"elevated_execute_command"` + // Whether to clean scripts up after executing the provisioner. + // Defaults to false. + SkipClean bool `mapstructure:"skip_clean"` + // The timeout for retrying to start the process. Until this timeout is // reached, if the provisioner can't start a process, it retries. This // can be set high to allow for reboots. @@ -82,12 +86,14 @@ func (p *Provisioner) defaultExecuteCommand() string { baseCmd := `& { if (Test-Path variable:global:ProgressPreference)` + `{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};` + `. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }` + if p.config.ExecutionPolicy == ExecutionPolicyNone { return baseCmd - } else { - return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd) } + + return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd) } + func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } func (p *Provisioner) Prepare(raws ...interface{}) error { @@ -294,11 +300,51 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C if err := p.config.ValidExitCode(cmd.ExitStatus()); err != nil { return err } + + if !p.config.SkipClean { + files := []string{p.config.RemotePath, p.config.RemoteEnvVarPath} + command, err := p.cleanUpRemoteFilesCommand(files...) + if err != nil { + log.Printf("failed to prepare packer cleanup script") + } + + cmd = &packer.RemoteCmd{Command: command} + if err := cmd.RunWithUi(ctx, comm, ui); err != nil { + log.Printf("failed to clean up temporary files") + } + } + } return nil } +func (p *Provisioner) cleanUpRemoteFilesCommand(files ...string) (string, error) { + if len(files) == 0 { + return "", fmt.Errorf("no files provided for cleanup") + } + + var b strings.Builder + baseDir := filepath.Dir(p.config.RemotePath) + uploadPath := filepath.Join(baseDir, fmt.Sprintf("packer-cleanup-%s.ps1", uuid.TimeOrderedUUID())) + // This script should self destruct. + files = append(files, uploadPath) + for _, filename := range files { + fmt.Fprintf(&b, "Remove-Item %s\n", filename) + } + + if err := p.communicator.Upload(uploadPath, strings.NewReader(b.String()), nil); err != nil { + log.Printf("packer clean up script %q failed to upload: %s", uploadPath, err) + } + + data := map[string]string{ + "Path": uploadPath, + "Vars": p.config.RemoteEnvVarPath, + } + p.config.ctx.Data = data + return interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) +} + // Environment variables required within the remote environment are uploaded // within a PS script and then enabled by 'dot sourcing' the script // immediately prior to execution of the main command @@ -388,9 +434,6 @@ func (p *Provisioner) uploadEnvVars(flattenedEnvVars string) (err error) { } return err }) - if err != nil { - return err - } return } diff --git a/provisioner/powershell/provisioner.hcl2spec.go b/provisioner/powershell/provisioner.hcl2spec.go index 81db6dc4f..72bf608bb 100644 --- a/provisioner/powershell/provisioner.hcl2spec.go +++ b/provisioner/powershell/provisioner.hcl2spec.go @@ -27,6 +27,7 @@ type FlatConfig struct { ExecuteCommand *string `mapstructure:"execute_command" cty:"execute_command"` RemoteEnvVarPath *string `mapstructure:"remote_env_var_path" cty:"remote_env_var_path"` ElevatedExecuteCommand *string `mapstructure:"elevated_execute_command" cty:"elevated_execute_command"` + SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean"` StartRetryTimeout *string `mapstructure:"start_retry_timeout" cty:"start_retry_timeout"` ElevatedEnvVarFormat *string `mapstructure:"elevated_env_var_format" cty:"elevated_env_var_format"` ElevatedUser *string `mapstructure:"elevated_user" cty:"elevated_user"` @@ -64,6 +65,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "execute_command": &hcldec.AttrSpec{Name: "execute_command", Type: cty.String, Required: false}, "remote_env_var_path": &hcldec.AttrSpec{Name: "remote_env_var_path", Type: cty.String, Required: false}, "elevated_execute_command": &hcldec.AttrSpec{Name: "elevated_execute_command", Type: cty.String, Required: false}, + "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, "start_retry_timeout": &hcldec.AttrSpec{Name: "start_retry_timeout", Type: cty.String, Required: false}, "elevated_env_var_format": &hcldec.AttrSpec{Name: "elevated_env_var_format", Type: cty.String, Required: false}, "elevated_user": &hcldec.AttrSpec{Name: "elevated_user", Type: cty.String, Required: false}, From 11db6014fa97870ea772d0f75eea0242ed8e7e31 Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Thu, 19 Mar 2020 19:50:58 -0400 Subject: [PATCH 2/5] provisioner/powershell: Update remote clean up logic * Add retry logic so that the provisioner will retry if it fails to upload/execute because of some restart provisioner step * Add a testConfigWithSkipClean for testing that the provisioner executes the correct commands * Add a test case for toggling the "skip_clean" config option --- provisioner/powershell/provisioner.go | 55 ++++++++----- provisioner/powershell/provisioner_test.go | 91 ++++++++++++++++------ 2 files changed, 103 insertions(+), 43 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index e5d28ba56..bb9ecf9ab 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -73,6 +73,8 @@ type Config struct { ExecutionPolicy ExecutionPolicy `mapstructure:"execution_policy"` + remoteCleanUpScriptPath string + ctx interpolate.Context } @@ -155,6 +157,8 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.Vars = make([]string, 0) } + p.config.remoteCleanUpScriptPath = fmt.Sprintf(`c:/Windows/Temp/packer-cleanup-%s.ps1`, uuid.TimeOrderedUUID()) + var errs error if p.config.Script != "" && len(p.config.Scripts) > 0 { errs = packer.MultiErrorAppend(errs, @@ -249,6 +253,8 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C defer os.Remove(temp) } + // every provisioner run will only have one env var script file so lets add it first + uploadedScripts := []string{p.config.RemoteEnvVarPath} for _, path := range scripts { ui.Say(fmt.Sprintf("Provisioning with powershell script: %s", path)) @@ -295,50 +301,57 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C // Close the original file since we copied it f.Close() - log.Printf("%s returned with exit code %d", p.config.RemotePath, cmd.ExitStatus()) + // Record every other uploaded script file so we can clean it up later + uploadedScripts = append(uploadedScripts, p.config.RemotePath) + log.Printf("%s returned with exit code %d", p.config.RemotePath, cmd.ExitStatus()) if err := p.config.ValidExitCode(cmd.ExitStatus()); err != nil { return err } + } - if !p.config.SkipClean { - files := []string{p.config.RemotePath, p.config.RemoteEnvVarPath} - command, err := p.cleanUpRemoteFilesCommand(files...) - if err != nil { - log.Printf("failed to prepare packer cleanup script") - } + if p.config.SkipClean { + return nil + } - cmd = &packer.RemoteCmd{Command: command} - if err := cmd.RunWithUi(ctx, comm, ui); err != nil { - log.Printf("failed to clean up temporary files") - } + err := retry.Config{StartTimeout: p.config.StartRetryTimeout}.Run(ctx, func(ctx context.Context) error { + command, err := p.createRemoteCleanUpCommand(uploadedScripts) + if err != nil { + log.Printf("failed to create a remote cleanup script: %s", err) + return err } + cmd := &packer.RemoteCmd{Command: command} + return cmd.RunWithUi(ctx, comm, ui) + }) + if err != nil { + log.Printf("failed to clean up temporary files: %s", strings.Join(uploadedScripts, ",")) } return nil } -func (p *Provisioner) cleanUpRemoteFilesCommand(files ...string) (string, error) { - if len(files) == 0 { - return "", fmt.Errorf("no files provided for cleanup") +// createRemoteCleanUpCommand will generated a powershell script that will remove remote files; +// returning a command that can be executed remotely to do the cleanup. +func (p *Provisioner) createRemoteCleanUpCommand(remoteFiles []string) (string, error) { + if len(remoteFiles) == 0 { + return "", fmt.Errorf("no remoteFiles provided for cleanup") } var b strings.Builder - baseDir := filepath.Dir(p.config.RemotePath) - uploadPath := filepath.Join(baseDir, fmt.Sprintf("packer-cleanup-%s.ps1", uuid.TimeOrderedUUID())) // This script should self destruct. - files = append(files, uploadPath) - for _, filename := range files { + remotePath := p.config.remoteCleanUpScriptPath + remoteFiles = append(remoteFiles, remotePath) + for _, filename := range remoteFiles { fmt.Fprintf(&b, "Remove-Item %s\n", filename) } - if err := p.communicator.Upload(uploadPath, strings.NewReader(b.String()), nil); err != nil { - log.Printf("packer clean up script %q failed to upload: %s", uploadPath, err) + if err := p.communicator.Upload(remotePath, strings.NewReader(b.String()), nil); err != nil { + return "", fmt.Errorf("clean up script %q failed to upload: %s", remotePath, err) } data := map[string]string{ - "Path": uploadPath, + "Path": remotePath, "Vars": p.config.RemoteEnvVarPath, } p.config.ctx.Data = data diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index edd05c096..2e6fd1427 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -15,16 +15,6 @@ import ( "github.com/stretchr/testify/assert" ) -func testConfig() map[string]interface{} { - return map[string]interface{}{ - "inline": []interface{}{"foo", "bar"}, - } -} - -func init() { - //log.SetOutput(ioutil.Discard) -} - func TestProvisionerPrepare_extractScript(t *testing.T) { config := testConfig() p := new(Provisioner) @@ -335,11 +325,6 @@ func testUi() *packer.BasicUi { } } -func testObjects() (packer.Ui, packer.Communicator) { - ui := testUi() - return ui, new(packer.MockCommunicator) -} - func TestProvisionerProvision_ValidExitCodes(t *testing.T) { config := testConfig() delete(config, "inline") @@ -387,7 +372,8 @@ func TestProvisionerProvision_InvalidExitCodes(t *testing.T) { } func TestProvisionerProvision_Inline(t *testing.T) { - config := testConfig() + // skip_clean is set to true otherwise the last command executed by the provisioner is the cleanup. + config := testConfigWithSkipClean() delete(config, "inline") // Defaults provided by Packer @@ -400,7 +386,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { p.config.PackerBuildName = "vmware" p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) - p.Prepare(config) + _ = p.Prepare(config) err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) if err != nil { t.Fatal("should not have error") @@ -439,7 +425,8 @@ func TestProvisionerProvision_Scripts(t *testing.T) { defer os.Remove(tempFile.Name()) defer tempFile.Close() - config := testConfig() + // skip_clean is set to true otherwise the last command executed by the provisioner is the cleanup. + config := testConfigWithSkipClean() delete(config, "inline") config["scripts"] = []string{tempFile.Name()} config["packer_build_name"] = "foobuild" @@ -465,11 +452,12 @@ func TestProvisionerProvision_Scripts(t *testing.T) { func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { tempFile, _ := ioutil.TempFile("", "packer") - config := testConfig() ui := testUi() defer os.Remove(tempFile.Name()) defer tempFile.Close() + // skip_clean is set to true otherwise the last command executed by the provisioner is the cleanup. + config := testConfigWithSkipClean() delete(config, "inline") config["scripts"] = []string{tempFile.Name()} @@ -499,10 +487,56 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { } } -func TestProvisionerProvision_UISlurp(t *testing.T) { - // UI should be called n times +func TestProvisionerProvision_SkipClean(t *testing.T) { + tempFile, _ := ioutil.TempFile("", "packer") + defer func() { + tempFile.Close() + os.Remove(tempFile.Name()) + }() - // UI should receive following messages / output + config := map[string]interface{}{ + "scripts": []string{tempFile.Name()}, + "remote_path": "c:/Windows/Temp/script.ps1", + } + + tt := []struct { + SkipClean bool + LastExecutedCommandRegex string + }{ + { + SkipClean: true, + LastExecutedCommandRegex: `powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1'; exit \$LastExitCode }"`, + }, + { + SkipClean: false, + LastExecutedCommandRegex: `powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};\. c:/Windows/Temp/packer-ps-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/packer-cleanup-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1'; exit \$LastExitCode }"`, + }, + } + + for _, tc := range tt { + tc := tc + p := new(Provisioner) + ui := testUi() + comm := new(packer.MockCommunicator) + + config["skip_clean"] = tc.SkipClean + if err := p.Prepare(config); err != nil { + t.Fatalf("failed to prepare config when SkipClean is %t: %s", tc.SkipClean, err) + } + err := p.Provision(context.Background(), ui, comm, make(map[string]interface{})) + if err != nil { + t.Fatal("should not have error") + } + + // When SkipClean is false the last executed command should be the clean up command; + // otherwise it will be the execution command for the provisioning script. + cmd := comm.StartCmd.Command + re := regexp.MustCompile(tc.LastExecutedCommandRegex) + matched := re.MatchString(cmd) + if !matched { + t.Fatalf(`Got unexpected command when SkipClean is %t: %s`, tc.SkipClean, cmd) + } + } } func TestProvisionerProvision_UploadFails(t *testing.T) { @@ -771,3 +805,16 @@ func TestCancel(t *testing.T) { // Don't actually call Cancel() as it performs an os.Exit(0) // which kills the 'go test' tool } + +func testConfig() map[string]interface{} { + return map[string]interface{}{ + "inline": []interface{}{"foo", "bar"}, + } +} + +func testConfigWithSkipClean() map[string]interface{} { + return map[string]interface{}{ + "inline": []interface{}{"foo", "bar"}, + "skip_clean": true, + } +} From 91c8afda8e6a645c153317a63f4b7540adbf8daa Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Fri, 3 Apr 2020 07:26:10 -0400 Subject: [PATCH 3/5] provisioner/elevated: Add cleanup logic to remove elevated user scheduled task artifacts --- provisioner/elevated.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/provisioner/elevated.go b/provisioner/elevated.go index d34e97e0a..099d20024 100644 --- a/provisioner/elevated.go +++ b/provisioner/elevated.go @@ -25,6 +25,7 @@ type elevatedOptions struct { TaskDescription string LogFile string XMLEscapedCommand string + ScriptFile string } var psEscape = strings.NewReplacer( @@ -117,6 +118,14 @@ $result = $t.LastTaskResult if (Test-Path $log) { Remove-Item $log -Force -ErrorAction SilentlyContinue | Out-Null } + +$script = [System.Environment]::ExpandEnvironmentVariables("{{.ScriptFile}}") +if (Test-Path $script) { + Remove-Item $script -Force -ErrorAction SilentlyContinue | Out-Null +} +$f = $s.GetFolder("\") +$f.DeleteTask("\$name", "") + [System.Runtime.Interopservices.Marshal]::ReleaseComObject($s) | Out-Null exit $result`)) @@ -166,12 +175,16 @@ func GenerateElevatedRunner(command string, p ElevatedProvisioner) (uploadedPath elevatedPassword, escapedElevatedPassword) } + uuid := uuid.TimeOrderedUUID() + path := fmt.Sprintf(`C:/Windows/Temp/packer-elevated-shell-%s.ps1`, uuid) + // Generate command err = elevatedTemplate.Execute(&buffer, elevatedOptions{ User: escapedElevatedUser, Password: escapedElevatedPassword, TaskName: taskName, TaskDescription: "Packer elevated task", + ScriptFile: path, LogFile: logFile, XMLEscapedCommand: escapedCommand, }) @@ -180,8 +193,6 @@ func GenerateElevatedRunner(command string, p ElevatedProvisioner) (uploadedPath fmt.Printf("Error creating elevated template: %s", err) return "", err } - uuid := uuid.TimeOrderedUUID() - path := fmt.Sprintf(`C:/Windows/Temp/packer-elevated-shell-%s.ps1`, uuid) log.Printf("Uploading elevated shell wrapper for command [%s] to [%s]", command, path) err = p.Communicator().Upload(path, &buffer, nil) if err != nil { From fe721d8e11c1c53a31d32851b5ab3fcf2a4be23f Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Tue, 7 Apr 2020 15:03:58 -0400 Subject: [PATCH 4/5] test/provisioner/powershell: Add acceptance test for powershell provisioner cleanup Passing Tests ``` --- PASS: TestAccPowershellProvisioner_basic (282.02s) --- PASS: TestAccPowershellProvisioner_basic/testing_amazon-ebs_builder_against_powershell_provisioner (282.01s) PASS ok github.com/hashicorp/packer/provisioner/powershell 282.046s ``` Failing tests on master ``` 2020/04/08 09:59:34 Uploading file to 'c:/Windows/Temp/script.bat' 2020/04/08 09:59:36 [INFO] starting remote command: set "PACKER_BUILDER_TYPE=amazon-ebs" && set "PACKER_BUILD_NAME=amazon-ebs" && "c:/Windows/Temp/script.bat" 2020/04/08 09:59:36 ui: amazon-ebs: 2020/04/08 09:59:36 ui: amazon-ebs: C:\Users\Administrator>dir C:\Windows\Temp\packer-*.ps1 2020/04/08 09:59:36 ui: amazon-ebs: Volume in drive C has no label. 2020/04/08 09:59:36 [INFO] command 'set "PACKER_BUILDER_TYPE=amazon-ebs" && set "PACKER_BUILD_NAME=amazon-ebs" && "c:/Windows/Temp/script.bat"' exited with code: 0 2020/04/08 09:59:36 ui: amazon-ebs: Volume Serial Number is 46CA-4083 2020/04/08 09:59:36 ui: amazon-ebs: 2020/04/08 09:59:36 ui: amazon-ebs: Directory of C:\Windows\Temp 2020/04/08 09:59:36 ui: amazon-ebs: 2020/04/08 09:59:36 ui: amazon-ebs: 04/08/2020 01:59 PM 102 packer-acc-test-script-test.ps1 2020/04/08 09:59:36 ui: amazon-ebs: 04/08/2020 01:59 PM 76 packer-acc-test-vars.ps1 2020/04/08 09:59:36 ui: amazon-ebs: 2 File(s) 178 bytes 2020/04/08 09:59:36 ui: amazon-ebs: 0 Dir(s) 9,735,806,976 bytes free 2020/04/08 09:59:36 ui: ==> amazon-ebs: Provisioning step had errors: Running the cleanup provisioner, if present... 2020/04/08 09:59:36 ui: ==> amazon-ebs: Terminating the source AWS instance... 2020/04/08 10:00:09 ui: ==> amazon-ebs: Cleaning up any extra volumes... 2020/04/08 10:00:09 ui: ==> amazon-ebs: No volumes to clean up, skipping 2020/04/08 10:00:09 ui: ==> amazon-ebs: Deleting temporary security group... 2020/04/08 10:00:10 ui: ==> amazon-ebs: Deleting temporary keypair... 2020/04/08 10:00:11 ui error: Build 'amazon-ebs' errored: Script exited with non-zero exit status: 0.Allowed exit codes are: [1] 2020/04/08 10:00:11 machine readable: error-count []string{"1"} 2020/04/08 10:00:11 ui error: ==> Some builds didn't complete successfully and had errors: 2020/04/08 10:00:11 machine readable: amazon-ebs,error []string{"Script exited with non-zero exit status: 0.Allowed exit codes are: [1]"} 2020/04/08 10:00:11 ui error: --> amazon-ebs: Script exited with non-zero exit status: 0.Allowed exit codes are: [1] ``` test: Fix windows-shell command --- provisioner/powershell/provisioner_acc_test.go | 16 ++++++++++++---- .../powershell-provisioner-cleanup.txt | 13 +++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 provisioner/powershell/test-fixtures/powershell-provisioner-cleanup.txt diff --git a/provisioner/powershell/provisioner_acc_test.go b/provisioner/powershell/provisioner_acc_test.go index a6d075ae0..87ad57cac 100644 --- a/provisioner/powershell/provisioner_acc_test.go +++ b/provisioner/powershell/provisioner_acc_test.go @@ -8,24 +8,31 @@ import ( "path/filepath" "testing" - "github.com/hashicorp/packer/provisioner/powershell" - "github.com/hashicorp/go-uuid" "github.com/hashicorp/packer/command" "github.com/hashicorp/packer/helper/tests/acc" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/provisioner/powershell" + windowsshellprovisioner "github.com/hashicorp/packer/provisioner/windows-shell" ) const TestProvisionerName = "powershell" -func TestPowershellProvisioner_Inline(t *testing.T) { +func TestAccPowershellProvisioner_basic(t *testing.T) { + acc.TestProvisionersPreCheck(TestProvisionerName, t) + + testProvisioner := PowershellProvisionerAccTest{"powershell-provisioner-cleanup.txt"} + acc.TestProvisionersAgainstBuilders(&testProvisioner, t) +} + +func TestAccPowershellProvisioner_Inline(t *testing.T) { acc.TestProvisionersPreCheck(TestProvisionerName, t) testProvisioner := PowershellProvisionerAccTest{"powershell-inline-provisioner.txt"} acc.TestProvisionersAgainstBuilders(&testProvisioner, t) } -func TestPowershellProvisioner_Script(t *testing.T) { +func TestAccPowershellProvisioner_Script(t *testing.T) { acc.TestProvisionersPreCheck(TestProvisionerName, t) testProvisioner := PowershellProvisionerAccTest{"powershell-script-provisioner.txt"} @@ -55,6 +62,7 @@ func (s *PowershellProvisionerAccTest) GetConfig() (string, error) { func (s *PowershellProvisionerAccTest) GetProvisionerStore() packer.MapOfProvisioner { return packer.MapOfProvisioner{ TestProvisionerName: func() (packer.Provisioner, error) { return &powershell.Provisioner{}, nil }, + "windows-shell": func() (packer.Provisioner, error) { return &windowsshellprovisioner.Provisioner{}, nil }, } } diff --git a/provisioner/powershell/test-fixtures/powershell-provisioner-cleanup.txt b/provisioner/powershell/test-fixtures/powershell-provisioner-cleanup.txt new file mode 100644 index 000000000..da1408f5b --- /dev/null +++ b/provisioner/powershell/test-fixtures/powershell-provisioner-cleanup.txt @@ -0,0 +1,13 @@ +{ + "type": "powershell", + "remote_path": "c:/Windows/Temp/packer-acc-test-script-test.ps1", + "remote_env_var_path": "C:/Windows/Temp/packer-acc-test-vars.ps1", + "inline": [ + "Write-Host \"Total files found in Temp directory\" ( dir C:/Windows/Temp/packer-*.ps1 | measure).Count;" + ] +}, +{ + "type": "windows-shell", + "inline": ["dir C:\\Windows\\Temp\\packer-*.ps1"], + "valid_exit_codes": ["1"] +} From 4a46d6ff7baa5bc95920c428a1ccdb645e7084d6 Mon Sep 17 00:00:00 2001 From: Wilken Rivera Date: Wed, 8 Apr 2020 00:44:00 -0400 Subject: [PATCH 5/5] docs/provisioner/powershell: Add documentation for `skip_clean` option --- provisioner/powershell/provisioner.go | 5 ++++- website/pages/docs/provisioners/powershell.mdx | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index bb9ecf9ab..a31f787a7 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -53,7 +53,10 @@ type Config struct { ElevatedExecuteCommand string `mapstructure:"elevated_execute_command"` // Whether to clean scripts up after executing the provisioner. - // Defaults to false. + // Defaults to false. When true any script created by a non-elevated Powershell + // provisioner will be removed from the remote machine. Elevated scripts, + // along with the scheduled tasks, will always be removed regardless of the + // value set for `skip_clean`. SkipClean bool `mapstructure:"skip_clean"` // The timeout for retrying to start the process. Until this timeout is diff --git a/website/pages/docs/provisioners/powershell.mdx b/website/pages/docs/provisioners/powershell.mdx index d7ee89076..93cbd2cc7 100644 --- a/website/pages/docs/provisioners/powershell.mdx +++ b/website/pages/docs/provisioners/powershell.mdx @@ -142,6 +142,12 @@ The example below is fully functional. script is uploaded to. The value must be a writable location and any parent directories must already exist. +- `skip_clean` (bool) - Whether to clean scripts up after executing the provisioner. + Defaults to false. When true any script created by a non-elevated Powershell + provisioner will be removed from the remote machine. Elevated scripts, + along with the scheduled tasks, will always be removed regardless of the + value set for `skip_clean`. + - `start_retry_timeout` (string) - The amount of time to attempt to _start_ the remote process. By default this is "5m" or 5 minutes. This setting exists in order to deal with times when SSH may restart, such as a system