github.com/benphegan/packer@v1.0.0-rc1/common/powershell/powershell.go (about) 1 package powershell 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "log" 9 "os" 10 "os/exec" 11 "strconv" 12 "strings" 13 ) 14 15 const ( 16 powerShellFalse = "False" 17 powerShellTrue = "True" 18 ) 19 20 type PowerShellCmd struct { 21 Stdout io.Writer 22 Stderr io.Writer 23 } 24 25 func (ps *PowerShellCmd) Run(fileContents string, params ...string) error { 26 _, err := ps.Output(fileContents, params...) 27 return err 28 } 29 30 // Output runs the PowerShell command and returns its standard output. 31 func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) { 32 path, err := ps.getPowerShellPath() 33 if err != nil { 34 return "", err 35 } 36 37 filename, err := saveScript(fileContents) 38 if err != nil { 39 return "", err 40 } 41 42 debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != "" 43 verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != "" 44 45 if !debug { 46 defer os.Remove(filename) 47 } 48 49 args := createArgs(filename, params...) 50 51 if verbose { 52 log.Printf("Run: %s %s", path, args) 53 } 54 55 var stdout, stderr bytes.Buffer 56 command := exec.Command(path, args...) 57 command.Stdout = &stdout 58 command.Stderr = &stderr 59 60 err = command.Run() 61 62 if ps.Stdout != nil { 63 stdout.WriteTo(ps.Stdout) 64 } 65 66 if ps.Stderr != nil { 67 stderr.WriteTo(ps.Stderr) 68 } 69 70 stderrString := strings.TrimSpace(stderr.String()) 71 72 if _, ok := err.(*exec.ExitError); ok { 73 err = fmt.Errorf("PowerShell error: %s", stderrString) 74 } 75 76 if len(stderrString) > 0 { 77 err = fmt.Errorf("PowerShell error: %s", stderrString) 78 } 79 80 stdoutString := strings.TrimSpace(stdout.String()) 81 82 if verbose && stdoutString != "" { 83 log.Printf("stdout: %s", stdoutString) 84 } 85 86 // only write the stderr string if verbose because 87 // the error string will already be in the err return value. 88 if verbose && stderrString != "" { 89 log.Printf("stderr: %s", stderrString) 90 } 91 92 return stdoutString, err 93 } 94 95 func IsPowershellAvailable() (bool, string, error) { 96 path, err := exec.LookPath("powershell") 97 if err != nil { 98 return false, "", err 99 } else { 100 return true, path, err 101 } 102 } 103 104 func (ps *PowerShellCmd) getPowerShellPath() (string, error) { 105 powershellAvailable, path, err := IsPowershellAvailable() 106 107 if !powershellAvailable { 108 log.Fatal("Cannot find PowerShell in the path") 109 return "", err 110 } 111 112 return path, nil 113 } 114 115 func saveScript(fileContents string) (string, error) { 116 file, err := ioutil.TempFile(os.TempDir(), "ps") 117 if err != nil { 118 return "", err 119 } 120 121 _, err = file.Write([]byte(fileContents)) 122 if err != nil { 123 return "", err 124 } 125 126 err = file.Close() 127 if err != nil { 128 return "", err 129 } 130 131 newFilename := file.Name() + ".ps1" 132 err = os.Rename(file.Name(), newFilename) 133 if err != nil { 134 return "", err 135 } 136 137 return newFilename, nil 138 } 139 140 func createArgs(filename string, params ...string) []string { 141 args := make([]string, len(params)+5) 142 args[0] = "-ExecutionPolicy" 143 args[1] = "Bypass" 144 145 args[2] = "-NoProfile" 146 147 args[3] = "-File" 148 args[4] = filename 149 150 for key, value := range params { 151 args[key+5] = value 152 } 153 154 return args 155 } 156 157 func GetHostAvailableMemory() float64 { 158 159 var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024" 160 161 var ps PowerShellCmd 162 output, _ := ps.Output(script) 163 164 freeMB, _ := strconv.ParseFloat(output, 64) 165 166 return freeMB 167 } 168 169 func GetHostName(ip string) (string, error) { 170 171 var script = ` 172 param([string]$ip) 173 try { 174 $HostName = [System.Net.Dns]::GetHostEntry($ip).HostName 175 if ($HostName -ne $null) { 176 $HostName = $HostName.Split('.')[0] 177 } 178 $HostName 179 } catch { } 180 ` 181 182 // 183 var ps PowerShellCmd 184 cmdOut, err := ps.Output(script, ip) 185 if err != nil { 186 return "", err 187 } 188 189 return cmdOut, nil 190 } 191 192 func IsCurrentUserAnAdministrator() (bool, error) { 193 var script = ` 194 $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() 195 $principal = new-object System.Security.Principal.WindowsPrincipal($identity) 196 $administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator 197 return $principal.IsInRole($administratorRole) 198 ` 199 200 var ps PowerShellCmd 201 cmdOut, err := ps.Output(script) 202 if err != nil { 203 return false, err 204 } 205 206 res := strings.TrimSpace(cmdOut) 207 return res == powerShellTrue, nil 208 } 209 210 func ModuleExists(moduleName string) (bool, error) { 211 212 var script = ` 213 param([string]$moduleName) 214 (Get-Module -Name $moduleName) -ne $null 215 ` 216 var ps PowerShellCmd 217 cmdOut, err := ps.Output(script) 218 if err != nil { 219 return false, err 220 } 221 222 res := strings.TrimSpace(string(cmdOut)) 223 224 if res == powerShellFalse { 225 err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName) 226 return false, err 227 } 228 229 return true, nil 230 } 231 232 func HasVirtualMachineVirtualizationExtensions() (bool, error) { 233 234 var script = ` 235 (GET-Command Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions" 236 ` 237 238 var ps PowerShellCmd 239 cmdOut, err := ps.Output(script) 240 241 if err != nil { 242 return false, err 243 } 244 245 var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True" 246 return hasVirtualMachineVirtualizationExtensions, err 247 } 248 249 func SetUnattendedProductKey(path string, productKey string) error { 250 251 var script = ` 252 param([string]$path,[string]$productKey) 253 254 $unattend = [xml](Get-Content -Path $path) 255 $ns = @{ un = 'urn:schemas-microsoft-com:unattend' } 256 257 $setupNode = $unattend | 258 Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns | 259 Select-Object -ExpandProperty Node 260 261 $productKeyNode = $setupNode | 262 Select-Xml -XPath '//un:ProductKey' -Namespace $ns | 263 Select-Object -ExpandProperty Node 264 265 if ($productKeyNode -eq $null) { 266 $productKeyNode = $unattend.CreateElement('ProductKey', $ns.un) 267 [Void]$setupNode.AppendChild($productKeyNode) 268 } 269 270 $productKeyNode.InnerText = $productKey 271 272 $unattend.Save($path) 273 ` 274 275 var ps PowerShellCmd 276 err := ps.Run(script, path, productKey) 277 return err 278 }