From d0a4108ba455ae6ef90e945c334881fbe2e2b800 Mon Sep 17 00:00:00 2001 From: karthik P Date: Tue, 3 Jun 2025 12:46:25 +0530 Subject: [PATCH 1/5] init commit --- provisioner/powershell/provisioner.go | 53 ++++++++++++++++++++-- provisioner/powershell/provisioner_test.go | 2 +- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 0f2719275..56755c717 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,41 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { return nil } +func extractScript(p *Provisioner, script string) (string, error) { + temp, err := tmp.File(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) + } + + 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 @@ -311,10 +344,10 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe p.generatedData = generatedData scripts := make([]string, len(p.config.Scripts)) - copy(scripts, p.config.Scripts) + //copy(scripts, p.config.Scripts) if p.config.Inline != nil { - temp, err := extractScript(p) + temp, err := extractInlineScript(p) if err != nil { ui.Error(fmt.Sprintf("Unable to extract inline scripts into a file: %s", err)) } @@ -323,6 +356,20 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe defer os.Remove(temp) } + if len(p.config.Scripts) > 0 { + log.Printf("RUNNING THE FOR LOOP FOR SCRIPTS") + for _, script := range p.config.Scripts { + temp, err := extractScript(p, script) + log.Printf("EXTRACTED SCRIPT: %s", temp) + if err != nil { + ui.Error(fmt.Sprintf("Unable to extract script into a file: %s", err)) + } + scripts = append(scripts, temp) + // Remove temp script containing the inline commands when done + 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 { diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index b858700b9..3a036871f 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) From 4e62b58c88c11b9d1b59ba0b88552bf596fd8695 Mon Sep 17 00:00:00 2001 From: karthik P Date: Tue, 3 Jun 2025 17:39:32 +0530 Subject: [PATCH 2/5] powershell enviornment variables for script config fix --- provisioner/powershell/provisioner.go | 20 ++++++++++-------- .../powershell/provisioner_acc_test.go | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 56755c717..88b9151a6 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -267,7 +267,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { } func extractScript(p *Provisioner, script string) (string, error) { - temp, err := tmp.File(script) + temp, err := tmp.File("powershell-provisioner-script") if err != nil { return "", err } @@ -280,7 +280,7 @@ func extractScript(p *Provisioner, script string) (string, error) { 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) } @@ -343,8 +343,7 @@ 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 if p.config.Inline != nil { temp, err := extractInlineScript(p) @@ -355,27 +354,30 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe // Remove temp script containing the inline commands when done defer os.Remove(temp) } + // maps temp script paths to original script paths + tempToOriginalScriptMap := make(map[string]string, len(p.config.Scripts)) if len(p.config.Scripts) > 0 { - log.Printf("RUNNING THE FOR LOOP FOR SCRIPTS") + for _, script := range p.config.Scripts { temp, err := extractScript(p, script) - log.Printf("EXTRACTED SCRIPT: %s", temp) + tempToOriginalScriptMap[temp] = script if err != nil { ui.Error(fmt.Sprintf("Unable to extract script into a file: %s", err)) } scripts = append(scripts, temp) - // Remove temp script containing the inline commands when done + // 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 }, } From cbee767cbced1d4a9ab6f72c184616d89a38ffb8 Mon Sep 17 00:00:00 2001 From: karthik P Date: Tue, 3 Jun 2025 22:09:49 +0530 Subject: [PATCH 3/5] adding new line character to each written command. --- provisioner/powershell/provisioner.go | 8 ++++---- .../test-fixtures/powershell-inline-provisioner.txt | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 88b9151a6..1769bff52 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -313,7 +313,7 @@ func extractInlineScript(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"); err != nil { return "", fmt.Errorf("failed to wrap script contents: %w", err) } } @@ -344,9 +344,11 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe p.generatedData = generatedData var scripts []string - + // maps temp script paths to original script paths + tempToOriginalScriptMap := make(map[string]string) if p.config.Inline != nil { temp, err := extractInlineScript(p) + tempToOriginalScriptMap[temp] = temp if err != nil { ui.Error(fmt.Sprintf("Unable to extract inline scripts into a file: %s", err)) } @@ -354,8 +356,6 @@ func (p *Provisioner) Provision(ctx context.Context, ui packersdk.Ui, comm packe // Remove temp script containing the inline commands when done defer os.Remove(temp) } - // maps temp script paths to original script paths - tempToOriginalScriptMap := make(map[string]string, len(p.config.Scripts)) if len(p.config.Scripts) > 0 { 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 From 49c3512da8b320935793136d4afacf0c0b0d4db0 Mon Sep 17 00:00:00 2001 From: karthik P Date: Tue, 3 Jun 2025 23:03:02 +0530 Subject: [PATCH 4/5] fixing identation and test case --- provisioner/powershell/provisioner.go | 2 +- provisioner/powershell/provisioner_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 1769bff52..307b9b917 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -313,7 +313,7 @@ func extractInlineScript(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 + "\n"); err != nil { + if _, err := commandBuilder.WriteString(command + "\n\t"); err != nil { return "", fmt.Errorf("failed to wrap script contents: %w", err) } } diff --git a/provisioner/powershell/provisioner_test.go b/provisioner/powershell/provisioner_test.go index 3a036871f..11b6b4837 100644 --- a/provisioner/powershell/provisioner_test.go +++ b/provisioner/powershell/provisioner_test.go @@ -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) From 85817176d424cb0294ce978ca67490b752cb5a0e Mon Sep 17 00:00:00 2001 From: karthik P Date: Tue, 3 Jun 2025 23:23:12 +0530 Subject: [PATCH 5/5] adding comment for extractScript --- provisioner/powershell/provisioner.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 307b9b917..901707c22 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -266,6 +266,8 @@ 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 {