From addedbb680eb4d2ac4df064f712931f6b55bea4e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 2 Feb 2018 09:58:39 -0800 Subject: [PATCH] Revert "Merge pull request #5515 from DanHam/dot-source-env-vars" revert so we can use a branch that's had more recent work done This reverts commit e56849c6056220b8dd555f841f7e0a5028b25af0, reversing changes made to 6d14eb6ea4ffa5cbaa0bad9d62d2dff09f946b15. --- provisioner/powershell/provisioner.go | 52 ++++-------- provisioner/powershell/provisioner_test.go | 85 ++++++------------- .../docs/provisioners/powershell.html.md | 50 +++++------ 3 files changed, 65 insertions(+), 122 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 3d2f16f85..4297acf08 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -113,7 +113,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.EnvVarFormat == "" { - p.config.EnvVarFormat = `$env:%s="%s"; ` + p.config.EnvVarFormat = `$env:%s=\"%s\"; ` } if p.config.ElevatedEnvVarFormat == "" { @@ -121,7 +121,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } if p.config.ExecuteCommand == "" { - p.config.ExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"` + p.config.ExecuteCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"` } if p.config.ElevatedExecuteCommand == "" { @@ -331,19 +331,6 @@ func (p *Provisioner) retryable(f func() error) error { } } -// Enviroment 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 -func (p *Provisioner) prepareEnvVars(elevated bool) (envVarPath string, err error) { - // Collate all required env vars into a plain string with required formatting applied - flattenedEnvVars := p.createFlattenedEnvVars(elevated) - // Create a powershell script on the target build fs containing the flattened env vars - envVarPath, err = p.uploadEnvVars(flattenedEnvVars) - if err != nil { - return "", err - } - return -} - func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) { flattened = "" envVars := make(map[string]string) @@ -380,19 +367,6 @@ func (p *Provisioner) createFlattenedEnvVars(elevated bool) (flattened string) { return } -func (p *Provisioner) uploadEnvVars(flattenedEnvVars string) (envVarPath string, err error) { - // Upload all env vars to a powershell script on the target build file system - envVarReader := strings.NewReader(flattenedEnvVars) - uuid := uuid.TimeOrderedUUID() - envVarPath = fmt.Sprintf(`${env:SYSTEMROOT}\Temp\packer-env-vars-%s.ps1`, uuid) - log.Printf("Uploading env vars to %s", envVarPath) - err = p.communicator.Upload(envVarPath, envVarReader, nil) - if err != nil { - return "", fmt.Errorf("Error uploading ps script containing env vars: %s", err) - } - return -} - func (p *Provisioner) createCommandText() (command string, err error) { // Return the interpolated command if p.config.ElevatedUser == "" { @@ -403,15 +377,12 @@ func (p *Provisioner) createCommandText() (command string, err error) { } func (p *Provisioner) createCommandTextNonPrivileged() (command string, err error) { - // Prepare everything needed to enable the required env vars within the remote environment - envVarPath, err := p.prepareEnvVars(false) - if err != nil { - return "", err - } + // Create environment variables to set before executing the command + flattenedEnvVars := p.createFlattenedEnvVars(false) p.config.ctx.Data = &ExecuteCommandTemplate{ + Vars: flattenedEnvVars, Path: p.config.RemotePath, - Vars: envVarPath, } command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx) @@ -424,10 +395,17 @@ func (p *Provisioner) createCommandTextNonPrivileged() (command string, err erro } func (p *Provisioner) createCommandTextPrivileged() (command string, err error) { - // Prepare everything needed to enable the required env vars within the remote environment - envVarPath, err := p.prepareEnvVars(false) + // Can't double escape the env vars, lets create shiny new ones + flattenedEnvVars := p.createFlattenedEnvVars(true) + // Need to create a mini ps1 script containing all of the environment variables we want; + // we'll be dot-sourcing this later + envVarReader := strings.NewReader(flattenedEnvVars) + uuid := uuid.TimeOrderedUUID() + envVarPath := fmt.Sprintf(`${env:SYSTEMROOT}\Temp\packer-env-vars-%s.ps1`, uuid) + log.Printf("Uploading env vars to %s", envVarPath) + err = p.communicator.Upload(envVarPath, envVarReader, nil) if err != nil { - return "", err + return "", fmt.Errorf("Error preparing elevated powershell script: %s", err) } p.config.ctx.Data = &ExecuteCommandTemplate{ diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 749564d4d..e7e64d52e 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -79,8 +79,8 @@ func TestProvisionerPrepare_Defaults(t *testing.T) { t.Error("expected elevated_password to be empty") } - if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"` { - t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}';exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand) + if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"` { + t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand) } if p.config.ElevatedExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` { @@ -403,7 +403,7 @@ func TestProvisionerProvision_Inline(t *testing.T) { ui := testUi() p := new(Provisioner) - // Defaults provided by Packer - env vars should not appear in cmd + // Defaults provided by Packer p.config.PackerBuildName = "vmware" p.config.PackerBuilderType = "iso" comm := new(packer.MockCommunicator) @@ -413,14 +413,11 @@ func TestProvisionerProvision_Inline(t *testing.T) { t.Fatal("should not have error") } - cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) - matched := re.MatchString(cmd) - if !matched { - t.Fatalf("Got unexpected command: %s", cmd) + expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/inlineScript.ps1';exit $LastExitCode }"` + if comm.StartCmd.Command != expectedCommand { + t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) } - // User supplied env vars should not change things envVars := make([]string, 2) envVars[0] = "FOO=BAR" envVars[1] = "BAR=BAZ" @@ -433,11 +430,9 @@ func TestProvisionerProvision_Inline(t *testing.T) { t.Fatal("should not have error") } - cmd = comm.StartCmd.Command - re = regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/inlineScript.ps1';exit \$LastExitCode }"`) - matched = re.MatchString(cmd) - if !matched { - t.Fatalf("Got unexpected command: %s", cmd) + expectedCommand = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/inlineScript.ps1';exit $LastExitCode }"` + if comm.StartCmd.Command != expectedCommand { + t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) } } @@ -460,12 +455,11 @@ func TestProvisionerProvision_Scripts(t *testing.T) { t.Fatal("should not have error") } - cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) - matched := re.MatchString(cmd) - if !matched { - t.Fatalf("Got unexpected command: %s", cmd) + expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` + if comm.StartCmd.Command != expectedCommand { + t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) } + } func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { @@ -494,11 +488,9 @@ func TestProvisionerProvision_ScriptsWithEnvVars(t *testing.T) { t.Fatal("should not have error") } - cmd := comm.StartCmd.Command - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) - matched := re.MatchString(cmd) - if !matched { - t.Fatalf("Got unexpected command: %s", cmd) + expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:BAR=\"BAZ\"; $env:FOO=\"BAR\"; $env:PACKER_BUILDER_TYPE=\"footype\"; $env:PACKER_BUILD_NAME=\"foobuild\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` + if comm.StartCmd.Command != expectedCommand { + t.Fatalf("Expect command to be: %s, got %s", expectedCommand, comm.StartCmd.Command) } } @@ -555,11 +547,11 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) { {"FOO==bar"}, // User env var with value starting with equals } expected := []string{ - `$env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, - `$env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, - `$env:BAZ="qux"; $env:FOO="bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, - `$env:FOO="bar=baz"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, - `$env:FOO="=bar"; $env:PACKER_BUILDER_TYPE="iso"; $env:PACKER_BUILD_NAME="vmware"; `, + `$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, + `$env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, + `$env:BAZ=\"qux\"; $env:FOO=\"bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, + `$env:FOO=\"bar=baz\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, + `$env:FOO=\"=bar\"; $env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; `, } p := new(Provisioner) @@ -579,6 +571,7 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) { } func TestProvision_createCommandText(t *testing.T) { + config := testConfig() config["remote_path"] = "c:/Windows/Temp/script.ps1" p := new(Provisioner) @@ -593,46 +586,22 @@ func TestProvision_createCommandText(t *testing.T) { // Non-elevated cmd, _ := p.createCommandText() - re := regexp.MustCompile(`powershell -executionpolicy bypass "& { if \(Test-Path variable:global:ProgressPreference\){\$ProgressPreference='SilentlyContinue'};\. \${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1; &'c:/Windows/Temp/script.ps1';exit \$LastExitCode }"`) - matched := re.MatchString(cmd) - if !matched { - t.Fatalf("Got unexpected command: %s", cmd) + expectedCommand := `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};$env:PACKER_BUILDER_TYPE=\"iso\"; $env:PACKER_BUILD_NAME=\"vmware\"; &'c:/Windows/Temp/script.ps1';exit $LastExitCode }"` + + if cmd != expectedCommand { + t.Fatalf("Expected Non-elevated command: %s, got %s", expectedCommand, cmd) } // Elevated p.config.ElevatedUser = "vagrant" p.config.ElevatedPassword = "vagrant" cmd, _ = p.createCommandText() - re = regexp.MustCompile(`powershell -executionpolicy bypass -file "%TEMP%\\packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`) - matched = re.MatchString(cmd) + matched, _ := regexp.MatchString("powershell -executionpolicy bypass -file \"%TEMP%(.{1})packer-elevated-shell.*", cmd) if !matched { t.Fatalf("Got unexpected elevated command: %s", cmd) } } -func TestProvision_uploadEnvVars(t *testing.T) { - p := new(Provisioner) - comm := new(packer.MockCommunicator) - p.communicator = comm - - flattenedEnvVars := `$env:PACKER_BUILDER_TYPE="footype"; $env:PACKER_BUILD_NAME="foobuild";` - - envVarPath, err := p.uploadEnvVars(flattenedEnvVars) - if err != nil { - t.Fatalf("Did not expect error: %s", err.Error()) - } - - if comm.UploadCalled != true { - t.Fatalf("Failed to upload env var file") - } - - re := regexp.MustCompile(`\${env:SYSTEMROOT}\\Temp\\packer-env-vars-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1`) - matched := re.MatchString(envVarPath) - if !matched { - t.Fatalf("Got unexpected path for env var file: %s", envVarPath) - } -} - func TestProvision_generateElevatedShellRunner(t *testing.T) { // Non-elevated diff --git a/website/source/docs/provisioners/powershell.html.md b/website/source/docs/provisioners/powershell.html.md index a096bf2f5..f085a6e82 100644 --- a/website/source/docs/provisioners/powershell.html.md +++ b/website/source/docs/provisioners/powershell.html.md @@ -1,8 +1,8 @@ --- description: | - The shell Packer provisioner provisions machines built by Packer using - shell scripts. Shell provisioning is the easiest way to get software - installed and configured on a machine. + The shell Packer provisioner provisions machines built by Packer using shell + scripts. Shell provisioning is the easiest way to get software installed and + configured on a machine. layout: docs page_title: 'PowerShell - Provisioners' sidebar_current: 'docs-provisioners-powershell' @@ -29,21 +29,20 @@ The example below is fully functional. ## Configuration Reference The reference of available configuration options is listed below. The only -required element is either "inline" or "script". Every other option is -optional. +required element is either "inline" or "script". Every other option is optional. Exactly *one* of the following is required: - `inline` (array of strings) - This is an array of commands to execute. The - commands are concatenated by newlines and turned into a single file, so - they are all executed within the same context. This allows you to change + commands are concatenated by newlines and turned into a single file, so they + are all executed within the same context. This allows you to change directories in one command and use something in the directory in the next and so on. Inline scripts are the easiest way to pull off simple tasks within the machine. - `script` (string) - The path to a script to upload and execute in - the machine. This path can be absolute or relative. If it is relative, it - is relative to the working directory when Packer is executed. + the machine. This path can be absolute or relative. If it is relative, it is + relative to the working directory when Packer is executed. - `scripts` (array of strings) - An array of scripts to execute. The scripts will be uploaded and executed in the order specified. Each script is @@ -52,12 +51,12 @@ Exactly *one* of the following is required: Optional parameters: -- `binary` (boolean) - If true, specifies that the script(s) are binary - files, and Packer should therefore not convert Windows line endings to Unix - line endings (if there are any). By default this is false. +- `binary` (boolean) - If true, specifies that the script(s) are binary files, + and Packer should therefore not convert Windows line endings to Unix line + endings (if there are any). By default this is false. -- `elevated_execute_command` (string) - The command to use to execute the - elevated script. By default this is as follows: +- `elevated_execute_command` (string) - The command to use to execute the elevated + script. By default this is as follows: ``` powershell powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }" @@ -66,34 +65,32 @@ Optional parameters: The value of this is treated as [configuration template](/docs/templates/engine.html). There are two available variables: `Path`, which is the path to the script to run, and - `Vars`, which is the location of a temp file containing the list of - `environment_vars`, if configured. + `Vars`, which is the location of a temp file containing the list of `environment_vars`, if configured. - `environment_vars` (array of strings) - An array of key/value pairs to inject prior to the execute\_command. The format should be `key=value`. - Packer injects some environmental variables by default into the - environment, as well, which are covered in the section below. + Packer injects some environmental variables by default into the environment, + as well, which are covered in the section below. - `execute_command` (string) - The command to use to execute the script. By default this is as follows: ``` powershell - powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }" + powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){$ProgressPreference='SilentlyContinue'};{{.Vars}}&'{{.Path}}';exit $LastExitCode }" ``` The value of this is treated as [configuration template](/docs/templates/engine.html). There are two available variables: `Path`, which is the path to the script to run, and - `Vars`, which is the location of a temp file containing the list of - `environment_vars`, if configured. + `Vars`, which is the list of `environment_vars`, if configured. - `elevated_user` and `elevated_password` (string) - If specified, the PowerShell script will be run with elevated privileges using the given Windows user. - `remote_path` (string) - The path where the script will be uploaded to in - the machine. This defaults to "c:/Windows/Temp/script.ps1". This value must - be a writable location and any parent directories must already exist. + the machine. This defaults to "c:/Windows/Temp/script.ps1". This value must be a + writable location and any parent directories must already exist. - `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 @@ -114,10 +111,9 @@ commonly useful environmental variables: This is most useful when Packer is making multiple builds and you want to distinguish them slightly from a common provisioning script. -- `PACKER_BUILDER_TYPE` is the type of the builder that was used to create - the machine that the script is running on. This is useful if you want to - run only certain parts of the script on systems built with certain - builders. +- `PACKER_BUILDER_TYPE` is the type of the builder that was used to create the + machine that the script is running on. This is useful if you want to run + only certain parts of the script on systems built with certain builders. - `PACKER_HTTP_ADDR` If using a builder that provides an http server for file transfer (such as hyperv, parallels, qemu, virtualbox, and vmware), this