diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 0f2719275..901707c22 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -11,6 +11,7 @@ import ( "context" "errors" "fmt" + "io" "log" "os" "path/filepath" @@ -265,9 +266,43 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } +// extractScript prepares a temporary PowerShell script by prepending environment setup and debug config. +// It copies the contents of the provided script file into the temp script and returns its path. +func extractScript(p *Provisioner, script string) (string, error) { + temp, err := tmp.File("powershell-provisioner-script") + if err != nil { + return "", err + } + + defer temp.Close() + + baseString := `if (Test-Path variable:global:ProgressPreference)` + + `{set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};` + + if p.config.DebugMode != 0 { + baseString += fmt.Sprintf(`Set-PsDebug -Trace %d;`, p.config.DebugMode) + } + baseString += p.createFlattenedEnvVars(p.config.ElevatedUser != "") + if _, err := temp.WriteString(baseString); err != nil { + return "", fmt.Errorf("Error writing PowerShell script: %w", err) + } + + f, err := os.Open(script) + if err != nil { + return "", fmt.Errorf("Error opening powershell script: %s", err) + } + defer f.Close() + if _, err := io.Copy(temp, f); err != nil { + return "", fmt.Errorf("Error copying script contents: %w", err) + } + + return temp.Name(), nil + +} + // Takes the inline scripts, adds a wrapper around the inline scripts, concatenates them into a temporary file and // returns a string containing the location of said file. -func extractScript(p *Provisioner) (string, error) { +func extractInlineScript(p *Provisioner) (string, error) { temp, err := tmp.File("powershell-provisioner") if err != nil { return "", err @@ -280,7 +315,7 @@ func extractScript(p *Provisioner) (string, error) { // we concatenate all the inline commands for _, command := range p.config.Inline { log.Printf("Found command: %s", command) - if _, err := commandBuilder.WriteString(command); err != nil { + if _, err := commandBuilder.WriteString(command + "\n\t"); err != nil { return "", fmt.Errorf("failed to wrap script contents: %w", err) } } @@ -310,11 +345,12 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe p.communicator = comm p.generatedData = generatedData - scripts := make([]string, len(p.config.Scripts)) - copy(scripts, p.config.Scripts) - + var scripts []string + // maps temp script paths to original script paths + tempToOriginalScriptMap := make(map[string]string) if p.config.Inline != nil { - temp, err := extractScript(p) + temp, err := extractInlineScript(p) + tempToOriginalScriptMap[temp] = temp if err != nil { ui.Error(fmt.Sprintf("Unable to extract inline scripts into a file: %s", err)) } @@ -323,12 +359,27 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe defer os.Remove(temp) } + if len(p.config.Scripts) > 0 { + + for _, script := range p.config.Scripts { + temp, err := extractScript(p, script) + tempToOriginalScriptMap[temp] = script + if err != nil { + ui.Error(fmt.Sprintf("Unable to extract script into a file: %s", err)) + } + scripts = append(scripts, temp) + // Defer removal until the function exits + 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)) + ui.Say(fmt.Sprintf("Provisioning with powershell script: %s", tempToOriginalScriptMap[path])) - log.Printf("Opening %s for reading", path) + log.Printf("Opening %s for reading", tempToOriginalScriptMap[path]) fi, err := os.Stat(path) if err != nil { return fmt.Errorf("Error stating powershell script: %s", err) diff --git a/provisioner/powershell/provisioner_acc_test.go b/provisioner/powershell/provisioner_acc_test.go index 90c16ddef..8348b9877 100644 --- a/provisioner/powershell/provisioner_acc_test.go +++ b/provisioner/powershell/provisioner_acc_test.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "runtime" "testing" @@ -82,6 +83,16 @@ func TestAccPowershellProvisioner_Inline(t *testing.T) { return fmt.Errorf("Bad exit code. Logfile: %s", logfile) } } + out, err := os.ReadFile(logfile) + if err != nil { + return err + } + output := string(out) + regexMatchString := "test_env_var: TestValue" + if !regexp.MustCompile(regexp.QuoteMeta(regexMatchString)).MatchString(output) { + t.Errorf("expected env string %q in logs:\n%s", regexMatchString, output) + } + return nil }, } @@ -105,6 +116,16 @@ func TestAccPowershellProvisioner_Script(t *testing.T) { return fmt.Errorf("Bad exit code. Logfile: %s", logfile) } } + + out, err := os.ReadFile(logfile) + if err != nil { + return err + } + output := string(out) + regexMatchString := "likewise, var2 is A`Backtick" + if !regexp.MustCompile(regexp.QuoteMeta(regexMatchString)).MatchString(output) { + t.Errorf("expected env string %q in logs:\n%s", regexMatchString, output) + } return nil }, } diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index b858700b9..11b6b4837 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -24,7 +24,7 @@ func TestProvisionerPrepare_extractScript(t *testing.T) { p := new(Provisioner) _ = p.Prepare(config) p.generatedData = generatedData() - file, err := extractScript(p) + file, err := extractInlineScript(p) defer os.Remove(file) if err != nil { t.Fatalf("Should not be error: %s", err) @@ -36,7 +36,7 @@ func TestProvisionerPrepare_extractScript(t *testing.T) { // File contents should contain 2 lines concatenated by newlines: foo\nbar readFile, err := os.ReadFile(file) - expectedContents := "if (Test-Path variable:global:ProgressPreference) {\n set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'\n }\n \n $exitCode = 0\n try {\n $env:PACKER_BUILDER_TYPE=\"\"; $env:PACKER_BUILD_NAME=\"\"; \n foobar\n $exitCode = 0\n } catch {\n Write-Error \"An error occurred: $_\"\n $exitCode = 1\n }\n \n if ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {\n $exitCode = $LASTEXITCODE\n }\n \n Write-Host $result\n exit $exitCode" + expectedContents := " \n\tif (Test-Path variable:global:ProgressPreference) {\n\t set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'\n\t}\n\t\n\t$exitCode = 0\n\ttry {\n\t$env:PACKER_BUILDER_TYPE=\"\"; $env:PACKER_BUILD_NAME=\"\"; \n\tfoo\n\tbar\n\t\n\t$exitCode = 0\n\t} catch {\n\tWrite-Error \"An error occurred: $_\"\n\t$exitCode = 1\n\t}\n\t\n\tif ($LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0) {\n\t\t$exitCode = $LASTEXITCODE\n\t}\n\t\n\tWrite-Host $result\n\texit $exitCode\n\n" normalizedExpectedContent := normalizeWhiteSpace(expectedContents) if err != nil { t.Fatalf("Should not be error: %s", err) diff --git a/provisioner/powershell/test-fixtures/powershell-inline-provisioner.txt b/provisioner/powershell/test-fixtures/powershell-inline-provisioner.txt index 1b63c1742..75ade524a 100644 --- a/provisioner/powershell/test-fixtures/powershell-inline-provisioner.txt +++ b/provisioner/powershell/test-fixtures/powershell-inline-provisioner.txt @@ -1,8 +1,10 @@ { "type": "powershell", - "environment_vars": "PackerRunUUID={{build `PackerRunUUID`}},ID={{build `ID`}}", + "environment_vars": "PackerRunUUID={{build `PackerRunUUID`}},ID={{build `ID`}},TEST_ENV_VAR=TestValue", "inline": [ - "Write-Host \"$env:ID for provisioner.$env:PackerRunUUID\"" + "Write-Host \"$env:ID for provisioner.$env:PackerRunUUID build_name: $env:packer_build_name, test_env_var: $env:test_env_var\"", + "setx BUILD_AMI_VERSION \"1.0.0\"", + "setx BUILD_AMI_NAME custom_ami_name", + "setx BUILD_AMI_DESCRIPTION \"This is a custom AMI created for testing purposes\"" ] -} - +} \ No newline at end of file diff --git a/version/VERSION b/version/VERSION index feaae22ba..b50dd27dd 100644 --- a/version/VERSION +++ b/version/VERSION @@ -1 +1 @@ -1.13.0 +1.13.1