github.com/codecheq/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  }