Public/Start-MyProcess.ps1

function Start-MyProcess {
<#
    .Synopsis
      Wraps up a call to execute a program using System.Diagnostics.Process. This allows us to redirect the stdout and stderr streams for better error handling. Specifcially this is used for quite a few MS utilities in our TFS build/deploy, as the utilities will usually throw warnings instead of terminating errors and we need to parse stdout to determine if there was an actuall error.
    .DESCRIPTION
        
    .EXAMPLE
        This example sets up a executable path, and options, then passes them to the function while captureing the returning stdout and stderr streams.
 
        Assume that $dacDinDir, $DestFile, $ConnectionString are all set to sueful values.
 
        $EXEPath = "$dacBinDir\SqlPackage.exe"
        $options = "/Action:Extract /OverwriteFiles:True /tf:$DestFile /scs:$ConnectionString"
 
        $return = Start-MyProcess -EXEPath $EXEPath -options $options
 
        if ($logLevel -eq "Debug"){
            #Only show the stdout stream if we are in debugging logLevel
            $return.stdout
        }
        if ($return.sterr -ne $null){
            Write-Log "$($return.sterr)" Warning
            Write-Log "There was an error of some type. See warning above for more info" Error
        }
    .OUTPUTS
        A object with 3 properties, stdout, stderr, and ExitCode. stdout and stderr are text streams that conatian output from the process. Generally if (stderr -eq $null) then there was some sort of error. You can also parse stdout to find errors, or check the ExitCode for non-success
        
    #>

  [CmdletBinding()]
  param(
    [Parameter(ValueFromPipeline = $True,Position = 0)] [string]$EXEPath
    ,[string]$options
    ,[Parameter(Position = 0)][ValidateSet("Debug","Info","Warning","Error","Disable")] [string]$logLevel = "Warning"
    ,[switch]$async
    ,[int]$sleepTimer = 5
    ,[string]$workingDir
  )


  Get-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState
  $currentLogLevel = Get-LogLevel
  if ([string]::IsNullOrEmpty($logLevel)) {
    $logLevel = "Warning"
  }
  Set-LogLevel $logLevel
  $EXE = $EXEPath.Substring($EXEPath.LastIndexOf("\") + 1,$EXEPath.Length - $EXEPath.LastIndexOf("\") - 1)
  $pinfo = New-Object System.Diagnostics.ProcessStartInfo
  $pinfo.FileName = "`"$EXEPath`""
  $pinfo.Arguments = "$options"
  $pinfo.UseShellExecute = $false
  $pinfo.CreateNoWindow = $true
  if ([string]::IsNullOrEmpty($workingDir)) {
    $pinfo.WorkingDirectory = Get-Location
  }
  else {
    $pinfo.WorkingDirectory = $workingDir
  }
  $pinfo.RedirectStandardOutput = $true
  $pinfo.RedirectStandardError = $true

  # Create a process object using the startup info
  $process = New-Object System.Diagnostics.Process
  $process.StartInfo = $pinfo

  Write-Log "Executing the following command" Debug
  Write-Log "$($pinfo.FileName) $($pinfo.Arguments)" Debug
  try {
    $process.Start() | Out-Null
  }
  catch {
    Write-Log "****Process errors****" Warning
    Write-Log "$($_.Exception.ToString())" Warning
    Write-Log "Error calling $EXE. See previous warning(s) for error text. Try running the script with a lower logLevel variable to collect more troubleshooting information. Aborting script" Error -ErrorAction Stop

  }

  if (!$async) {
    if (!$process.HasExited) {
      # Wait a while for the process to exitn
      Write-Log "$EXE is not done, let's wait 5 more seconds"
      sleep -Seconds $sleepTimer
    }
    Write-Log "$EXE has completed."
    # get output from stdout and stderr
    $stdout = $process.StandardOutput.ReadToEnd()
    $stderr = $process.StandardError.ReadToEnd()

    $stdOutput = New-Object -TypeName PSObject
    $stdOutput | Add-Member –MemberType NoteProperty –Name stderr –Value $stderr
    $stdOutput | Add-Member –MemberType NoteProperty –Name stdout –Value $stdout
    $stdOutput | Add-Member -MemberType NoteProperty -Name exitCode -Value $process.ExitCode

    $returnVal = $stdOutput
  }
  else {
    $returnVal = $process
  }

  Set-LogLevel $currentLogLevel
  return $returnVal
} Export-ModuleMember -Function Start-MyProcess