title: Redirecting stdout and stderr and elevate to administrator using START-PROCESS
body: <p>As part of our server build and configuration which is all done using Powershell, I need to remotely install .net framework 4.5.1 on a number of Server 2012 machines. <a href="http://www.microsoft.com/en-gb/download/details.aspx?id=40779" rel="nofollow">The offline install</a> ships as an .exe which, when run, unpacks a number of MSI installers and a setup program.</p>
  
  <p>The code we use to run installers can work with MSI files or exe's. If we use MSI files then a call is made to MSIEXEC and if it is an EXE then that program is called directly. Any arguments required can also be passed.</p>
  
  <p>For our purposes this is acheived using the Powershell cmdlet Start-Process.</p>
  
  <p>I need to get the return code to work out whether the install has completed correctly and would also like to capture stdout and stderr to help diagnose any problems if the installation failed.</p>
  
  <p>I have the following custom written function to wrap the Start-Process cmdlet.</p>
  
  <pre><code>Function Start-Proc()
  {
  [CmdletBinding()]
      param (
      [string][ValidateNotNullOrEmpty()][ValidateScript({Test-Path -Path $_ -Type Leaf})] $FilePath,
      [string][ValidateNotNullOrEmpty()]$Arguments,
      [switch] $Hidden,
      [switch] $WaitForExit
      )
  
  #Create files to hold the output to avoid the deadlock issue that seems to arise when we read
  #the output and error at the same time http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.110).aspx
  $errorFilePath = Join-Path -Path $env:TEMP -ChildPath ([system.guid]::NewGuid().ToString())
  $outputFilePath = Join-Path -Path $env:TEMP -ChildPath ([system.guid]::NewGuid().ToString())
  
  $process = Start-Process `
                  -FilePath $FilePath `
                  -ArgumentList $Arguments `
                  -RedirectStandardError $errorFilePath `
                  -RedirectStandardOutput $outputFilePath `
                  -NoNewWindow:$Hidden `
                  -PassThru `
                  -Wait:$WaitForExit
  
  $errorOut = Get-Content -Path $errorFilePath
  $stdOut = Get-Content -Path $outputFilePath
  
  #Tidy up files
  If ($process.HasExited)
  {
      Remove-Item $errorFilePath
      Remove-Item $outputFilePath
  }
  
  #Create new object to send the messages and exit code back
  $output = New-Object -TypeName PSObject
  $output | Add-Member –MemberType NoteProperty -Name Message -Value $stdOut
  $output | Add-Member –MemberType NoteProperty -Name ErrorMessage -Value $errorOut
  $output | Add-Member –MemberType NoteProperty -Name ExitCode -Value $process.ExitCode
  
  return $output
  }
  </code></pre>
  
  <p>Note: I have to use the -RedirectStandardOutput and -RedirectStandardInput Params sending the output to files and then using Get-Content to avoid a deadlock reading the properties as text.</p>
  
  <p>Calling this function to install .NET framework 4.5.1 always fails with an error code of 5 which is Access Denied. </p>
  
  <p>Looking into this it appears you need to use the 
      -Verb RunAs
  parameter with Start-Process to elevate the command as administrator however adding that parameter to the cmdlet causes the following error:</p>
  
  <pre><code>Parameter set cannot be resolved using the specified named parameters.
  </code></pre>
  
  <p>This is because -Verb and -RedirectStandardOutput &amp; -RedirectStandardError are in different parameter sets.</p>
  
  <p>My question is therefore:</p>
  
  <p>How can I run an executable from Powershell that does the following:</p>
  
  <ol>
  <li>captures the exit code</li>
  <li>captures the output and error messages</li>
  <li>runs as elevated administrator</li>
  </ol>
  
  <p>I have already tried using the .NET System.Diagnostics.Process.Start method directly however this suffers from the same problem.</p>
  
  <p>Thanks for any help as this is proving to be a difficult problem!</p>