github.phpd.cn/hashicorp/packer@v1.3.2/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 func IsTrue(s string) bool { 21 return strings.TrimSpace(s) == powerShellTrue 22 } 23 24 func IsFalse(s string) bool { 25 return strings.TrimSpace(s) == powerShellFalse 26 } 27 28 type PowerShellCmd struct { 29 Stdout io.Writer 30 Stderr io.Writer 31 } 32 33 func (ps *PowerShellCmd) Run(fileContents string, params ...string) error { 34 _, err := ps.Output(fileContents, params...) 35 return err 36 } 37 38 // Output runs the PowerShell command and returns its standard output. 39 func (ps *PowerShellCmd) Output(fileContents string, params ...string) (string, error) { 40 path, err := ps.getPowerShellPath() 41 if err != nil { 42 return "", fmt.Errorf("Cannot find PowerShell in the path") 43 } 44 45 filename, err := saveScript(fileContents) 46 if err != nil { 47 return "", err 48 } 49 50 debug := os.Getenv("PACKER_POWERSHELL_DEBUG") != "" 51 verbose := debug || os.Getenv("PACKER_POWERSHELL_VERBOSE") != "" 52 53 if !debug { 54 defer os.Remove(filename) 55 } 56 57 args := createArgs(filename, params...) 58 59 if verbose { 60 log.Printf("Run: %s %s", path, args) 61 } 62 63 var stdout, stderr bytes.Buffer 64 command := exec.Command(path, args...) 65 command.Stdout = &stdout 66 command.Stderr = &stderr 67 68 err = command.Run() 69 70 if ps.Stdout != nil { 71 stdout.WriteTo(ps.Stdout) 72 } 73 74 if ps.Stderr != nil { 75 stderr.WriteTo(ps.Stderr) 76 } 77 78 stderrString := strings.TrimSpace(stderr.String()) 79 80 if _, ok := err.(*exec.ExitError); ok { 81 err = fmt.Errorf("PowerShell error: %s", stderrString) 82 } 83 84 if len(stderrString) > 0 { 85 err = fmt.Errorf("PowerShell error: %s", stderrString) 86 } 87 88 stdoutString := strings.TrimSpace(stdout.String()) 89 90 if verbose && stdoutString != "" { 91 log.Printf("stdout: %s", stdoutString) 92 } 93 94 // only write the stderr string if verbose because 95 // the error string will already be in the err return value. 96 if verbose && stderrString != "" { 97 log.Printf("stderr: %s", stderrString) 98 } 99 100 return stdoutString, err 101 } 102 103 func IsPowershellAvailable() (bool, string, error) { 104 path, err := exec.LookPath("powershell") 105 if err != nil { 106 return false, "", err 107 } else { 108 return true, path, err 109 } 110 } 111 112 func (ps *PowerShellCmd) getPowerShellPath() (string, error) { 113 powershellAvailable, path, err := IsPowershellAvailable() 114 115 if !powershellAvailable { 116 log.Fatal("Cannot find PowerShell in the path") 117 return "", err 118 } 119 120 return path, nil 121 } 122 123 func saveScript(fileContents string) (string, error) { 124 file, err := ioutil.TempFile(os.TempDir(), "ps") 125 if err != nil { 126 return "", err 127 } 128 129 _, err = file.Write([]byte(fileContents)) 130 if err != nil { 131 return "", err 132 } 133 134 err = file.Close() 135 if err != nil { 136 return "", err 137 } 138 139 newFilename := file.Name() + ".ps1" 140 err = os.Rename(file.Name(), newFilename) 141 if err != nil { 142 return "", err 143 } 144 145 return newFilename, nil 146 } 147 148 func createArgs(filename string, params ...string) []string { 149 args := make([]string, len(params)+5) 150 args[0] = "-ExecutionPolicy" 151 args[1] = "Bypass" 152 153 args[2] = "-NoProfile" 154 155 args[3] = "-File" 156 args[4] = filename 157 158 for key, value := range params { 159 args[key+5] = value 160 } 161 162 return args 163 } 164 165 func GetHostAvailableMemory() float64 { 166 167 var script = "(Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1024" 168 169 var ps PowerShellCmd 170 output, _ := ps.Output(script) 171 172 freeMB, _ := strconv.ParseFloat(output, 64) 173 174 return freeMB 175 } 176 177 func GetHostName(ip string) (string, error) { 178 179 var script = ` 180 param([string]$ip) 181 try { 182 $HostName = [System.Net.Dns]::GetHostEntry($ip).HostName 183 if ($HostName -ne $null) { 184 $HostName = $HostName.Split('.')[0] 185 } 186 $HostName 187 } catch { } 188 ` 189 190 // 191 var ps PowerShellCmd 192 cmdOut, err := ps.Output(script, ip) 193 if err != nil { 194 return "", err 195 } 196 197 return cmdOut, nil 198 } 199 200 func IsCurrentUserAnAdministrator() (bool, error) { 201 var script = ` 202 $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() 203 $principal = new-object System.Security.Principal.WindowsPrincipal($identity) 204 $administratorRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator 205 return $principal.IsInRole($administratorRole) 206 ` 207 208 var ps PowerShellCmd 209 cmdOut, err := ps.Output(script) 210 if err != nil { 211 return false, err 212 } 213 214 res := strings.TrimSpace(cmdOut) 215 return res == powerShellTrue, nil 216 } 217 218 func ModuleExists(moduleName string) (bool, error) { 219 220 var script = ` 221 param([string]$moduleName) 222 (Get-Module -Name $moduleName) -ne $null 223 ` 224 var ps PowerShellCmd 225 cmdOut, err := ps.Output(script) 226 if err != nil { 227 return false, err 228 } 229 230 res := strings.TrimSpace(cmdOut) 231 232 if res == powerShellFalse { 233 err := fmt.Errorf("PowerShell %s module is not loaded. Make sure %s feature is on.", moduleName, moduleName) 234 return false, err 235 } 236 237 return true, nil 238 } 239 240 func HasVirtualMachineVirtualizationExtensions() (bool, error) { 241 242 var script = ` 243 (GET-Command Hyper-V\Set-VMProcessor).parameters.keys -contains "ExposeVirtualizationExtensions" 244 ` 245 246 var ps PowerShellCmd 247 cmdOut, err := ps.Output(script) 248 249 if err != nil { 250 return false, err 251 } 252 253 var hasVirtualMachineVirtualizationExtensions = strings.TrimSpace(cmdOut) == "True" 254 return hasVirtualMachineVirtualizationExtensions, err 255 } 256 257 func DoesVirtualMachineExist(vmName string) (bool, error) { 258 259 var script = ` 260 param([string]$vmName) 261 return (Hyper-V\Get-VM | ?{$_.Name -eq $vmName}) -ne $null 262 ` 263 264 var ps PowerShellCmd 265 cmdOut, err := ps.Output(script, vmName) 266 267 if err != nil { 268 return false, err 269 } 270 271 var exists = strings.TrimSpace(cmdOut) == "True" 272 return exists, err 273 } 274 275 func DoesVirtualMachineSnapshotExist(vmName string, snapshotName string) (bool, error) { 276 277 var script = ` 278 param([string]$vmName, [string]$snapshotName) 279 return (Hyper-V\Get-VMSnapshot -VMName $vmName | ?{$_.Name -eq $snapshotName}) -ne $null 280 ` 281 282 var ps PowerShellCmd 283 cmdOut, err := ps.Output(script, vmName, snapshotName) 284 285 if err != nil { 286 return false, err 287 } 288 289 var exists = strings.TrimSpace(cmdOut) == "True" 290 return exists, err 291 } 292 293 func IsVirtualMachineOn(vmName string) (bool, error) { 294 295 var script = ` 296 param([string]$vmName) 297 $vm = Hyper-V\Get-VM -Name $vmName -ErrorAction SilentlyContinue 298 $vm.State -eq [Microsoft.HyperV.PowerShell.VMState]::Running 299 ` 300 301 var ps PowerShellCmd 302 cmdOut, err := ps.Output(script, vmName) 303 304 if err != nil { 305 return false, err 306 } 307 308 var isRunning = strings.TrimSpace(cmdOut) == "True" 309 return isRunning, err 310 } 311 312 func GetVirtualMachineGeneration(vmName string) (uint, error) { 313 var script = ` 314 param([string]$vmName) 315 $generation = Hyper-V\Get-Vm -Name $vmName | %{$_.Generation} 316 if (!$generation){ 317 $generation = 1 318 } 319 return $generation 320 ` 321 var ps PowerShellCmd 322 cmdOut, err := ps.Output(script, vmName) 323 324 if err != nil { 325 return 0, err 326 } 327 328 generationUint32, err := strconv.ParseUint(strings.TrimSpace(string(cmdOut)), 10, 32) 329 330 if err != nil { 331 return 0, err 332 } 333 334 generation := uint(generationUint32) 335 336 return generation, err 337 } 338 339 func SetUnattendedProductKey(path string, productKey string) error { 340 341 var script = ` 342 param([string]$path,[string]$productKey) 343 344 $unattend = [xml](Get-Content -Path $path) 345 $ns = @{ un = 'urn:schemas-microsoft-com:unattend' } 346 347 $setupNode = $unattend | 348 Select-Xml -XPath '//un:settings[@pass = "specialize"]/un:component[@name = "Microsoft-Windows-Shell-Setup"]' -Namespace $ns | 349 Select-Object -ExpandProperty Node 350 351 $productKeyNode = $setupNode | 352 Select-Xml -XPath '//un:ProductKey' -Namespace $ns | 353 Select-Object -ExpandProperty Node 354 355 if ($productKeyNode -eq $null) { 356 $productKeyNode = $unattend.CreateElement('ProductKey', $ns.un) 357 [Void]$setupNode.AppendChild($productKeyNode) 358 } 359 360 $productKeyNode.InnerText = $productKey 361 362 $unattend.Save($path) 363 ` 364 365 var ps PowerShellCmd 366 err := ps.Run(script, path, productKey) 367 return err 368 }