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  }