Compare commits
6 Commits
master
...
b-powershe
Author | SHA1 | Date | |
---|---|---|---|
|
8de1eddcb2 | ||
|
9df4129d6d | ||
|
ab93bc8a5d | ||
|
e90913fcd2 | ||
|
b1fec8f0bc | ||
|
ed1a2b1deb |
@ -9,6 +9,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -37,6 +38,43 @@ var psEscape = strings.NewReplacer(
|
|||||||
"'", "`'",
|
"'", "`'",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const PowershellWrapperScript string = `
|
||||||
|
if (Test-Path variable:global:ProgressPreference) {
|
||||||
|
set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'
|
||||||
|
}
|
||||||
|
set-variable -name variable:global:ErrorActionPreference -value 'Continue'
|
||||||
|
$global:LASTEXITCODE = 0
|
||||||
|
$global:lastcmdlet = $null
|
||||||
|
trap [Exception] {write-error ($_.Exception.Message);exit 1}
|
||||||
|
|
||||||
|
{{if .DebugMode}}
|
||||||
|
Set-PsDebug -Trace {{.DebugMode}}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{.Vars}}
|
||||||
|
|
||||||
|
$results = {
|
||||||
|
|
||||||
|
{{.Payload}}
|
||||||
|
|
||||||
|
$global:lastcmdlet = $?
|
||||||
|
}.invokereturnasis()
|
||||||
|
|
||||||
|
$exitstatus = 1
|
||||||
|
|
||||||
|
if ($lastcmdlet) {
|
||||||
|
$exitstatus = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $LASTEXITCODE -ne $null -and $LASTEXITCODE -ne 0 ) {
|
||||||
|
$exitstatus = $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host $results
|
||||||
|
|
||||||
|
exit $exitstatus
|
||||||
|
`
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
shell.Provisioner `mapstructure:",squash"`
|
shell.Provisioner `mapstructure:",squash"`
|
||||||
|
|
||||||
@ -76,8 +114,6 @@ type Config struct {
|
|||||||
|
|
||||||
ExecutionPolicy ExecutionPolicy `mapstructure:"execution_policy"`
|
ExecutionPolicy ExecutionPolicy `mapstructure:"execution_policy"`
|
||||||
|
|
||||||
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)
|
// 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
|
// in order to make script debugging easier. For instance, setting the
|
||||||
// value to 1 results in adding this to the execute command:
|
// value to 1 results in adding this to the execute command:
|
||||||
@ -87,30 +123,30 @@ type Config struct {
|
|||||||
// ```
|
// ```
|
||||||
DebugMode int `mapstructure:"debug_mode"`
|
DebugMode int `mapstructure:"debug_mode"`
|
||||||
|
|
||||||
|
// If set, any Powershell provided `Inline` command(s) or `Script(s)` will
|
||||||
|
// get wrapped in a Packer error handling script to help with capturing
|
||||||
|
// non-zero exit codes or unexpected failures. Defaults to true.
|
||||||
|
// It is explicitly to false when using a custom ExecuteCommand or ElevatedExecuteCommand.
|
||||||
|
UseErrorWrapperScript bool `mapstructure:"use_error_wrapper"`
|
||||||
|
|
||||||
|
remoteCleanUpScriptPath string
|
||||||
|
|
||||||
ctx interpolate.Context
|
ctx interpolate.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
type Provisioner struct {
|
type Provisioner struct {
|
||||||
config Config
|
config Config
|
||||||
communicator packer.Communicator
|
communicator packer.Communicator
|
||||||
generatedData map[string]interface{}
|
generatedData map[string]interface{}
|
||||||
|
useWrappedCommmand bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) defaultExecuteCommand() string {
|
func (p *Provisioner) defaultExecuteCommand() string {
|
||||||
baseCmd := `& { if (Test-Path variable:global:ProgressPreference)` +
|
|
||||||
`{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 {
|
if p.config.ExecutionPolicy == ExecutionPolicyNone {
|
||||||
return baseCmd
|
return `-file {{.Path}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd)
|
return fmt.Sprintf(`powershell -noninteractive -noprofile -executionpolicy %s -file {{.Path}}`, p.config.ExecutionPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
|
func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }
|
||||||
@ -132,6 +168,9 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Remote execution defaults
|
||||||
|
p.config.remoteCleanUpScriptPath = fmt.Sprintf(`c:/Windows/Temp/packer-cleanup-%s.ps1`, uuid.TimeOrderedUUID())
|
||||||
|
|
||||||
if p.config.EnvVarFormat == "" {
|
if p.config.EnvVarFormat == "" {
|
||||||
p.config.EnvVarFormat = `$env:%s="%s"; `
|
p.config.EnvVarFormat = `$env:%s="%s"; `
|
||||||
}
|
}
|
||||||
@ -142,10 +181,12 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||||||
|
|
||||||
if p.config.ExecuteCommand == "" {
|
if p.config.ExecuteCommand == "" {
|
||||||
p.config.ExecuteCommand = p.defaultExecuteCommand()
|
p.config.ExecuteCommand = p.defaultExecuteCommand()
|
||||||
|
p.config.UseErrorWrapperScript = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.ElevatedExecuteCommand == "" {
|
if p.config.ElevatedExecuteCommand == "" {
|
||||||
p.config.ElevatedExecuteCommand = p.defaultExecuteCommand()
|
p.config.ElevatedExecuteCommand = p.defaultExecuteCommand()
|
||||||
|
p.config.UseErrorWrapperScript = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.Inline != nil && len(p.config.Inline) == 0 {
|
if p.config.Inline != nil && len(p.config.Inline) == 0 {
|
||||||
@ -157,13 +198,11 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if p.config.RemotePath == "" {
|
if p.config.RemotePath == "" {
|
||||||
uuid := uuid.TimeOrderedUUID()
|
p.config.RemotePath = fmt.Sprintf(`c:/Windows/Temp/script-%s.ps1`, uuid.TimeOrderedUUID())
|
||||||
p.config.RemotePath = fmt.Sprintf(`c:/Windows/Temp/script-%s.ps1`, uuid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.RemoteEnvVarPath == "" {
|
if p.config.RemoteEnvVarPath == "" {
|
||||||
uuid := uuid.TimeOrderedUUID()
|
p.config.RemoteEnvVarPath = fmt.Sprintf(`c:/Windows/Temp/packer-ps-env-vars-%s.ps1`, uuid.TimeOrderedUUID())
|
||||||
p.config.RemoteEnvVarPath = fmt.Sprintf(`c:/Windows/Temp/packer-ps-env-vars-%s.ps1`, uuid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.config.Scripts == nil {
|
if p.config.Scripts == nil {
|
||||||
@ -174,8 +213,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||||||
p.config.Vars = make([]string, 0)
|
p.config.Vars = make([]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.config.remoteCleanUpScriptPath = fmt.Sprintf(`c:/Windows/Temp/packer-cleanup-%s.ps1`, uuid.TimeOrderedUUID())
|
// Validate parsed configuration data
|
||||||
|
|
||||||
var errs error
|
var errs error
|
||||||
if p.config.Script != "" && len(p.config.Scripts) > 0 {
|
if p.config.Script != "" && len(p.config.Scripts) > 0 {
|
||||||
errs = packer.MultiErrorAppend(errs,
|
errs = packer.MultiErrorAppend(errs,
|
||||||
@ -223,37 +261,11 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !(p.config.DebugMode >= 0 && p.config.DebugMode <= 2) {
|
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))
|
s := "%d is an invalid Trace level for `debug_mode`; valid values are 0, 1, and 2"
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf(s, p.config.DebugMode))
|
||||||
}
|
}
|
||||||
|
|
||||||
if errs != nil {
|
return errs
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes 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) {
|
|
||||||
temp, err := tmp.File("powershell-provisioner")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer temp.Close()
|
|
||||||
writer := bufio.NewWriter(temp)
|
|
||||||
for _, command := range p.config.Inline {
|
|
||||||
log.Printf("Found command: %s", command)
|
|
||||||
if _, err := writer.WriteString(command + "\n"); err != nil {
|
|
||||||
return "", fmt.Errorf("Error preparing powershell script: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Flush(); err != nil {
|
|
||||||
return "", fmt.Errorf("Error preparing powershell script: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return temp.Name(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error {
|
func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator, generatedData map[string]interface{}) error {
|
||||||
@ -284,17 +296,26 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error stating powershell script: %s", err)
|
return fmt.Errorf("Error stating powershell script: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(p.config.RemotePath, `\`) {
|
if strings.HasSuffix(p.config.RemotePath, `\`) {
|
||||||
// path is a directory
|
// path is a directory
|
||||||
p.config.RemotePath += filepath.Base((fi).Name())
|
p.config.RemotePath += filepath.Base((fi).Name())
|
||||||
}
|
}
|
||||||
f, err := os.Open(path)
|
|
||||||
|
payload, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error opening powershell script: %s", err)
|
return fmt.Errorf("Error opening powershell script: %s", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
command, err := p.createCommandText()
|
data := string(payload)
|
||||||
|
if p.config.UseErrorWrapperScript {
|
||||||
|
data, err = p.WrapScriptContents(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err := p.buildInterpolatedCommand()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error processing command: %s", err)
|
return fmt.Errorf("Error processing command: %s", err)
|
||||||
}
|
}
|
||||||
@ -305,10 +326,8 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
|
|||||||
// command is executed but the file doesn't exist any longer.
|
// command is executed but the file doesn't exist any longer.
|
||||||
var cmd *packer.RemoteCmd
|
var cmd *packer.RemoteCmd
|
||||||
err = retry.Config{StartTimeout: p.config.StartRetryTimeout}.Run(ctx, func(ctx context.Context) error {
|
err = retry.Config{StartTimeout: p.config.StartRetryTimeout}.Run(ctx, func(ctx context.Context) error {
|
||||||
if _, err := f.Seek(0, 0); err != nil {
|
|
||||||
return err
|
if err := comm.Upload(p.config.RemotePath, strings.NewReader(data), nil); err != nil {
|
||||||
}
|
|
||||||
if err := comm.Upload(p.config.RemotePath, f, &fi); err != nil {
|
|
||||||
return fmt.Errorf("Error uploading script: %s", err)
|
return fmt.Errorf("Error uploading script: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,9 +338,6 @@ func (p *Provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the original file since we copied it
|
|
||||||
f.Close()
|
|
||||||
|
|
||||||
// Record every other uploaded script file so we can clean it up later
|
// Record every other uploaded script file so we can clean it up later
|
||||||
uploadedScripts = append(uploadedScripts, p.config.RemotePath)
|
uploadedScripts = append(uploadedScripts, p.config.RemotePath)
|
||||||
|
|
||||||
@ -371,13 +387,119 @@ func (p *Provisioner) createRemoteCleanUpCommand(remoteFiles []string) (string,
|
|||||||
return "", fmt.Errorf("clean up script %q failed to upload: %s", remotePath, err)
|
return "", fmt.Errorf("clean up script %q failed to upload: %s", remotePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data := p.generatedData
|
ctxData := p.generatedData
|
||||||
data["Path"] = remotePath
|
ctxData["Path"] = remotePath
|
||||||
data["Vars"] = p.config.RemoteEnvVarPath
|
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
||||||
p.config.ctx.Data = data
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error processing command: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
p.config.ctx.Data = data
|
// Return the interpolated command
|
||||||
return interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
return command, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildInterpolatedCommand returns the actual command to be executed at runtime.
|
||||||
|
func (p *Provisioner) buildInterpolatedCommand() (string, error) {
|
||||||
|
if p.config.ElevatedUser != "" {
|
||||||
|
return p.elevatedExecuteCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.executeCommand()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapScriptContents will generate a Powershell wrapper for executing p.config.Inline or p.config.Scripts
|
||||||
|
func (p *Provisioner) WrapScriptContents(payload []byte) (string, error) {
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
if _, err := b.Write(payload); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to wrap script contents: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxData := p.generatedData
|
||||||
|
ctxData["Vars"] = p.createFlattenedEnvVars(p.config.ElevatedUser != "")
|
||||||
|
ctxData["Payload"] = b.String()
|
||||||
|
ctxData["DebugMode"] = p.config.DebugMode
|
||||||
|
p.config.ctx.Data = ctxData
|
||||||
|
|
||||||
|
data, err := interpolate.Render(PowershellWrapperScript, &p.config.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error building powershell wrapper: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) executeCommand() (string, error) {
|
||||||
|
|
||||||
|
// Prepare everything needed to enable the required env vars within the
|
||||||
|
// remote environment
|
||||||
|
err := p.prepareEnvVars(false)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxData := p.generatedData
|
||||||
|
ctxData["Path"] = p.config.RemotePath
|
||||||
|
ctxData["Vars"] = p.config.RemoteEnvVarPath
|
||||||
|
p.config.ctx.Data = ctxData
|
||||||
|
|
||||||
|
command, err := interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error processing command: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the interpolated command
|
||||||
|
return command, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Provisioner) elevatedExecuteCommand() (command string, err error) {
|
||||||
|
|
||||||
|
// Prepare everything needed to enable the required env vars within the
|
||||||
|
// remote environment
|
||||||
|
err = p.prepareEnvVars(true)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
ctxData := p.generatedData
|
||||||
|
ctxData["Path"] = p.config.RemotePath
|
||||||
|
ctxData["Vars"] = p.config.RemoteEnvVarPath
|
||||||
|
p.config.ctx.Data = ctxData
|
||||||
|
|
||||||
|
command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error processing command: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
command, err = provisioner.GenerateElevatedRunner(command, p)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Error generating elevated runner: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return command, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Takes 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) {
|
||||||
|
temp, err := tmp.File("powershell-provisioner")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer temp.Close()
|
||||||
|
writer := bufio.NewWriter(temp)
|
||||||
|
for _, command := range p.config.Inline {
|
||||||
|
log.Printf("Found command: %s", command)
|
||||||
|
if _, err := writer.WriteString(command + "\n"); err != nil {
|
||||||
|
return "", fmt.Errorf("Error preparing powershell script: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Flush(); err != nil {
|
||||||
|
return "", fmt.Errorf("Error preparing powershell script: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return temp.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Environment variables required within the remote environment are uploaded
|
// Environment variables required within the remote environment are uploaded
|
||||||
@ -472,63 +594,6 @@ func (p *Provisioner) uploadEnvVars(flattenedEnvVars string) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provisioner) createCommandText() (command string, err error) {
|
|
||||||
// Return the interpolated command
|
|
||||||
if p.config.ElevatedUser == "" {
|
|
||||||
return p.createCommandTextNonPrivileged()
|
|
||||||
} else {
|
|
||||||
return p.createCommandTextPrivileged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provisioner) createCommandTextNonPrivileged() (command string, err error) {
|
|
||||||
// Prepare everything needed to enable the required env vars within the
|
|
||||||
// remote environment
|
|
||||||
err = p.prepareEnvVars(false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctxData := p.generatedData
|
|
||||||
ctxData["Path"] = p.config.RemotePath
|
|
||||||
ctxData["Vars"] = p.config.RemoteEnvVarPath
|
|
||||||
p.config.ctx.Data = ctxData
|
|
||||||
|
|
||||||
command, err = interpolate.Render(p.config.ExecuteCommand, &p.config.ctx)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Error processing command: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the interpolated command
|
|
||||||
return command, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provisioner) createCommandTextPrivileged() (command string, err error) {
|
|
||||||
// Prepare everything needed to enable the required env vars within the
|
|
||||||
// remote environment
|
|
||||||
err = p.prepareEnvVars(true)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
ctxData := p.generatedData
|
|
||||||
ctxData["Path"] = p.config.RemotePath
|
|
||||||
ctxData["Vars"] = p.config.RemoteEnvVarPath
|
|
||||||
p.config.ctx.Data = ctxData
|
|
||||||
|
|
||||||
command, err = interpolate.Render(p.config.ElevatedExecuteCommand, &p.config.ctx)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Error processing command: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
command, err = provisioner.GenerateElevatedRunner(command, p)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("Error generating elevated runner: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return command, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provisioner) Communicator() packer.Communicator {
|
func (p *Provisioner) Communicator() packer.Communicator {
|
||||||
return p.communicator
|
return p.communicator
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ type FlatConfig struct {
|
|||||||
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password" hcl:"elevated_password"`
|
ElevatedPassword *string `mapstructure:"elevated_password" cty:"elevated_password" hcl:"elevated_password"`
|
||||||
ExecutionPolicy *string `mapstructure:"execution_policy" cty:"execution_policy" hcl:"execution_policy"`
|
ExecutionPolicy *string `mapstructure:"execution_policy" cty:"execution_policy" hcl:"execution_policy"`
|
||||||
DebugMode *int `mapstructure:"debug_mode" cty:"debug_mode" hcl:"debug_mode"`
|
DebugMode *int `mapstructure:"debug_mode" cty:"debug_mode" hcl:"debug_mode"`
|
||||||
|
UseErrorWrapperScript *bool `mapstructure:"use_error_wrapper" cty:"use_error_wrapper" hcl:"use_error_wrapper"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// FlatMapstructure returns a new FlatConfig.
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
@ -73,6 +74,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"elevated_password": &hcldec.AttrSpec{Name: "elevated_password", 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},
|
"execution_policy": &hcldec.AttrSpec{Name: "execution_policy", Type: cty.String, Required: false},
|
||||||
"debug_mode": &hcldec.AttrSpec{Name: "debug_mode", Type: cty.Number, Required: false},
|
"debug_mode": &hcldec.AttrSpec{Name: "debug_mode", Type: cty.Number, Required: false},
|
||||||
|
"use_error_wrapper": &hcldec.AttrSpec{Name: "use_error_wrapper", Type: cty.Bool, Required: false},
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,15 @@ func TestAccPowershellProvisioner_Script(t *testing.T) {
|
|||||||
acc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
acc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccPowershellProvisioner_ExitCodes(t *testing.T) {
|
||||||
|
acc.TestProvisionersPreCheck(TestProvisionerName, t)
|
||||||
|
|
||||||
|
// This provisioner should fail with an exit code of 1. To assert the failure the fixture
|
||||||
|
// uses the valid_exit_codes option to confirm a non-zero exit code
|
||||||
|
testProvisioner := PowershellProvisionerAccTest{"powershell-exit_codes-provisioner.txt"}
|
||||||
|
acc.TestProvisionersAgainstBuilders(&testProvisioner, t)
|
||||||
|
}
|
||||||
|
|
||||||
type PowershellProvisionerAccTest struct {
|
type PowershellProvisionerAccTest struct {
|
||||||
ConfigName string
|
ConfigName string
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,44 @@ func TestProvisionerPrepare_Defaults(t *testing.T) {
|
|||||||
t.Error("expected elevated_password to be empty")
|
t.Error("expected elevated_password to be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matched, _ = regexp.MatchString("powershell -noninteractive -noprofile -executionpolicy bypass -file {{.Path}}", p.config.ExecuteCommand)
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("expected default execute command, but got : %s", p.config.ExecuteCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
matched, _ = regexp.MatchString("powershell -noninteractive -noprofile -executionpolicy bypass -file {{.Path}}", p.config.ElevatedExecuteCommand)
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("expected default elevated execute command, but got : %s", p.config.ElevatedExecuteCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.config.ElevatedEnvVarFormat != `$env:%s="%s"; ` {
|
||||||
|
t.Fatalf(`Default command should be powershell '$env:%%s="%%s"; ', but got %s`, p.config.ElevatedEnvVarFormat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProvisionerPrepare_CustomExecuteCommands(t *testing.T) {
|
||||||
|
var p Provisioner
|
||||||
|
config := testConfig()
|
||||||
|
config["execute_command"] = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"`
|
||||||
|
config["elevated_execute_command"] = `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"`
|
||||||
|
|
||||||
|
err := p.Prepare(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matched, _ := regexp.MatchString("c:/Windows/Temp/script-.*.ps1", p.config.RemotePath)
|
||||||
|
if !matched {
|
||||||
|
t.Errorf("unexpected remote path: %s", p.config.RemotePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.config.ElevatedUser != "" {
|
||||||
|
t.Error("expected elevated_user to be empty")
|
||||||
|
}
|
||||||
|
if p.config.ElevatedPassword != "" {
|
||||||
|
t.Error("expected elevated_password to be empty")
|
||||||
|
}
|
||||||
|
|
||||||
if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` {
|
if p.config.ExecuteCommand != `powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"` {
|
||||||
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand)
|
t.Fatalf(`Default command should be 'powershell -executionpolicy bypass "& { if (Test-Path variable:global:ProgressPreference){set-variable -name variable:global:ProgressPreference -value 'SilentlyContinue'};. {{.Vars}}; &'{{.Path}}'; exit $LastExitCode }"', but got '%s'`, p.config.ExecuteCommand)
|
||||||
}
|
}
|
||||||
@ -476,7 +514,7 @@ func TestProvisionerProvision_Scripts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := comm.StartCmd.Command
|
cmd := comm.StartCmd.Command
|
||||||
re := regexp.MustCompile(`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 }"`)
|
re := regexp.MustCompile(`powershell -noninteractive -noprofile -executionpolicy bypass -file c:/Windows/Temp/script.ps1'`)
|
||||||
matched := re.MatchString(cmd)
|
matched := re.MatchString(cmd)
|
||||||
if !matched {
|
if !matched {
|
||||||
t.Fatalf("Got unexpected command: %s", cmd)
|
t.Fatalf("Got unexpected command: %s", cmd)
|
||||||
@ -786,7 +824,7 @@ func TestProvisioner_createFlattenedEnvVars_windows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProvision_createCommandText(t *testing.T) {
|
func TestProvision_buildInterpolatedCommand(t *testing.T) {
|
||||||
config := testConfig()
|
config := testConfig()
|
||||||
config["remote_path"] = "c:/Windows/Temp/script.ps1"
|
config["remote_path"] = "c:/Windows/Temp/script.ps1"
|
||||||
p := new(Provisioner)
|
p := new(Provisioner)
|
||||||
@ -800,9 +838,9 @@ func TestProvision_createCommandText(t *testing.T) {
|
|||||||
|
|
||||||
// Non-elevated
|
// Non-elevated
|
||||||
p.generatedData = make(map[string]interface{})
|
p.generatedData = make(map[string]interface{})
|
||||||
cmd, _ := p.createCommandText()
|
cmd, _ := p.buildInterpolatedCommand()
|
||||||
|
|
||||||
re := regexp.MustCompile(`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 }"`)
|
re := regexp.MustCompile(`powershell -noninteractive -noprofile -executionpolicy bypass -file c:/Windows/Temp/script.ps1`)
|
||||||
matched := re.MatchString(cmd)
|
matched := re.MatchString(cmd)
|
||||||
if !matched {
|
if !matched {
|
||||||
t.Fatalf("Got unexpected command: %s", cmd)
|
t.Fatalf("Got unexpected command: %s", cmd)
|
||||||
@ -811,7 +849,7 @@ func TestProvision_createCommandText(t *testing.T) {
|
|||||||
// Elevated
|
// Elevated
|
||||||
p.config.ElevatedUser = "vagrant"
|
p.config.ElevatedUser = "vagrant"
|
||||||
p.config.ElevatedPassword = "vagrant"
|
p.config.ElevatedPassword = "vagrant"
|
||||||
cmd, _ = p.createCommandText()
|
cmd, _ = p.buildInterpolatedCommand()
|
||||||
re = regexp.MustCompile(`powershell -executionpolicy bypass -file "C:/Windows/Temp/packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`)
|
re = regexp.MustCompile(`powershell -executionpolicy bypass -file "C:/Windows/Temp/packer-elevated-shell-[[:alnum:]]{8}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{4}-[[:alnum:]]{12}\.ps1"`)
|
||||||
matched = re.MatchString(cmd)
|
matched = re.MatchString(cmd)
|
||||||
if !matched {
|
if !matched {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"type": "powershell",
|
||||||
|
"inline": ["invalid-cmdlet"],
|
||||||
|
"valid_exit_codes": ["1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "powershell",
|
||||||
|
"inline": ["#Requires -Version 10.0"],
|
||||||
|
"valid_exit_codes": ["1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "powershell",
|
||||||
|
"script": "../../provisioner/powershell/test-fixtures/scripts/set_version_latest.ps1",
|
||||||
|
"valid_exit_codes": ["0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "powershell",
|
||||||
|
"elevated_user": "Administrator",
|
||||||
|
"elevated_password": "{{.WinRMPassword}}",
|
||||||
|
"inline": "Get-ItemProperty -Path HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
|
||||||
|
"valid_exit_codes": ["0"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "powershell",
|
||||||
|
"inline": "sc.exe start Life",
|
||||||
|
"valid_exit_codes": ["1060"]
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
# Test fixture is a modified version of the example found at
|
||||||
|
# https://www.powershellmagazine.com/2012/10/23/pstip-set-strictmode-why-should-you-care/
|
||||||
|
|
||||||
|
Set-StrictMode -Version Latest
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$myNumbersCollection = 1..5
|
||||||
|
if($myNumbersCollection -contains 3) {
|
||||||
|
"collection contains 3"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"collection doesn't contain 3"
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user