mirror of
https://github.com/Grace-Solutions/Invoke-OpenSSHConfiguration.git
synced 2026-07-26 19:48:14 +00:00
103181eedc
- Suppress Get-CIMInstance verbose output in Toolkit.ps1 with -Verbose:$False - Add CurrentUser Root and CA stores alongside LocalMachine stores - Dedupe certificates by Thumbprint (Sort-Object -Unique) - Suppress verbose from Process Select-Object (triggers WMI queries) - Use explicit property list instead of wildcard for process object
815 lines
49 KiB
PowerShell
815 lines
49 KiB
PowerShell
#region Start-ProcessWithOutput
|
|
Function Start-ProcessWithOutput
|
|
{
|
|
<#
|
|
.SYNOPSIS
|
|
Allows for the execution of processes with the ability to return their output without first dumping the content to a file. It can all be kept in memory.
|
|
|
|
.DESCRIPTION
|
|
This is not the best option when your process returns a large enough amount of output to cause a memory leak or overflow.
|
|
|
|
.PARAMETER FilePath
|
|
Your parameter description
|
|
|
|
.PARAMETER WorkingDirectory
|
|
Your parameter description
|
|
|
|
.PARAMETER ArgumentList
|
|
Your parameter description
|
|
|
|
.PARAMETER AcceptableExitCodeList
|
|
A * can be used to accept all exit codes.
|
|
|
|
.PARAMETER WindowStyle
|
|
Your parameter description
|
|
|
|
.PARAMETER CreateNoWindow
|
|
Your parameter description
|
|
|
|
.PARAMETER ExecutionTimeout
|
|
Your parameter description
|
|
|
|
.PARAMETER ExecutionTimeoutInterval
|
|
Your parameter description
|
|
|
|
.PARAMETER StandardInputObject
|
|
Your parameter description
|
|
|
|
.PARAMETER ParsingExpression
|
|
A valid regular expression that will allow for the output to be parsed into objects.
|
|
|
|
.PARAMETER SecureArgumentList
|
|
Your parameter description
|
|
|
|
.PARAMETER LogOutput
|
|
Your parameter description
|
|
|
|
.EXAMPLE
|
|
Start-ProcessWithOutput -FilePath 'cmd.exe' -ArgumentList '/c ipconfig /all' -AcceptableExitCodeList @('*') -CreateNoWindow -NoWait -Verbose
|
|
|
|
.EXAMPLE
|
|
Start-ProcessWithOutput -FilePath 'cmd.exe' -ArgumentList '/c ipconfig /all' -AcceptableExitCodeList @('*') -CreateNoWindow -Timeout ([System.TimeSpan]::FromSeconds(30)) -Verbose
|
|
|
|
.EXAMPLE
|
|
$StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$StartProcessWithOutputParameters.FilePath = "dsregcmd.exe"
|
|
$StartProcessWithOutputParameters.WorkingDirectory = "$([System.Environment]::SystemDirectory)"
|
|
$StartProcessWithOutputParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$StartProcessWithOutputParameters.ArgumentList.Add('/status')
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList.Add('0')
|
|
$StartProcessWithOutputParameters.WindowStyle = "Hidden"
|
|
$StartProcessWithOutputParameters.Priority = "Normal"
|
|
$StartProcessWithOutputParameters.ParsingExpression = "(?:\s+)(?<PropertyName>.+)(?:\s+\:\s+)(?<PropertyValue>.+)"
|
|
$StartProcessWithOutputParameters.LogOutput = $True
|
|
$StartProcessWithOutputParameters.ExecutionTimeout = [Timespan]::FromMinutes(1)
|
|
$StartProcessWithOutputParameters.ExecutionTimeoutInterval = [Timespan]::FromSeconds(30)
|
|
$StartProcessWithOutputParameters.StandardInputObjectList = New-Object -TypeName 'System.Collections.Generic.List[System.Object]'
|
|
$StartProcessWithOutputParameters.StandardInputObjectList.Add('S')
|
|
$StartProcessWithOutputParameters.SecureArgumentList = $False
|
|
$StartProcessWithOutputParameters.Verbose = $True
|
|
|
|
$StartProcessWithOutputResult = Start-ProcessWithOutput @StartProcessWithOutputParameters
|
|
|
|
Write-Output -InputObject ($StartProcessWithOutputResult)
|
|
|
|
.EXAMPLE
|
|
$StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$StartProcessWithOutputParameters.FilePath = "dsregcmd.exe"
|
|
$StartProcessWithOutputParameters.WorkingDirectory = "$([System.Environment]::SystemDirectory)"
|
|
$StartProcessWithOutputParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$StartProcessWithOutputParameters.ArgumentList.Add('/status')
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$StartProcessWithOutputParameters.AcceptableExitCodeList.Add('0')
|
|
$StartProcessWithOutputParameters.WindowStyle = "Hidden"
|
|
$StartProcessWithOutputParameters.Priority = "Normal"
|
|
$StartProcessWithOutputParameters.ParsingExpression = "(?:\s+)(?<PropertyName>.+)(?:\s+\:\s+)(?<PropertyValue>.+)"
|
|
$StartProcessWithOutputParameters.LogOutput = $True
|
|
$StartProcessWithOutputParameters.ExecutionTimeout = [Timespan]::FromMinutes(1)
|
|
$StartProcessWithOutputParameters.ExecutionTimeoutInterval = [Timespan]::FromSeconds(30)
|
|
$StartProcessWithOutputParameters.StandardInputObjectList = New-Object -TypeName 'System.Collections.Generic.List[System.Object]'
|
|
$StartProcessWithOutputParameters.StandardInputObjectList.Add('S')
|
|
$StartProcessWithOutputParameters.SecureArgumentList = $True
|
|
$StartProcessWithOutputParameters.Verbose = $True
|
|
|
|
$StartProcessWithOutputResult = Start-ProcessWithOutput @StartProcessWithOutputParameters
|
|
|
|
Write-Output -InputObject ($StartProcessWithOutputResult)
|
|
|
|
.NOTES
|
|
Mileage may vary when parsing output and may have to be done using additional code outside of this function to address specific needs.
|
|
|
|
.LINK
|
|
Place any useful link here where your function or cmdlet can be referenced
|
|
#>
|
|
|
|
[CmdletBinding(DefaultParameterSetName = 'WindowStyle')]
|
|
Param
|
|
(
|
|
[Parameter(Mandatory=$True)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias('FP')]
|
|
[String]$FilePath,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias('WD')]
|
|
[System.IO.DirectoryInfo]$WorkingDirectory,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[AllowEmptyCollection()]
|
|
[AllowNull()]
|
|
[Alias('AL')]
|
|
[String[]]$ArgumentList,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[AllowEmptyCollection()]
|
|
[AllowNull()]
|
|
[Alias('AECL')]
|
|
[String[]]$AcceptableExitCodeList,
|
|
|
|
[Parameter(Mandatory=$False, ParameterSetName = 'WindowStyle')]
|
|
[ValidateNotNullOrEmpty()]
|
|
[ValidateSet('Normal', 'Hidden', 'Minimized', 'Maximized')]
|
|
[Alias('WS')]
|
|
[String]$WindowStyle,
|
|
|
|
[Parameter(Mandatory=$False, ParameterSetName = 'CreateNoWindow')]
|
|
[Alias('CNW')]
|
|
[Switch]$CreateNoWindow,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('NW')]
|
|
[Switch]$NoWait,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[ValidateSet('AboveNormal', 'BelowNormal', 'High', 'Idle', 'Normal', 'RealTime')]
|
|
[Alias('P')]
|
|
[String]$Priority,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('ET')]
|
|
[System.Timespan]$ExecutionTimeout,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('ETI')]
|
|
[System.Timespan]$ExecutionTimeoutInterval,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[Alias('SIO')]
|
|
[System.Object[]]$StandardInputObjectList,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[AllowEmptyString()]
|
|
[AllowNull()]
|
|
[Alias('StandardOutputParsingExpression', 'SOPE', 'PE')]
|
|
[Regex]$ParsingExpression,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('SAL')]
|
|
[Switch]$SecureArgumentList,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('LO')]
|
|
[Switch]$LogOutput,
|
|
|
|
[Parameter(Mandatory=$False)]
|
|
[Alias('COE')]
|
|
[Switch]$ContinueOnError
|
|
)
|
|
|
|
Try
|
|
{
|
|
$DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss.FFF tt' ###Monday, January 01, 2019 @ 10:15:34.000 AM###
|
|
[ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)}
|
|
|
|
|
|
|
|
|
|
|
|
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
|
|
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
|
|
|
|
#Determine the date and time we executed the function
|
|
$FunctionStartTime = (Get-Date)
|
|
|
|
[String]$CmdletName = $MyInvocation.MyCommand.Name
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is beginning. Please Wait..."))
|
|
|
|
#Define Default Action Preferences
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key):$($_.Value.GetType().Name)"}
|
|
$WriteLogMessage.Invoke(0, @("Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Execution of $($CmdletName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Parameter Set Name: $($PSCmdlet.ParameterSetName)"))
|
|
|
|
#Set default parameter values (If necessary)
|
|
Switch ($True)
|
|
{
|
|
{([String]::IsNullOrEmpty($WindowStyle) -eq $True) -or ([String]::IsNullOrWhiteSpace($WindowStyle) -eq $True)}
|
|
{
|
|
[String]$WindowStyle = 'Hidden'
|
|
}
|
|
|
|
{([String]::IsNullOrEmpty($Priority) -eq $True) -or ([String]::IsNullOrWhiteSpace($Priority) -eq $True)}
|
|
{
|
|
[String]$Priority = 'Normal'
|
|
}
|
|
|
|
{($Null -ine $ExecutionTimeout)}
|
|
{
|
|
Switch ($Null -ieq $ExecutionTimeoutInterval)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ExecutionTimeoutInterval = [System.TimeSpan]::FromSeconds(15)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[ScriptBlock]$GetTimeSpanMessage = {
|
|
Param
|
|
(
|
|
[System.TimeSpan]$InputObject
|
|
)
|
|
|
|
Switch ($Null -ine $InputObject)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$InputObjectMessageBuilder = New-Object -TypeName 'System.Text.StringBuilder'
|
|
|
|
$InputObjectProperties = $InputObject | Select-Object -Property @('Days', 'Hours', 'Minutes', 'Seconds', 'Milliseconds')
|
|
|
|
$InputObjectPropertyNameList = New-Object -TypeName 'System.Collections.Generic.List[System.String]'
|
|
|
|
($InputObjectProperties.PSObject.Properties | Where-Object {($_.Value -gt 0)}).Name | ForEach-Object {($InputObjectPropertyNameList.Add($_))}
|
|
|
|
$InputObjectPropertyNameListUpperBound = $InputObjectPropertyNameList.ToArray().GetUpperBound(0)
|
|
|
|
For ($InputObjectPropertyNameListIndex = 0; $InputObjectPropertyNameListIndex -lt $InputObjectPropertyNameList.Count; $InputObjectPropertyNameListIndex++)
|
|
{
|
|
$InputObjectPropertyName = $InputObjectPropertyNameList[$InputObjectPropertyNameListIndex]
|
|
|
|
$InputObjectPropertyValue = $InputObject.$($InputObjectPropertyName)
|
|
|
|
Switch ($True)
|
|
{
|
|
{($InputObjectPropertyNameList.Count -gt 1) -and ($InputObjectPropertyNameListIndex -eq $InputObjectPropertyNameListUpperBound)}
|
|
{
|
|
$Null = $InputObjectMessageBuilder.Append('and ')
|
|
}
|
|
|
|
{($InputObjectPropertyValue -eq 1)}
|
|
{
|
|
$Null = $InputObjectMessageBuilder.Append("$($InputObjectPropertyValue) $($InputObjectPropertyName.TrimEnd('s').ToLower())")
|
|
}
|
|
|
|
{($InputObjectPropertyValue -gt 1)}
|
|
{
|
|
$Null = $InputObjectMessageBuilder.Append("$($InputObjectPropertyValue) $($InputObjectPropertyName.ToLower())")
|
|
}
|
|
|
|
{($InputObjectPropertyNameList.Count -gt 1) -and ($InputObjectPropertyNameListIndex -ne $InputObjectPropertyNameListUpperBound)}
|
|
{
|
|
$Null = $InputObjectMessageBuilder.Append(', ')
|
|
}
|
|
}
|
|
}
|
|
|
|
$OutputObject = $InputObjectMessageBuilder.ToString()
|
|
|
|
Switch ($InputObjectMessageBuilder.Length -gt 0)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$OutputObject = $InputObjectMessageBuilder.ToString()
|
|
}
|
|
|
|
Default
|
|
{
|
|
$OutputObject = 'N/A'
|
|
}
|
|
}
|
|
}
|
|
|
|
Default
|
|
{
|
|
$OutputObject = 'N/A'
|
|
}
|
|
}
|
|
|
|
Write-Output -InputObject ($OutputObject)
|
|
}
|
|
|
|
[ScriptBlock]$WriteStandardOutputStream = {
|
|
Param
|
|
(
|
|
[System.Object[]]$StandardInputObjectList,
|
|
[Switch]$SecureArgumentList
|
|
)
|
|
|
|
Switch (($Null -ine $StandardInputObjectList) -and ($StandardInputObjectList.Count -gt 0))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$StandardInputObjectListCounter = 1
|
|
|
|
For ($StandardInputObjectListIndex = 0; $StandardInputObjectListIndex -lt $StandardInputObjectList.Count; $StandardInputObjectListIndex++)
|
|
{
|
|
Try
|
|
{
|
|
$StandardInputObject = $StandardInputObjectList[$StandardInputObjectListIndex]
|
|
|
|
Switch ($SecureArgumentList.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ObfuscationCharacter = '*'
|
|
|
|
$ObfuscationCharacterCount = Get-Random -Minimum 5 -Maximum 20
|
|
|
|
$ObfuscationValue = $ObfuscationCharacter.PadRight($ObfuscationCharacterCount, $ObfuscationCharacter)
|
|
|
|
$WriteLogMessage.Invoke(0, @("Attempting to write standard input object $($StandardInputObjectListCounter) of $($StandardInputObjectList.Count) to the standard input stream for process ID $($Process.ID). Please Wait..."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Object Value: $($ObfuscationValue)"))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to write standard input object $($StandardInputObjectListCounter) of $($StandardInputObjectList.Count) to the standard input stream for process ID $($Process.ID). Please Wait..."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Object Value: $($StandardInputObject)"))
|
|
}
|
|
}
|
|
|
|
$Null = $Process.StandardInput.WriteLine($StandardInputObject)
|
|
}
|
|
Catch
|
|
{
|
|
$ExceptionPropertyDictionary = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$ExceptionPropertyDictionary.Add('Message', $_.Exception.Message)
|
|
$ExceptionPropertyDictionary.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
|
|
$ExceptionPropertyDictionary.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
|
|
$ExceptionPropertyDictionary.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
|
|
$ExceptionPropertyDictionary.Add('Code', $_.InvocationInfo.Line.Trim())
|
|
|
|
$ExceptionMessageList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
|
|
ForEach ($ExceptionProperty In $ExceptionPropertyDictionary.GetEnumerator())
|
|
{
|
|
$ExceptionMessageList.Add("[$($ExceptionProperty.Key): $($ExceptionProperty.Value)]")
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(2, @("$($ExceptionMessageList -Join ' ')"))
|
|
}
|
|
Finally
|
|
{
|
|
$StandardInputObjectListCounter++
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$OutputObjectProperties.ExitCode = -1
|
|
$OutputObjectProperties.ExitCodeAsHex = $Null
|
|
$OutputObjectProperties.ExitCodeAsInteger = $Null
|
|
$OutputObjectProperties.ExitCodeAsDecimal = $Null
|
|
$OutputObjectProperties.ProcessObject = $Null
|
|
$OutputObjectProperties.StandardOutput = $Null
|
|
$OutputObjectProperties.StandardOutputObject = $Null
|
|
$OutputObjectProperties.StandardError = $Null
|
|
$OutputObjectProperties.StandardErrorObject = $Null
|
|
|
|
$Process = New-Object -TypeName 'System.Diagnostics.Process'
|
|
$Process.StartInfo.FileName = $FilePath
|
|
$Process.StartInfo.UseShellExecute = $False
|
|
$Process.StartInfo.RedirectStandardOutput = $True
|
|
$Process.StartInfo.RedirectStandardError = $True
|
|
$Process.StartInfo.RedirectStandardInput = $True
|
|
|
|
Switch ($True)
|
|
{
|
|
{([String]::IsNullOrEmpty($WorkingDirectory.FullName) -eq $False) -and ([String]::IsNullOrWhiteSpace($WorkingDirectory.FullName) -eq $False)}
|
|
{
|
|
Switch ($True)
|
|
{
|
|
{([System.IO.Directory]::Exists($WorkingDirectory.FullName) -eq $False)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to create the non-existing process working directory. Please Wait..."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Path: $($WorkingDirectory.FullName)"))
|
|
|
|
$Null = [System.IO.Directory]::CreateDirectory($WorkingDirectory.FullName)
|
|
}
|
|
}
|
|
|
|
$Process.StartInfo.WorkingDirectory = $WorkingDirectory.FullName
|
|
}
|
|
}
|
|
|
|
Switch ($PSCmdlet.ParameterSetName)
|
|
{
|
|
{($_ -iin @('CreateNoWindow'))}
|
|
{
|
|
$Process.StartInfo.CreateNoWindow = $True
|
|
}
|
|
|
|
{($_ -iin @('WindowStyle'))}
|
|
{
|
|
$Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::"$($WindowStyle)"
|
|
}
|
|
}
|
|
|
|
Switch (($Null -ieq $AcceptableExitCodeList) -or ($AcceptableExitCodeList.Count -eq 0))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$DefaultExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
$DefaultExitCodeList.Add('0')
|
|
$DefaultExitCodeList.Add('3010')
|
|
|
|
$AcceptableExitCodeList = $DefaultExitCodeList.ToArray()
|
|
}
|
|
}
|
|
|
|
$CommandExecutionMessageBuilder = New-Object -TypeName 'System.Text.StringBuilder'
|
|
|
|
$Null = $CommandExecutionMessageBuilder.Append('Attempting to execute the following command:')
|
|
$Null = $CommandExecutionMessageBuilder.Append(' ')
|
|
$Null = $CommandExecutionMessageBuilder.Append($Process.StartInfo.FileName)
|
|
|
|
Switch (($Null -ine $ArgumentList) -and ($ArgumentList.Count -gt 0))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$Process.StartInfo.Arguments = $ArgumentList -Join ' '
|
|
|
|
Switch ($SecureArgumentList.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$ObfuscationCharacter = '*'
|
|
|
|
$ObfuscationCharacterCount = Get-Random -Minimum 5 -Maximum ($Process.StartInfo.Arguments.Length)
|
|
|
|
$ObfuscationValue = $ObfuscationCharacter.PadRight($ObfuscationCharacterCount, $ObfuscationCharacter)
|
|
|
|
$Null = $CommandExecutionMessageBuilder.Append(' ')
|
|
$Null = $CommandExecutionMessageBuilder.Append($ObfuscationValue)
|
|
}
|
|
|
|
Default
|
|
{
|
|
$Null = $CommandExecutionMessageBuilder.Append(' ')
|
|
$Null = $CommandExecutionMessageBuilder.Append($Process.StartInfo.Arguments)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(0, @("$($CommandExecutionMessageBuilder.ToString())"))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Acceptable Exit Code List: $($AcceptableExitCodeList -Join '; ')"))
|
|
|
|
Switch ($NoWait.IsPresent)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$Null = $Process.Start()
|
|
|
|
$WriteLogMessage.Invoke(0, @("Skipping the execution wait for process ID $($Process.ID)."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("[ProcessName: $([System.IO.Path]::GetFileName($Process.Path))] [ID: $($Process.ID)] [Version: $($Process.FileVersion)] [Description: $($Process.Description)] [Path: $($Process.Path)] "))
|
|
|
|
$Null = $WriteStandardOutputStream.InvokeReturnAsIs($StandardInputObjectList, $SecureArgumentList.IsPresent)
|
|
|
|
$OutputObjectProperties.StandardOutput = $Null
|
|
$OutputObjectProperties.StandardError = $Null
|
|
}
|
|
|
|
Default
|
|
{
|
|
Switch ($Null -ieq $ExecutionTimeout)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("A timeout was not specified for process ID $($Process.ID)."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("The wait for process ID $($Process.ID) termination will be indefinite."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$ProcessTimeoutStopWatch = New-Object -TypeName 'System.Diagnostics.StopWatch'
|
|
|
|
$Null = $ProcessTimeoutStopWatch.Start()
|
|
}
|
|
}
|
|
|
|
$Null = $Process.Start()
|
|
|
|
Switch ($Process.PriorityClass -ine $Priority)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to change the process priority class for process ID $($Process.ID) from `"$($Process.PriorityClass)`" to `"$($Priority)`". Please Wait..."))
|
|
|
|
$Null = $Process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::"$($Priority)"
|
|
}
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(0, @("[ProcessName: $([System.IO.Path]::GetFileName($Process.Path))] [ID: $($Process.ID)] [Version: $($Process.FileVersion)] [Description: $($Process.Description)] [Path: $($Process.Path)] "))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Process Start Time: $($Process.StartTime.ToString($DateTimeLogFormat))"))
|
|
|
|
Switch ($Null -ine $ExecutionTimeout)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Process Timeout Time: $($Process.StartTime.AddTicks($ExecutionTimeout.Ticks).ToString($DateTimeLogFormat))"))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Process Timeout Duration: $($GetTimeSpanMessage.InvokeReturnAsIs($ExecutionTimeout))"))
|
|
}
|
|
}
|
|
|
|
$Null = $WriteStandardOutputStream.InvokeReturnAsIs($StandardInputObjectList, $SecureArgumentList.IsPresent)
|
|
|
|
$OutputObjectProperties.StandardOutput = $Process.StandardOutput.ReadToEndAsync()
|
|
$OutputObjectProperties.StandardError = $Process.StandardError.ReadToEndAsync()
|
|
|
|
$ProcessTimeOutLoopCondition = {$Process.HasExited -eq $False}
|
|
|
|
:ProcessTimeoutLoop While ($ProcessTimeOutLoopCondition.InvokeReturnAsIs() -eq $True)
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Process ID $($Process.ID) has been running for $($GetTimeSpanMessage.InvokeReturnAsIs($ProcessTimeoutStopWatch.Elapsed))."))
|
|
|
|
Switch ($Null -ine $ExecutionTimeout)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
Switch ($ProcessTimeoutStopWatch.Elapsed.TotalMilliseconds -le $ExecutionTimeout.TotalMilliseconds)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Process ID $($Process.ID) has not exceeded the maximum timeout duration of $($GetTimeSpanMessage.InvokeReturnAsIs($ExecutionTimeout))."))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Process ID $($Process.ID) has exceeded the maximum timeout duration of $($GetTimeSpanMessage.InvokeReturnAsIs($ExecutionTimeout))."))
|
|
|
|
Try
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to forcibly terminate the process ID $($Process.ID). Please Wait..."))
|
|
|
|
$Null = $Process.Kill()
|
|
|
|
Switch ($?)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Process ID $($Process.ID) was forcibly terminated successfully."))
|
|
}
|
|
}
|
|
}
|
|
Catch
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("$($_.Exception.Message)"))
|
|
}
|
|
|
|
Break ProcessTimeoutLoop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(0, @("Checking again in another $($GetTimeSpanMessage.InvokeReturnAsIs($ExecutionTimeoutInterval)). Please Wait..."))
|
|
|
|
$Null = Start-Sleep -Milliseconds ($ExecutionTimeoutInterval.TotalMilliseconds)
|
|
}
|
|
|
|
$OutputObjectProperties.ExitCode = Try {$Process.ExitCode} Catch {$Null}
|
|
$OutputObjectProperties.ExitCodeAsHex = Try {'0x' + [System.Convert]::ToString($OutputObjectProperties.ExitCode, 16).PadLeft(8, '0').ToUpper()} Catch {$Null}
|
|
$OutputObjectProperties.ExitCodeAsInteger = Try {$OutputObjectProperties.ExitCodeAsHex -As [Int]} Catch {$Null}
|
|
$OutputObjectProperties.ExitCodeAsDecimal = Try {[System.Convert]::ToString($OutputObjectProperties.ExitCodeAsHex, 10)} Catch {$Null}
|
|
|
|
$ExitCodeMessageList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
|
|
$Null = $OutputObjectProperties.GetEnumerator() | Where-Object {($_.Key -imatch '(^ExitCode.*$)')} | Sort-Object -Property @('Key') | ForEach-Object {$ExitCodeMessageList.Add("[$($_.Key): $($_.Value)]")}
|
|
|
|
$ProcessExecutionTimespan = New-TimeSpan -Start ($Process.StartTime) -End ($Process.ExitTime)
|
|
|
|
#Suppress verbose output from Select-Object on Process object (can trigger WMI queries)
|
|
$OutputObjectProperties.ProcessObject = $Process | Select-Object -Property @('Id', 'ProcessName', 'Path', 'StartTime', 'ExitTime', 'HasExited', 'PriorityClass') -Verbose:$False -ErrorAction SilentlyContinue
|
|
|
|
$WriteLogMessage.Invoke(0, @("Process Exit Time: $($Process.ExitTime.ToString($DateTimeLogFormat))"))
|
|
|
|
#Dispose of the process object
|
|
Try {$Null = $Process.Dispose()} Catch {}
|
|
|
|
$WriteLogMessage.Invoke(0, @("The command execution took $($GetTimeSpanMessage.InvokeReturnAsIs($ProcessExecutionTimespan))."))
|
|
|
|
Switch (($AcceptableExitCodeList -icontains '*') -or ($OutputObjectProperties.ExitCode.ToString() -iin $AcceptableExitCodeList) -or ($OutputObjectProperties.ExitCodeAsHex.ToString() -iin $AcceptableExitCodeList) -or ($OutputObjectProperties.ExitCodeAsInteger.ToString() -iin $AcceptableExitCodeList) -or ($OutputObjectProperties.ExitCodeAsDecimal.ToString() -iin $AcceptableExitCodeList))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("The command execution was successful. $($ExitCodeMessageList -Join ' ')"))
|
|
|
|
[Boolean]$CommandExecutionErrorOccured = $False
|
|
}
|
|
|
|
{($_ -eq $False)}
|
|
{
|
|
$WriteLogMessage.Invoke(2, @("The command execution was unsuccessful. $($ExitCodeMessageList -Join ' ')"))
|
|
|
|
$ErrorMessage = "The command execution was unsuccessful. $($ExitCodeMessageList -Join ' ')"
|
|
$Exception = [System.Exception]::New($ErrorMessage)
|
|
$ErrorRecord = [System.Management.Automation.ErrorRecord]::New($Exception, [System.Management.Automation.ErrorCategory]::InvalidResult.ToString(), [System.Management.Automation.ErrorCategory]::InvalidResult, $Process)
|
|
|
|
[Boolean]$CommandExecutionErrorOccured = $True
|
|
}
|
|
}
|
|
|
|
Switch (([String]::IsNullOrEmpty($ParsingExpression) -eq $False) -and ([String]::IsNullOrWhiteSpace($ParsingExpression) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$RegexOptions = New-Object -TypeName 'System.Collections.Generic.List[System.Text.RegularExpressions.RegexOptions]'
|
|
$RegexOptions.Add('IgnoreCase')
|
|
$RegexOptions.Add('Multiline')
|
|
|
|
$Regex = New-Object -TypeName 'System.Text.RegularExpressions.Regex' -ArgumentList @($ParsingExpression, $RegexOptions.ToArray())
|
|
|
|
$OutputStreamDictionary = New-Object -TypeName 'System.Collections.Generic.Dictionary[[String], [String]]'
|
|
$OutputStreamDictionary.Add('StandardOutput', $OutputObjectProperties.StandardOutput)
|
|
$OutputStreamDictionary.Add('StandardError', $OutputObjectProperties.StandardError)
|
|
|
|
ForEach ($OutputStream In $OutputStreamDictionary.GetEnumerator())
|
|
{
|
|
Switch (([String]::IsNullOrEmpty($OutputStream.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($OutputStream.Value) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to parse the `"$($OutputStream.Key)`" property value. Please Wait..."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Regular Expression: $($ParsingExpression)"))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Regular Expression Options: $($RegexOptions -Join ', ')"))
|
|
|
|
$OutputStreamRegexResult = $Regex.IsMatch($OutputStream.Value)
|
|
|
|
Switch ($OutputStreamRegexResult)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$RegexMatchList = $Regex.Matches($OutputStream.Value)
|
|
|
|
$WriteLogMessage.Invoke(0, @("The `"$($OutputStream.Key)`" property value matches the regular expression. $($RegexMatchList.Count) matches were found."))
|
|
|
|
$OutputObjectProperties."$($OutputStream.Key)Object" = $RegexMatchList
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("The `"$($OutputStream.Key)`" property value does not match the regular expression."))
|
|
|
|
$OutputObjectProperties."$($OutputStream.Key)Object" = New-Object -TypeName 'System.Collections.Generic.List[System.Text.RegularExpressions.Match]'
|
|
}
|
|
}
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Skipping the parsing of the `"$($OutputStream.Key)`" property value because it is blank."))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$Null = $ProcessTimeoutStopWatch.Stop()
|
|
|
|
$Null = $ProcessTimeoutStopWatch.Reset()
|
|
}
|
|
}
|
|
}
|
|
Catch
|
|
{
|
|
$ExceptionPropertyDictionary = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
|
|
$ExceptionPropertyDictionary.Add('Message', $_.Exception.Message)
|
|
$ExceptionPropertyDictionary.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
|
|
$ExceptionPropertyDictionary.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
|
|
$ExceptionPropertyDictionary.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
|
|
$ExceptionPropertyDictionary.Add('Code', $_.InvocationInfo.Line.Trim())
|
|
|
|
$ExceptionMessageList = New-Object -TypeName 'System.Collections.Generic.List[String]'
|
|
|
|
ForEach ($ExceptionProperty In $ExceptionPropertyDictionary.GetEnumerator())
|
|
{
|
|
$ExceptionMessageList.Add("[$($ExceptionProperty.Key): $($ExceptionProperty.Value)]")
|
|
}
|
|
|
|
$WriteLogMessage.Invoke(2, @("$($ExceptionMessageList -Join ' ')"))
|
|
}
|
|
Finally
|
|
{
|
|
$OutputObjectProperties.StandardOutput = Try {$OutputObjectProperties.StandardOutput.Result} Catch {$Null}
|
|
$OutputObjectProperties.StandardError = Try {$OutputObjectProperties.StandardError.Result} Catch {$Null}
|
|
|
|
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
|
|
|
|
Switch (($LogOutput.IsPresent -eq $True) -or ($LogOutput -eq $True) -or ($CommandExecutionErrorOccured -eq $True))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
ForEach ($Property In $OutputObject.PSObject.Properties)
|
|
{
|
|
Switch ($Property.Name)
|
|
{
|
|
{($_ -iin @('StandardOutput', 'StandardError'))}
|
|
{
|
|
Switch (([String]::IsNullOrEmpty($Property.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($Property.Value) -eq $False))
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("$($Property.Name): $($Property.Value)"))
|
|
}
|
|
|
|
Default
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("$($Property.Name): N/A"))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Try
|
|
{
|
|
Switch ($NoWait.IsPresent)
|
|
{
|
|
{($_ -eq $False)}
|
|
{
|
|
Switch ($Process.HasExited)
|
|
{
|
|
{($_ -eq $False)}
|
|
{
|
|
$WriteLogMessage.Invoke(0, @("Attempting to forcibly terminate the process ID $($Process.ID). Please Wait..."))
|
|
|
|
$Null = Try {$Process.CancelErrorRead()} Catch {}
|
|
$Null = Try {$Process.CancelOutputRead()} Catch {}
|
|
$Null = Try {$Process.Kill()} Catch {}
|
|
$Null = Try {$Process.Dispose()} Catch {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Catch
|
|
{
|
|
|
|
}
|
|
|
|
#Determine the date and time the function completed execution
|
|
$FunctionEndTime = (Get-Date)
|
|
|
|
$WriteLogMessage.Invoke(0, @("Execution of $($CmdletName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"))
|
|
|
|
#Log the total script execution time
|
|
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function execution took $($GetTimeSpanMessage.InvokeReturnAsIs($FunctionExecutionTimespan))."))
|
|
|
|
$WriteLogMessage.Invoke(0, @("Function `'$($CmdletName)`' is completed."))
|
|
|
|
Switch ($CommandExecutionErrorOccured)
|
|
{
|
|
{($_ -eq $True)}
|
|
{
|
|
Write-Output -InputObject ($OutputObject)
|
|
|
|
If ($ContinueOnError.IsPresent -eq $False) {$PSCmdlet.ThrowTerminatingError($ErrorRecord)}
|
|
}
|
|
|
|
Default
|
|
{
|
|
Write-Output -InputObject ($OutputObject)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion |