From 2e4b00f59dd24b3a8e3943dc253d622cc639922b Mon Sep 17 00:00:00 2001 From: Matt Dainty Date: Fri, 7 Dec 2018 15:30:50 +0000 Subject: [PATCH] Refactor powershell provisioner Use the common elevated code instead. --- provisioner/powershell/elevated.go | 100 -------------------------- provisioner/powershell/provisioner.go | 86 ++++------------------ 2 files changed, 12 insertions(+), 174 deletions(-) delete mode 100644 provisioner/powershell/elevated.go diff --git a/provisioner/powershell/elevated.go b/provisioner/powershell/elevated.go deleted file mode 100644 index be8dcdf62..000000000 --- a/provisioner/powershell/elevated.go +++ /dev/null @@ -1,100 +0,0 @@ -package powershell - -import ( - "text/template" -) - -type elevatedOptions struct { - User string - Password string - TaskName string - TaskDescription string - LogFile string - XMLEscapedCommand string -} - -var elevatedTemplate = template.Must(template.New("ElevatedCommand").Parse(` -$name = "{{.TaskName}}" -$log = [System.Environment]::ExpandEnvironmentVariables("{{.LogFile}}") -$s = New-Object -ComObject "Schedule.Service" -$s.Connect() -$t = $s.NewTask($null) -$xml = [xml]@' - - - - {{.TaskDescription}} - - - - {{.User}} - Password - HighestAvailable - - - - IgnoreNew - false - false - true - false - false - - false - false - - true - true - false - false - false - PT24H - 4 - - - - cmd - /c {{.XMLEscapedCommand}} - - - -'@ -$logon_type = 1 -$password = "{{.Password}}" -if ($password.Length -eq 0) { - $logon_type = 5 - $password = $null - $ns = New-Object System.Xml.XmlNamespaceManager($xml.NameTable) - $ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI) - $node = $xml.SelectSingleNode("/ns:Task/ns:Principals/ns:Principal/ns:LogonType", $ns) - $node.ParentNode.RemoveChild($node) | Out-Null -} -$t.XmlText = $xml.OuterXml -if (Test-Path variable:global:ProgressPreference){$ProgressPreference="SilentlyContinue"} -$f = $s.GetFolder("\") -$f.RegisterTaskDefinition($name, $t, 6, "{{.User}}", $password, $logon_type, $null) | Out-Null -$t = $f.GetTask("\$name") -$t.Run($null) | Out-Null -$timeout = 10 -$sec = 0 -while ((!($t.state -eq 4)) -and ($sec -lt $timeout)) { - Start-Sleep -s 1 - $sec++ -} - -$line = 0 -do { - Start-Sleep -m 100 - if (Test-Path $log) { - Get-Content $log | select -skip $line | ForEach { - $line += 1 - Write-Output "$_" - } - } -} while (!($t.state -eq 3)) -$result = $t.LastTaskResult -if (Test-Path $log) { - Remove-Item $log -Force -ErrorAction SilentlyContinue | Out-Null -} -[System.Runtime.Interopservices.Marshal]::ReleaseComObject($s) | Out-Null -exit $result`)) diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index ed35bdd01..fa4586fb3 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -4,8 +4,6 @@ package powershell import ( "bufio" - "bytes" - "encoding/xml" "errors" "fmt" "io/ioutil" @@ -20,6 +18,7 @@ import ( commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" + "github.com/hashicorp/packer/provisioner" "github.com/hashicorp/packer/template/interpolate" ) @@ -498,90 +497,29 @@ func (p *Provisioner) createCommandTextPrivileged() (command string, err error) return "", fmt.Errorf("Error processing command: %s", err) } - // OK so we need an elevated shell runner to wrap our command, this is - // going to have its own path generate the script and update the command - // runner in the process - path, err := p.generateElevatedRunner(command) + command, err = provisioner.GenerateElevatedRunner(command, p) if err != nil { return "", fmt.Errorf("Error generating elevated runner: %s", err) } - // Return the path to the elevated shell wrapper - command = fmt.Sprintf("powershell -executionpolicy bypass -file \"%s\"", path) - return command, err } -func (p *Provisioner) generateElevatedRunner(command string) (uploadedPath string, err error) { - log.Printf("Building elevated command wrapper for: %s", command) - - var buffer bytes.Buffer - - // Output from the elevated command cannot be returned directly to the - // Packer console. In order to be able to view output from elevated - // commands and scripts an indirect approach is used by which the commands - // output is first redirected to file. The output file is then 'watched' - // by Packer while the elevated command is running and any content - // appearing in the file is written out to the console. Below the portion - // of command required to redirect output from the command to file is - // built and appended to the existing command string - taskName := fmt.Sprintf("packer-%s", uuid.TimeOrderedUUID()) - // Only use %ENVVAR% format for environment variables when setting the log - // file path; Do NOT use $env:ENVVAR format as it won't be expanded - // correctly in the elevatedTemplate - logFile := `%SYSTEMROOT%/Temp/` + taskName + ".out" - command += fmt.Sprintf(" > %s 2>&1", logFile) - - // elevatedTemplate wraps the command in a single quoted XML text string - // so we need to escape characters considered 'special' in XML. - err = xml.EscapeText(&buffer, []byte(command)) - if err != nil { - return "", fmt.Errorf("Error escaping characters special to XML in command %s: %s", command, err) - } - escapedCommand := buffer.String() - log.Printf("Command [%s] converted to [%s] for use in XML string", command, escapedCommand) - buffer.Reset() +func (p *Provisioner) Communicator() packer.Communicator { + return p.communicator +} - // Escape chars special to PowerShell in the ElevatedUser string - escapedElevatedUser := psEscape.Replace(p.config.ElevatedUser) - if escapedElevatedUser != p.config.ElevatedUser { - log.Printf("Elevated user %s converted to %s after escaping chars special to PowerShell", - p.config.ElevatedUser, escapedElevatedUser) - } +func (p *Provisioner) ElevatedUser() string { + return p.config.ElevatedUser +} + +func (p *Provisioner) ElevatedPassword() string { // Replace ElevatedPassword for winrm users who used this feature p.config.ctx.Data = &EnvVarsTemplate{ WinRMPassword: getWinRMPassword(p.config.PackerBuildName), } - p.config.ElevatedPassword, _ = interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) - - // Escape chars special to PowerShell in the ElevatedPassword string - escapedElevatedPassword := psEscape.Replace(p.config.ElevatedPassword) - if escapedElevatedPassword != p.config.ElevatedPassword { - log.Printf("Elevated password %s converted to %s after escaping chars special to PowerShell", - p.config.ElevatedPassword, escapedElevatedPassword) - } - - // Generate command - err = elevatedTemplate.Execute(&buffer, elevatedOptions{ - User: escapedElevatedUser, - Password: escapedElevatedPassword, - TaskName: taskName, - TaskDescription: "Packer elevated task", - LogFile: logFile, - XMLEscapedCommand: escapedCommand, - }) + elevatedPassword, _ := interpolate.Render(p.config.ElevatedPassword, &p.config.ctx) - if err != nil { - 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 { - return "", fmt.Errorf("Error preparing elevated powershell script: %s", err) - } - return path, err + return elevatedPassword }