Major adjustments

This commit is contained in:
freedbygrace
2023-03-23 22:56:35 -04:00
parent 1a3f75a3b3
commit 9c57fbbfe1
12 changed files with 3742 additions and 222 deletions
+621
View File
@@ -0,0 +1,621 @@
## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
#region Function Copy-ItemWithProgress
Function Copy-ItemWithProgress
{
<#
.SYNOPSIS
Copies file(s) from a valid path in segments.
.DESCRIPTION
Supports the copying of files from a valid location in segments either directly or recursively.
Supports the usage of filters and exclusions exactly the way that the Get-ChildItem or Copy-Item cmdlets work.
.PARAMETER Path
A valid file or folder location.
.PARAMETER Destination
The destination directory where the content will be copied.
.PARAMETER Include
One or more filter(s) to implicitly copy what is specified.
.PARAMETER Exclude
One or more filter(s) to implicitly skip the copying of what is specified.
.PARAMETER Recurse
Recurse through the specified directories.
.PARAMETER Force
Overwrite file(s) that have already been copied. The default behavior is to skip the copying of the file.
.PARAMETER SegmentSize
The segment size in megabytes that each file will be transferred with.
.PARAMETER RandomDelay
Adds a pulse in between the transfer of each segment.
.PARAMETER ContinueOnError
Continues processing even if an error has occured.
.EXAMPLE
Copy-ItemWithProgress -Path 'FileOrDirectoryPath' -Destination 'YourDestinationDirectory' -Verbose
.EXAMPLE
$CopyItemWithProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$CopyItemWithProgressParameters.Path = "FileOrDirectoryPath"
$CopyItemWithProgressParameters.Destination = "YourDestinationDirectory"
$CopyItemWithProgressParameters.Include = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CopyItemWithProgressParameters.Include.Add('*.*')
$CopyItemWithProgressParameters.Exclude = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CopyItemWithProgressParameters.Exclude.Add('*.ini')
$CopyItemWithProgressParameters.Recurse = $True
$CopyItemWithProgressParameters.Force = $False
$CopyItemWithProgressParameters.SegmentSize = 4096
$CopyItemWithProgressParameters.RandomDelay = $False
$CopyItemWithProgressParameters.ContinueOnError = $False
$CopyItemWithProgressParameters.Verbose = $True
$CopyItemWithProgressResult = Copy-ItemWithProgress @CopyItemWithProgressParameters
Write-Output -InputObject ($CopyItemWithProgressResult)
.NOTES
A progress bar is displayed and can be useful for scripts where the copying of large file(s) is taking place.
.LINK
http://stackoverflow.com/questions/2434133/progress-during-large-file-copy-copy-item-write-progress
.LINK
https://stackoverflow.com/questions/13883404/custom-robocopy-progress-bar-in-powershell
#>
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path -Path $_})]
[Alias('P')]
[String]$Path,
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[Alias('D')]
[System.IO.DirectoryInfo]$Destination,
[Parameter(Mandatory=$False)]
[Alias('I')]
[String[]]$Include,
[Parameter(Mandatory=$False)]
[Alias('E')]
[String[]]$Exclude,
[Parameter(Mandatory=$False)]
[Alias('R')]
[Switch]$Recurse,
[Parameter(Mandatory=$False)]
[Alias('Overwrite')]
[Switch]$Force,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('SS', 'BufferInMegabytes')]
[Int]$SegmentSize,
[Parameter(Mandatory=$False)]
[Alias('RD')]
[Switch]$RandomDelay,
[Parameter(Mandatory=$False)]
[Alias('COE')]
[Switch]$ContinueOnError
)
Begin
{
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)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$TextInfo = (Get-Culture).TextInfo
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.Add('LogMessage', $Null)
$LoggingDetails.Add('WarningMessage', $Null)
$LoggingDetails.Add('ErrorMessage', $Null)
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
$FileSystemObject = New-Object -ComObject 'Scripting.FileSystemObject'
[ScriptBlock]$ErrorHandlingDefinition = {
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('Message', $_.Exception.Message)
$ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
$ErrorMessageList.Add('Script', $_.InvocationInfo.ScriptName)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$LoggingDetails.ErrorMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - ERROR: $($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($LoggingDetails.ErrorMessage)
}
Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False))
{
{($_ -eq $True)}
{
Throw
}
}
}
#Determine the date and time we executed the function
$FunctionStartTime = (Get-Date)
[String]$FunctionName = $MyInvocation.MyCommand
[System.IO.FileInfo]$InvokingScriptPath = $MyInvocation.PSCommandPath
[System.IO.DirectoryInfo]$InvokingScriptDirectory = $InvokingScriptPath.Directory.FullName
[System.IO.FileInfo]$FunctionPath = "$($InvokingScriptDirectory.FullName)\Functions\$($FunctionName).ps1"
[System.IO.DirectoryInfo]$FunctionDirectory = "$($FunctionPath.Directory.FullName)"
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is beginning. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#Define Default Action Preferences
$ErrorActionPreference = 'Stop'
[String[]]$AvailableScriptParameters = (Get-Command -Name ($FunctionName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key):$($_.Value.GetType().Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#region Load any required libraries
[System.IO.DirectoryInfo]$LibariesDirectory = "$($FunctionDirectory.FullName)\Libraries"
Switch ([System.IO.Directory]::Exists($LibariesDirectory.FullName))
{
{($_ -eq $True)}
{
$LibraryPatternList = New-Object -TypeName 'System.Collections.Generic.List[String]'
#$LibraryPatternList.Add('')
Switch ($LibraryPatternList.Count -gt 0)
{
{($_ -eq $True)}
{
$LibraryList = Get-ChildItem -Path ($LibariesDirectory.FullName) -Include ($LibraryPatternList.ToArray()) -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])}
$LibraryListCount = ($LibraryList | Measure-Object).Count
Switch ($LibraryListCount -gt 0)
{
{($_ -eq $True)}
{
For ($LibraryListIndex = 0; $LibraryListIndex -lt $LibraryListCount; $LibraryListIndex++)
{
$Library = $LibraryList[$LibraryListIndex]
[Byte[]]$LibraryBytes = [System.IO.File]::ReadAllBytes($Library.FullName)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to load assembly `"$($Library.FullName)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$Null = [System.Reflection.Assembly]::Load($LibraryBytes)
}
}
}
}
}
}
}
#endregion
#region Set Default Parameter Values
Switch ($True)
{
{($Null -ieq $SegmentSize) -or ($SegmentSize -eq 0)}
{
[Int]$SegmentSize = 8192
}
}
#endregion
#Create an object that will contain the functions output.
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
}
}
Process
{
Try
{
Switch ($Null -ine $Path)
{
{($_ -eq $True)}
{
$GetChildItemParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$GetChildItemParameters.Path = $Path
$GetChildItemParameters.Force = $True
$GetChildItemParameters.ErrorAction = [System.Management.Automation.Actionpreference]::SilentlyContinue
Switch ($True)
{
{([String]::IsNullOrEmpty($Include) -eq $False) -and ([String]::IsNullOrWhiteSpace($Include) -eq $False)}
{
$GetChildItemParameters.Include = $Include
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - File(s) matching $($GetChildItemParameters.Include -Join ', ') will be included."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
{([String]::IsNullOrEmpty($Exclude) -eq $False) -and ([String]::IsNullOrWhiteSpace($Exclude) -eq $False)}
{
$GetChildItemParameters.Exclude = $Exclude
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - File(s) matching $($GetChildItemParameters.Exclude -Join ', ') will be excluded."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
{($Recurse.IsPresent -eq $True)}
{
$GetChildItemParameters.Recurse = $True
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The search will be performed recursively."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
}
$FileList = Get-ChildItem @GetChildItemParameters | Where-Object {($_ -is [System.IO.FileInfo])}
$FileListDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$FileListDetails.Count = ($FileList | Measure-Object).Count
Switch ($FileListDetails.Count -gt 0)
{
{($_ -eq $True)}
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($FileListDetails.Count) file(s) will be copied in $($SegmentSize) MB segments to the destination directory of `"$($Destination.FullName)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$FileListDetails.TotalBytes = ($FileList | Measure-Object -Sum 'Length').Sum
$FileListDetails.TransferredBytes = 0
$FileListDetails.PercentComplete = 0
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A total of $([System.Math]::Round(($FileListDetails.TotalBytes / 1MB), 2)) MB needs to be transferred."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$StopWatchTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$StopWatchTable.Primary = New-Object -TypeName 'System.Diagnostics.Stopwatch'
$StopWatchTable.Secondary = New-Object -TypeName 'System.Diagnostics.Stopwatch'
$Null = $StopWatchTable.Primary.Start()
For ($FileListIndex = 0; $FileListIndex -lt $FileListDetails.Count; $FileListIndex++)
{
Try
{
$FileObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$FileObjectProperties.Source = ($FileList[$FileListIndex]) -As [System.IO.FileInfo]
Switch ($Recurse.IsPresent)
{
{($_ -eq $True)}
{
$FileObjectProperties.SourceSegmentList = $FileObjectProperties.Source.FullName.Replace($Path, '').Split('\', [System.StringSplitOptions]::RemoveEmptyEntries) -As [System.Collections.Generic.List[String]]
$FileObjectProperties.Destination = (Join-Path -Path ($Destination.FullName) -ChildPath ($FileObjectProperties.SourceSegmentList -Join '\')) -As [System.IO.FileInfo]
$FileObjectProperties.Remove('SourceSegmentList')
}
Default
{
$FileObjectProperties.Destination = "$($Destination.FullName)\$($FileObjectProperties.Source.Name)" -As [System.IO.FileInfo]
}
}
Switch ($True)
{
{([System.IO.Directory]::Exists($FileObjectProperties.Destination.Directory.FullName) -eq $False)}
{
$Null = [System.IO.Directory]::CreateDirectory($FileObjectProperties.Destination.Directory.FullName)
}
}
Switch (([System.IO.File]::Exists($FileObjectProperties.Destination.FullName) -eq $False) -or ($Force.IsPresent -eq $True))
{
{($_ -eq $True)}
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to copy `"$($FileObjectProperties.Source.FullName)`" to `"$($FileObjectProperties.Destination.FullName)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$FileDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$FileDetails.Source = [System.IO.File]::OpenRead($FileObjectProperties.Source.FullName)
$FileDetails.Destination = [System.IO.File]::Create($FileObjectProperties.Destination.FullName)
$FileDetails.Buffer = New-Object -TypeName 'Byte[]' -ArgumentList ($SegmentSize * 1024)
$FileDetails.SegmentBytes = 0
$FileDetails.TransferredBytes = 0
$FileDetails.Number = $FileListIndex + 1
$Null = $StopWatchTable.Secondary.Start()
Do
{
$FileDetails.SegmentBytes = $FileDetails.Source.Read($FileDetails.Buffer, 0, $FileDetails.Buffer.Length)
$FileDetails.Destination.Write($FileDetails.Buffer, 0, $FileDetails.SegmentBytes)
$FileDetails.TransferredBytes = $FileDetails.TransferredBytes + $FileDetails.SegmentBytes
$FileListDetails.TransferredBytes = $FileListDetails.TransferredBytes + $FileDetails.SegmentBytes
Switch ($FileDetails.Source.Length -gt 1)
{
{($_ -eq $True)}
{
$FileDetails.PercentComplete = (($FileDetails.TransferredBytes / $FileDetails.Source.Length) * 100) -As [Int]
}
Default
{
$FileDetails.PercentComplete = (100) -As [Int]
}
}
$FileDetails.PercentComplete = [System.Math]::Round($FileDetails.PercentComplete, 2)
$FileDetails.TotalSecondsElasped = $StopWatchTable.Secondary.Elapsed.TotalSeconds -As [Int]
Switch ($FileDetails.TotalSecondsElasped -ne 0)
{
{($_ -eq $True)}
{
$FileDetails.TransferRate = ($FileDetails.TransferredBytes / $FileDetails.TotalSecondsElasped / 1MB) -As [Single]
}
Default
{
$FileDetails.TransferRate = (0.0) -As [Single]
}
}
$FileDetails.TransferRate = [System.Math]::Round($FileDetails.TransferRate, 2)
Switch (($Total % 1MB) -eq 0)
{
{($_ -eq $True)}
{
Switch ($FileDetails.PercentComplete -gt 0)
{
{($_ -eq $True)}
{
$FileDetails.SecondsRemaining = ((($FileDetails.TotalSecondsElasped / $FileDetails.PercentComplete) * 100) - $FileDetails.TotalSecondsElasped) -As [Int]
}
Default
{
$FileDetails.SecondsRemaining = (0) -As [Int]
}
}
$FileListDetails.PercentComplete = [System.Math]::Round((($FileListDetails.TransferredBytes / $FileListDetails.TotalBytes) * 100), 2)
$FileListDetails.TotalSecondsElasped = $StopWatchTable.Primary.Elapsed.TotalSeconds -As [Int]
Switch ($FileListDetails.PercentComplete -gt 0)
{
{($_ -eq $True)}
{
$FileListDetails.SecondsRemaining = ((($FileListDetails.TotalSecondsElasped / $FileListDetails.PercentComplete) * 100) - $FileListDetails.TotalSecondsElasped) -As [Int]
}
Default
{
$FileListDetails.SecondsRemaining = ((($FileListDetails.TotalSecondsElasped / $FileListDetails.PercentComplete) * 100) - $FileListDetails.TotalSecondsElasped) -As [Int]
}
}
$TaskSequence = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$TaskSequence.Environment = Try {New-Object -ComObject 'Microsoft.SMS.TSEnvironment'} Catch {$Null}
$TaskSequence.IsRunning = $Null -ine $TaskSequence.Environment
Switch ($TaskSequence.IsRunning)
{
{($_ -eq $True)}
{
$FileProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$FileProgressParameters.ID = 1
$FileProgressParameters.Activity = "$($FileDetails.PercentComplete)% - Downloading $($FileSystemObject.GetFile($FileObjectProperties.Destination.FullName).ShortName) @ $($FileDetails.TransferRate) Mbps ($($FileDetails.Number) of $($FileListDetails.Count))"
$FileProgressParameters.Status = "$($FileDetails.PercentComplete)% - Downloading $($FileSystemObject.GetFile($FileObjectProperties.Destination.FullName).ShortName) @ $($FileDetails.TransferRate) Mbps ($($FileDetails.Number) of $($FileListDetails.Count))"
$FileProgressParameters.PercentComplete = $FileDetails.PercentComplete
$FileProgressParameters.SecondsRemaining = $FileDetails.SecondsRemaining
Write-Progress @FileProgressParameters
}
Default
{
$TotalProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$TotalProgressParameters.ID = 1
$TotalProgressParameters.Activity = "$($FileListDetails.PercentComplete)% - Copying file $($FileDetails.Number) of $($FileListDetails.Count) ($($FileListDetails.Count - $($FileListIndex)) left)"
$TotalProgressParameters.Status = $FileObjectProperties.Source.FullName
$TotalProgressParameters.PercentComplete = $FileListDetails.PercentComplete
$TotalProgressParameters.SecondsRemaining = $FileListDetails.SecondsRemaining
Write-Progress @TotalProgressParameters
$FileProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$FileProgressParameters.ParentID = $TotalProgressParameters.ID
$FileProgressParameters.ID = $TotalProgressParameters.ID + 1
$FileProgressParameters.Activity = "$($FileDetails.PercentComplete)% - Copying @ $($FileDetails.TransferRate) Mbps"
$FileProgressParameters.Status = $FileObjectProperties.Destination.FullName
$FileProgressParameters.PercentComplete = $FileDetails.PercentComplete
$FileProgressParameters.SecondsRemaining = $FileDetails.SecondsRemaining
Write-Progress @FileProgressParameters
}
}
}
}
Switch ($RandomDelay.IsPresent)
{
{($_ -eq $True)}
{
$Delay = Get-Random -Minimum 1 -Maximum 1500
$Null = Start-Sleep -Milliseconds ($Delay)
}
}
}
While ($FileDetails.SegmentBytes -gt 0)
$Null = $FileDetails.Source.Close()
$Null = $FileDetails.Destination.Close()
$Null = $StopWatchTable.Secondary.Stop()
$FileDetails = Get-Item -Path $FileObjectProperties.Destination.FullName -Force
$FileAttributeList = $FileDetails.PSObject.Properties | Where-Object {($_.MemberType -iin @('NoteProperty', 'Property')) -and ($_.TypeNameOfValue -ieq 'System.DateTime') -and ($_.IsSettable -eq $True) -and ($_.Name -inotmatch '.*UTC.*')}
ForEach ($FileAttribute In $FileAttributeList)
{
#$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to update file attribute `"$($FileAttribute.Name)`" on file `"$($FileDetails.FullName)`" from `"$($FileDetails.$($FileAttribute.Name))`" to `"$($FileObjectProperties.Source.$($FileAttribute.Name))`". Please Wait..."
#Write-Verbose -Message ($LoggingDetails.LogMessage)
$FileDetails.$($FileAttribute.Name) = $FileObjectProperties.Source.$($FileAttribute.Name)
}
$FileObjectProperties.Destination = $FileDetails
$FileObjectProperties.Status = 'Successful'
}
Default
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - File `"$($FileObjectProperties.Destination.FullName)`" already exists. Skipping."
Write-Verbose -Message ($LoggingDetails.WarningMessage)
$FileObjectProperties.Status = 'Skipped'
}
}
}
Catch
{
$FileObjectProperties.Status = 'Error'
$ErrorHandlingDefinition.Invoke()
}
Finally
{
$FileObjectProperties.TimeToTransfer = $StopWatchTable.Secondary.Elapsed
$Null = $StopWatchTable.Secondary.Reset()
$FileObject = New-Object -TypeName 'PSObject' -Property ($FileObjectProperties)
$OutputObjectList.Add($FileObject)
}
}
$Null = $StopWatchTable.Primary.Stop()
$Null = $StopWatchTable.Primary.Reset()
$FileListStatusGroups = $OutputObjectList | Group-Object -Property 'Status' | Sort-Object -Property @('Name')
ForEach ($FileListStatusGroup In $FileListStatusGroups)
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($FileListStatusGroup.Count) of $($FileListDetails.Count) file(s) have a status of `"$($FileListStatusGroup.Name)`"."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
}
}
Default
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($FileListDetails.Count) file(s) to copy to `"$($Destination.FullName)`". No further action will be taken."
Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose
}
}
}
Default
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There were 0 paths specified to search. No further action will be taken."
Write-Warning -Message ($LoggingDetails.WarningMessage)
}
}
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
$Null = Start-Sleep -Seconds 3
}
}
End
{
Try
{
#Determine the date and time the function completed execution
$FunctionEndTime = (Get-Date)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#Log the total script execution time
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is completed."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
#Write the object to the powershell pipeline
$OutputObjectList = $OutputObjectList.ToArray()
Write-Output -InputObject ($OutputObjectList)
}
}
}
#endregion
+450
View File
@@ -0,0 +1,450 @@
## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
#region Function Get-InstalledSoftware
Function Get-InstalledSoftware
{
<#
.SYNOPSIS
Retrieves a list of software installed on the current device.
.DESCRIPTION
Supports filtering the resulting list of software by using regular expressions.
.PARAMETER FilterInclusionExpression
Includes software based on their display name.
.PARAMETER FilterInclusionExpression
Excludes software based on their display name.
.PARAMETER ContinueOnError
Continues processing even if an error has occured.
.EXAMPLE
Get-InstalledSoftware
.EXAMPLE
Get-InstalledSoftware -Verbose
.EXAMPLE
$GetInstalledSoftwareParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$GetInstalledSoftwareParameters.FilterInclusionExpression = "(.*)"
$GetInstalledSoftwareParameters.FilterExclusionExpression = "(^.{0,0}$)"
$GetInstalledSoftwareParameters.ContinueOnError = $False
$GetInstalledSoftwareParameters.Verbose = $True
$GetInstalledSoftwareResult = Get-InstalledSoftware @GetInstalledSoftwareParameters
Write-Output -InputObject ($GetInstalledSoftwareResult)
#>
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('FIE')]
[Regex]$FilterInclusionExpression,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias('FEE')]
[Regex]$FilterExclusionExpression,
[Parameter(Mandatory=$False)]
[Alias('COE')]
[Switch]$ContinueOnError
)
Begin
{
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)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$TextInfo = (Get-Culture).TextInfo
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.Add('LogMessage', $Null)
$LoggingDetails.Add('WarningMessage', $Null)
$LoggingDetails.Add('ErrorMessage', $Null)
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
[ScriptBlock]$ErrorHandlingDefinition = {
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('Message', $_.Exception.Message)
$ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
$ErrorMessageList.Add('Script', $_.InvocationInfo.ScriptName)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$LoggingDetails.ErrorMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - ERROR: $($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($LoggingDetails.ErrorMessage)
}
Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False))
{
{($_ -eq $True)}
{
Throw
}
}
}
#Determine the date and time we executed the function
$FunctionStartTime = (Get-Date)
[String]$FunctionName = $MyInvocation.MyCommand
[System.IO.FileInfo]$InvokingScriptPath = $MyInvocation.PSCommandPath
[System.IO.DirectoryInfo]$InvokingScriptDirectory = $InvokingScriptPath.Directory.FullName
[System.IO.FileInfo]$FunctionPath = "$($InvokingScriptDirectory.FullName)\Functions\$($FunctionName).ps1"
[System.IO.DirectoryInfo]$FunctionDirectory = "$($FunctionPath.Directory.FullName)"
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is beginning. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#Define Default Action Preferences
$ErrorActionPreference = 'Stop'
[String[]]$AvailableScriptParameters = (Get-Command -Name ($FunctionName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key):$($_.Value.GetType().Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#region Set Default Parameter Values
Switch ($True)
{
{([String]::IsNullOrEmpty($FilterInclusionExpression) -eq $True) -or ([String]::IsNullOrWhiteSpace($FilterInclusionExpression) -eq $True)}
{
[Regex]$FilterInclusionExpression = '(.*)'
}
{([String]::IsNullOrEmpty($FilterExclusionExpression) -eq $True) -or ([String]::IsNullOrWhiteSpace($FilterExclusionExpression) -eq $True)}
{
[Regex]$FilterExclusionExpression = '(^.{0,0}$)'
}
}
#endregion
#Create a table for the conversion of dates
$DateTimeProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$DateTimeProperties.FormatList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$DateTimeProperties.FormatList.AddRange(([System.Globalization.DateTimeFormatInfo]::CurrentInfo.GetAllDateTimePatterns()))
$DateTimeProperties.FormatList.AddRange(([System.Globalization.DateTimeFormatInfo]::InvariantInfo.GetAllDateTimePatterns()))
$DateTimeProperties.FormatList.Add('yyyyMM')
$DateTimeProperties.FormatList.Add('yyyyMMdd')
$DateTimeProperties.Culture = $Null
$DateTimeProperties.Styles = New-Object -TypeName 'System.Collections.Generic.List[System.Globalization.DateTimeStyles]'
$DateTimeProperties.Styles.Add([System.Globalization.DateTimeStyles]::AssumeLocal)
$DateTimeProperties.Styles.Add([System.Globalization.DateTimeStyles]::AllowWhiteSpaces)
#Create an object that will contain the functions output.
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
}
}
Process
{
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)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$TextInfo = (Get-Culture).TextInfo
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.Add('LogMessage', $Null)
$LoggingDetails.Add('WarningMessage', $Null)
$LoggingDetails.Add('ErrorMessage', $Null)
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
$RegularExpressionTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$RegularExpressionTable.Base64 = '^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})$' -As [Regex]
$RegularExpressionTable.GUID = '(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}' -As [Regex]
$RegexOptionList = New-Object -TypeName 'System.Collections.Generic.List[System.Text.RegularExpressions.RegexOptions[]]'
$RegexOptionList.Add([System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$RegexOptionList.Add([System.Text.RegularExpressions.RegexOptions]::Multiline)
[ScriptBlock]$ErrorHandlingDefinition = {
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('Message', $_.Exception.Message)
$ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
$ErrorMessageList.Add('Script', $_.InvocationInfo.ScriptName)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$LoggingDetails.ErrorMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - ERROR: $($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($LoggingDetails.ErrorMessage)
}
Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False))
{
{($_ -eq $True)}
{
Throw
}
}
}
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$OutputObjectValueList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$OutputObjectValueList.Add('DisplayName')
$OutputObjectValueList.Add('DisplayVersion')
$OutputObjectValueList.Add('UninstallString')
$OutputObjectValueList.Add('InstallLocation')
$OutputObjectValueList.Add('Publisher')
$OutputObjectValueList.Add('InstallDate')
$RegistryHiveList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$RegistryHiveProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$RegistryHiveProperties.Add('Type', [Microsoft.Win32.RegistryHive]::LocalMachine)
$RegistryHiveProperties.Add('KeyList', (New-Object -TypeName 'System.Collections.Generic.List[String]'))
$RegistryHiveProperties.KeyList.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall')
$RegistryHiveProperties.KeyList.Add('Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall')
$RegistryHiveObject = New-Object -TypeName 'PSObject' -Property ($RegistryHiveProperties)
$RegistryHiveList.Add($RegistryHiveObject)
$RegistryHiveProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$RegistryHiveProperties.Add('Type', [Microsoft.Win32.RegistryHive]::CurrentUser)
$RegistryHiveProperties.Add('KeyList', (New-Object -TypeName 'System.Collections.Generic.List[String]'))
$RegistryHiveProperties.KeyList.Add('Software\Microsoft\Windows\CurrentVersion\Uninstall')
$RegistryHiveObject = New-Object -TypeName 'PSObject' -Property ($RegistryHiveProperties)
$RegistryHiveList.Add($RegistryHiveObject)
For ($RegistryHiveListIndex = 0; $RegistryHiveListIndex -lt $RegistryHiveList.Count; $RegistryHiveListIndex++)
{
$RegistryHive = $RegistryHiveList[$RegistryHiveListIndex]
$RegistryHiveObject = [Microsoft.Win32.RegistryKey]::OpenBaseKey($RegistryHive.Type, [Microsoft.Win32.RegistryView]::Default)
For ($KeyListIndex = 0; $KeyListIndex -lt $RegistryHive.KeyList.Count; $KeyListIndex++)
{
$RegistryKey = $RegistryHive.KeyList[$KeyListIndex]
$RegistryKeyObject = $RegistryHiveObject.OpenSubKey($RegistryKey)
Switch ($Null -ine $RegistryKeyObject)
{
{($_ -eq $True)}
{
$SubKeyNameList = $RegistryKeyObject.GetSubKeyNames() | Sort-Object
For ($SubKeyNameListIndex = 0; $SubKeyNameListIndex -lt $SubKeyNameList.Count; $SubKeyNameListIndex++)
{
Try
{
$SubKeyName = $SubKeyNameList[$SubKeyNameListIndex]
$SubKeyObject = $RegistryKeyObject.OpenSubKey($SubKeyName)
$SubKeyObjectSegments = $SubKeyObject.Name.Split('\')
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.Path = $SubKeyObject.Name
$OutputObjectProperties.Hive = $SubKeyObjectSegments[0]
$OutputObjectProperties.Location = ($SubKeyObjectSegments[1..$($SubKeyObjectSegments.GetUpperBound(0))]) -Join '\'
ForEach ($OutputObjectValueName In $OutputObjectValueList)
{
$OutputObjectProperties.$($OutputObjectValueName) = $Null
}
$ValueNameList = $SubKeyObject.GetValueNames() | Sort-Object
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Now processing entry `"$($OutputObjectProperties.Path)`" [ValueCount: $($ValueNameList.Count)]. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Value Name List: $(($ValueNameList | Sort-Object) -Join '; ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
Switch ($ValueNameList.Count -gt 0)
{
{($_ -eq $True)}
{
For ($ValueNameListIndex = 0; $ValueNameListIndex -lt $ValueNameList.Count; $ValueNameListIndex++)
{
$ValueName = $ValueNameList[$ValueNameListIndex]
$ValueKind = $SubKeyObject.GetValueKind($ValueName)
Switch ($ValueKind)
{
{($_ -ieq 'PlaceHolder')}
{
}
Default
{
$Value = $SubKeyObject.GetValue($ValueName)
}
}
$OutputObjectProperties.$ValueName = $Value
}
$OutputObjectProperties.ProductCode = Try {[Regex]::Match($OutputObjectProperties.Location, $RegularExpressionTable.GUID.ToString(), $RegexOptionList.ToArray()).Value.Trim()} Catch {$Null}
Switch (($OutputObjectProperties.DisplayName -imatch $FilterInclusionExpression.ToString()) -and ($OutputObjectProperties.DisplayName -inotmatch $FilterExclusionExpression.ToString()))
{
{($_ -eq $True)}
{
ForEach ($OutputObjectProperty In ($OutputObjectProperties.Keys | Sort-Object))
{
$OutputObjectPropertyName = $OutputObjectProperty
$OutputObjectPropertyValue = $OutputObjectProperties.$($OutputObjectPropertyName)
Switch ($OutputObjectPropertyName)
{
{($_ -iin @('DisplayVersion'))}
{
$OutputObjectPropertyValue = Try {New-Object -TypeName 'System.Version' -ArgumentList ($OutputObjectPropertyValue)} Catch {$OutputObjectPropertyValue}
}
{($_ -iin @('InstallDate'))}
{
$DateTime = New-Object -TypeName 'DateTime'
$DateTimeProperties.Input = $OutputObjectPropertyValue
$DateTimeProperties.Successful = [DateTime]::TryParseExact($DateTimeProperties.Input, $DateTimeProperties.FormatList, $DateTimeProperties.Culture, $DateTimeProperties.Styles.ToArray(), [Ref]$DateTime)
$DateTimeProperties.DateTime = $DateTime
$DateTimeObject = New-Object -TypeName 'PSObject' -Property ($DateTimeProperties)
Switch ($DateTimeObject.Successful)
{
{($_ -eq $True)}
{
$OutputObjectPropertyValue = $DateTimeObject.DateTime
}
}
}
Default
{
Switch ($OutputObjectPropertyValue)
{
{($_ -imatch '(^\d+$)')}
{
$OutputObjectPropertyValue = $OutputObjectPropertyValue -As [Int32]
}
}
}
}
$OutputObjectProperties.Remove($OutputObjectPropertyName)
$OutputObjectProperties.$($OutputObjectPropertyName) = $OutputObjectPropertyValue
}
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
$OutputObjectList.Add($OutputObject)
}
}
}
}
Try {$Null = $SubKeyObject.Close()} Catch {}
}
Catch
{
}
Finally
{
}
}
}
}
Try {$Null = $RegistryKeyObject.Close()} Catch {}
}
Try {$Null = $RegistryHiveObject.Close()} Catch {}
}
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
}
}
End
{
Try
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Found $($OutputObjectList.Count) instances of software matching the specified regular expression(s)."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#Determine the date and time the function completed execution
$FunctionEndTime = (Get-Date)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($FunctionName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
#Log the total script execution time
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is completed."
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
#Write the object to the powershell pipeline
$OutputObjectList = $OutputObjectList.ToArray()
Write-Output -InputObject ($OutputObjectList)
}
}
}
#endregion
+41
View File
@@ -0,0 +1,41 @@
Function Get-MSIProductList
{
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
$ComObject = New-Object -ComObject 'WindowsInstaller.Installer'
$ComObjectType = $ComObject.GetType()
[String[]]$AttributeList = @('Language', 'ProductName', 'PackageCode', 'Transforms', 'AssignmentType', 'PackageName', 'InstalledProductName', 'VersionString', 'RegCompany', 'RegOwner', 'ProductID', 'ProductIcon', 'InstallLocation', 'InstallSource', 'InstallDate', 'Publisher', 'LocalPackage', 'HelpLink', 'HelpTelephone', 'URLInfoAbout', 'URLUpdateInfo') | Sort-Object
$ProductList = $ComObjectType.InvokeMember('Products', [System.Reflection.BindingFlags]::GetProperty, $Null, $ComObject, $Null)
ForEach ($Product In $ProductList)
{
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.Add('ProductCode', $Product)
For ($AttributeListIndex = 0; $AttributeListIndex -lt $AttributeList.Count; $AttributeListIndex++)
{
[String]$AttributeName = $AttributeList[$AttributeListIndex]
Switch ($OutputObjectProperties.Contains($AttributeName))
{
{($_ -eq $False)}
{
$OutputObjectProperties.Add($AttributeName, $Null)
}
}
Try {$OutputObjectProperties."$($AttributeName)" = $ComObjectType.InvokeMember('ProductInfo', [System.Reflection.BindingFlags]::GetProperty, $Null, $ComObject, @($Product, $AttributeName))} Catch {}
}
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
$Null = $OutputObjectList.Add($OutputObject)
}
Write-Output -InputObject ($OutputObjectList)
}
#Get-MSIProductList
+91
View File
@@ -0,0 +1,91 @@
Function Get-MSIPropertyList
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[ValidateScript({(Test-Path -Path $_)})]
[System.IO.FileInfo[]]$Path
)
Begin
{
$OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]'
}
Process
{
Try
{
ForEach ($Item In $Path)
{
$ComObject = New-Object -ComObject 'WindowsInstaller.Installer'
$MSIDatabase = $ComObject.GetType().InvokeMember('OpenDatabase', 'InvokeMethod', $Null, $ComObject, @($Item.FullName, 0))
[String]$Query = 'SELECT * FROM Property'
$View = $MSIDatabase.GetType().InvokeMember('OpenView', 'InvokeMethod', $Null, $MSIDatabase, $Query)
$View.GetType().InvokeMember('Execute', 'InvokeMethod', $Null, $View, $Null)
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.Add('Path', $Item)
While ($Record = $View.GetType().InvokeMember('Fetch', 'InvokeMethod', $Null, $View, $Null))
{
Switch ($Null -ine $Record)
{
{($_ -eq $True)}
{
[String]$MSIPropertyName = $Record.GetType().InvokeMember('StringData', 'GetProperty', $Null, $Record, 1)
[String]$MSIPropertyValue = $Record.GetType().InvokeMember('StringData', 'GetProperty', $Null, $Record, 2)
Switch ($OutputObjectProperties.Contains($MSIPropertyName))
{
{($_ -eq $False)}
{
$OutputObjectProperties.Add($MSIPropertyName, $MSIPropertyValue)
}
}
}
}
}
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
$OutputObjectList.Add($OutputObject)
$Null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($MSIDatabase)
$Null = [System.Runtime.InteropServices.Marshal]::ReleaseComObject($ComObject)
}
}
Catch
{
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('ErrorMessage', $_.Exception.Message)
$ErrorMessageList.Add('Command', $_.InvocationInfo.MyCommand.Name)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$ErrorMessage = "$($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($ErrorMessage) -Verbose
}
}
Finally
{
$OutputObjectList = $OutputObjectList.ToArray()
Write-Output -InputObject ($OutputObjectList)
}
}
End
{
}
}
+538
View File
@@ -0,0 +1,538 @@
## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
#region Function Get-UserSessionList
Function Get-UserSessionList
{
[CmdletBinding()]
Param
(
[ValidateNotNullOrEmpty()]
[String]$ComputerName = "$($Env:ComputerName)"
)
<#
.SYNOPSIS
Get session details for all local and RDP logged on users.
.DESCRIPTION
Get session details for all local and RDP logged on users using Win32 APIs. Get the following session details:
NTAccount, SID, UserName, DomainName, SessionId, SessionName, ConnectState, IsCurrentSession, IsConsoleSession, IsUserSession, IsActiveUserSession
IsRdpSession, IsLocalAdmin, LogonTime, IdleTime, DisconnectTime, ClientName, ClientProtocolType, ClientDirectory, ClientBuildNumber
.EXAMPLE
Get-LoggedOnUser
.NOTES
Description of ConnectState property:
Value Description
----- -----------
Active A user is logged on to the session.
ConnectQuery The session is in the process of connecting to a client.
Connected A client is connected to the session.
Disconnected The session is active, but the client has disconnected from it.
Down The session is down due to an error.
Idle The session is waiting for a client to connect.
Initializing The session is initializing.
Listening The session is listening for connections.
Reset The session is being reset.
Shadowing This session is shadowing another session.
Description of IsActiveUserSession property:
If a console user exists, then that will be the active user session.
If no console user exists but users are logged in, such as on terminal servers, then the first logged-in non-console user that is either 'Active' or 'Connected' is the active user.
Description of IsRdpSession property:
Gets a value indicating whether the user is associated with an RDP client session.
.LINK
http://psappdeploytoolkit.com
#>
$TypeDefinition =
@"
// Date Modified: 01-08-2016
// Version Number: 3.6.8
using System;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.DirectoryServices;
using System.Security.Principal;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace PSADT
{
public class QueryUser
{
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr WTSOpenServer(string pServerName);
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern void WTSCloseServer(IntPtr hServer);
[DllImport("wtsapi32.dll", CharSet = CharSet.Ansi, SetLastError = false)]
public static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WTS_INFO_CLASS wtsInfoClass, out IntPtr pBuffer, out int pBytesReturned);
[DllImport("wtsapi32.dll", CharSet = CharSet.Ansi, SetLastError = false)]
public static extern int WTSEnumerateSessions(IntPtr hServer, int Reserved, int Version, out IntPtr pSessionInfo, out int pCount);
[DllImport("wtsapi32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr pMemory);
[DllImport("winsta.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int WinStationQueryInformation(IntPtr hServer, int sessionId, int information, ref WINSTATIONINFORMATIONW pBuffer, int bufferLength, ref int returnedLength);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int GetCurrentProcessId();
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern bool ProcessIdToSessionId(int processId, ref int pSessionId);
public class TerminalSessionData
{
public int SessionId;
public string ConnectionState;
public string SessionName;
public bool IsUserSession;
public TerminalSessionData(int sessionId, string connState, string sessionName, bool isUserSession)
{
SessionId = sessionId;
ConnectionState = connState;
SessionName = sessionName;
IsUserSession = isUserSession;
}
}
public class TerminalSessionInfo
{
public string NTAccount;
public string SID;
public string UserName;
public string DomainName;
public int SessionId;
public string SessionName;
public string ConnectState;
public bool IsCurrentSession;
public bool IsConsoleSession;
public bool IsActiveUserSession;
public bool IsUserSession;
public bool IsRdpSession;
public bool IsLocalAdmin;
public DateTime? LogonTime;
public TimeSpan? IdleTime;
public DateTime? DisconnectTime;
public string ClientName;
public string ClientProtocolType;
public string ClientDirectory;
public int ClientBuildNumber;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public Int32 SessionId;
[MarshalAs(UnmanagedType.LPStr)]
public string SessionName;
public WTS_CONNECTSTATE_CLASS State;
}
[StructLayout(LayoutKind.Sequential)]
public struct WINSTATIONINFORMATIONW
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 70)]
private byte[] Reserved1;
public int SessionId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
private byte[] Reserved2;
public FILETIME ConnectTime;
public FILETIME DisconnectTime;
public FILETIME LastInputTime;
public FILETIME LoginTime;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1096)]
private byte[] Reserved3;
public FILETIME CurrentTime;
}
public enum WINSTATIONINFOCLASS
{
WinStationInformation = 8
}
public enum WTS_CONNECTSTATE_CLASS
{
Active,
Connected,
ConnectQuery,
Shadow,
Disconnected,
Idle,
Listen,
Reset,
Down,
Init
}
public enum WTS_INFO_CLASS
{
SessionId=4,
UserName,
SessionName,
DomainName,
ConnectState,
ClientBuildNumber,
ClientName,
ClientDirectory,
ClientProtocolType=16
}
private static IntPtr OpenServer(string Name)
{
IntPtr server = WTSOpenServer(Name);
return server;
}
private static void CloseServer(IntPtr ServerHandle)
{
WTSCloseServer(ServerHandle);
}
private static IList<T> PtrToStructureList<T>(IntPtr ppList, int count) where T : struct
{
List<T> result = new List<T>();
long pointer = ppList.ToInt64();
int sizeOf = Marshal.SizeOf(typeof(T));
for (int index = 0; index < count; index++)
{
T item = (T) Marshal.PtrToStructure(new IntPtr(pointer), typeof(T));
result.Add(item);
pointer += sizeOf;
}
return result;
}
public static DateTime? FileTimeToDateTime(FILETIME ft)
{
if (ft.dwHighDateTime == 0 && ft.dwLowDateTime == 0)
{
return null;
}
long hFT = (((long) ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
return DateTime.FromFileTime(hFT);
}
public static WINSTATIONINFORMATIONW GetWinStationInformation(IntPtr server, int sessionId)
{
int retLen = 0;
WINSTATIONINFORMATIONW wsInfo = new WINSTATIONINFORMATIONW();
WinStationQueryInformation(server, sessionId, (int) WINSTATIONINFOCLASS.WinStationInformation, ref wsInfo, Marshal.SizeOf(typeof(WINSTATIONINFORMATIONW)), ref retLen);
return wsInfo;
}
public static TerminalSessionData[] ListSessions(string ServerName)
{
IntPtr server = IntPtr.Zero;
if (ServerName == "localhost" || ServerName == String.Empty)
{
ServerName = Environment.MachineName;
}
List<TerminalSessionData> results = new List<TerminalSessionData>();
try
{
server = OpenServer(ServerName);
IntPtr ppSessionInfo = IntPtr.Zero;
int count;
bool _isUserSession = false;
IList<WTS_SESSION_INFO> sessionsInfo;
if (WTSEnumerateSessions(server, 0, 1, out ppSessionInfo, out count) == 0)
{
throw new Win32Exception();
}
try
{
sessionsInfo = PtrToStructureList<WTS_SESSION_INFO>(ppSessionInfo, count);
}
finally
{
WTSFreeMemory(ppSessionInfo);
}
foreach (WTS_SESSION_INFO sessionInfo in sessionsInfo)
{
if (sessionInfo.SessionName != "Services" && sessionInfo.SessionName != "RDP-Tcp")
{
_isUserSession = true;
}
results.Add(new TerminalSessionData(sessionInfo.SessionId, sessionInfo.State.ToString(), sessionInfo.SessionName, _isUserSession));
_isUserSession = false;
}
}
finally
{
CloseServer(server);
}
TerminalSessionData[] returnData = results.ToArray();
return returnData;
}
public static TerminalSessionInfo GetSessionInfo(string ServerName, int SessionId)
{
IntPtr server = IntPtr.Zero;
IntPtr buffer = IntPtr.Zero;
int bytesReturned;
TerminalSessionInfo data = new TerminalSessionInfo();
bool _IsCurrentSessionId = false;
bool _IsConsoleSession = false;
bool _IsUserSession = false;
int currentSessionID = 0;
string _NTAccount = String.Empty;
if (ServerName == "localhost" || ServerName == String.Empty)
{
ServerName = Environment.MachineName;
}
if (ProcessIdToSessionId(GetCurrentProcessId(), ref currentSessionID) == false)
{
currentSessionID = -1;
}
// Get all members of the local administrators group
bool _IsLocalAdminCheckSuccess = false;
List<string> localAdminGroupSidsList = new List<string>();
try
{
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + ServerName + ",Computer");
string localAdminGroupName = new SecurityIdentifier("S-1-5-32-544").Translate(typeof(NTAccount)).Value.Split('\\')[1];
DirectoryEntry admGroup = localMachine.Children.Find(localAdminGroupName, "group");
object members = admGroup.Invoke("members", null);
foreach (object groupMember in (IEnumerable)members)
{
DirectoryEntry member = new DirectoryEntry(groupMember);
if (member.Name != String.Empty)
{
localAdminGroupSidsList.Add((new NTAccount(member.Name)).Translate(typeof(SecurityIdentifier)).Value);
}
}
_IsLocalAdminCheckSuccess = true;
}
catch { }
try
{
server = OpenServer(ServerName);
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.ClientBuildNumber, out buffer, out bytesReturned) == false)
{
return data;
}
int lData = Marshal.ReadInt32(buffer);
data.ClientBuildNumber = lData;
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.ClientDirectory, out buffer, out bytesReturned) == false)
{
return data;
}
string strData = Marshal.PtrToStringAnsi(buffer);
data.ClientDirectory = strData;
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.ClientName, out buffer, out bytesReturned) == false)
{
return data;
}
strData = Marshal.PtrToStringAnsi(buffer);
data.ClientName = strData;
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.ClientProtocolType, out buffer, out bytesReturned) == false)
{
return data;
}
Int16 intData = Marshal.ReadInt16(buffer);
if (intData == 2)
{
strData = "RDP";
data.IsRdpSession = true;
}
else
{
strData = "";
data.IsRdpSession = false;
}
data.ClientProtocolType = strData;
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.ConnectState, out buffer, out bytesReturned) == false)
{
return data;
}
lData = Marshal.ReadInt32(buffer);
data.ConnectState = ((WTS_CONNECTSTATE_CLASS) lData).ToString();
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.SessionId, out buffer, out bytesReturned) == false)
{
return data;
}
lData = Marshal.ReadInt32(buffer);
data.SessionId = lData;
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.DomainName, out buffer, out bytesReturned) == false)
{
return data;
}
strData = Marshal.PtrToStringAnsi(buffer).ToUpper();
data.DomainName = strData;
if (strData != String.Empty)
{
_NTAccount = strData;
}
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.UserName, out buffer, out bytesReturned) == false)
{
return data;
}
strData = Marshal.PtrToStringAnsi(buffer);
data.UserName = strData;
if (strData != String.Empty)
{
data.NTAccount = _NTAccount + "\\" + strData;
string _Sid = (new NTAccount(_NTAccount + "\\" + strData)).Translate(typeof(SecurityIdentifier)).Value;
data.SID = _Sid;
if (_IsLocalAdminCheckSuccess == true)
{
foreach (string localAdminGroupSid in localAdminGroupSidsList)
{
if (localAdminGroupSid == _Sid)
{
data.IsLocalAdmin = true;
break;
}
else
{
data.IsLocalAdmin = false;
}
}
}
}
if (WTSQuerySessionInformation(server, SessionId, WTS_INFO_CLASS.SessionName, out buffer, out bytesReturned) == false)
{
return data;
}
strData = Marshal.PtrToStringAnsi(buffer);
data.SessionName = strData;
if (strData != "Services" && strData != "RDP-Tcp" && data.UserName != String.Empty)
{
_IsUserSession = true;
}
data.IsUserSession = _IsUserSession;
if (strData == "Console")
{
_IsConsoleSession = true;
}
data.IsConsoleSession = _IsConsoleSession;
WINSTATIONINFORMATIONW wsInfo = GetWinStationInformation(server, SessionId);
DateTime? _loginTime = FileTimeToDateTime(wsInfo.LoginTime);
DateTime? _lastInputTime = FileTimeToDateTime(wsInfo.LastInputTime);
DateTime? _disconnectTime = FileTimeToDateTime(wsInfo.DisconnectTime);
DateTime? _currentTime = FileTimeToDateTime(wsInfo.CurrentTime);
TimeSpan? _idleTime = (_currentTime != null && _lastInputTime != null) ? _currentTime.Value - _lastInputTime.Value : TimeSpan.Zero;
data.LogonTime = _loginTime;
data.IdleTime = _idleTime;
data.DisconnectTime = _disconnectTime;
if (currentSessionID == SessionId)
{
_IsCurrentSessionId = true;
}
data.IsCurrentSession = _IsCurrentSessionId;
}
finally
{
WTSFreeMemory(buffer);
buffer = IntPtr.Zero;
CloseServer(server);
}
return data;
}
public static TerminalSessionInfo[] GetUserSessionInfo(string ServerName)
{
if (ServerName == "localhost" || ServerName == String.Empty)
{
ServerName = Environment.MachineName;
}
// Find and get detailed information for all user sessions
// Also determine the active user session. If a console user exists, then that will be the active user session.
// If no console user exists but users are logged in, such as on terminal servers, then select the first logged-in non-console user that is either 'Active' or 'Connected' as the active user.
TerminalSessionData[] sessions = ListSessions(ServerName);
TerminalSessionInfo sessionInfo = new TerminalSessionInfo();
List<TerminalSessionInfo> userSessionsInfo = new List<TerminalSessionInfo>();
string firstActiveUserNTAccount = String.Empty;
bool IsActiveUserSessionSet = false;
foreach (TerminalSessionData session in sessions)
{
if (session.IsUserSession == true)
{
sessionInfo = GetSessionInfo(ServerName, session.SessionId);
if (sessionInfo.IsUserSession == true)
{
if ((firstActiveUserNTAccount == String.Empty) && (sessionInfo.ConnectState == "Active" || sessionInfo.ConnectState == "Connected"))
{
firstActiveUserNTAccount = sessionInfo.NTAccount;
}
if (sessionInfo.IsConsoleSession == true)
{
sessionInfo.IsActiveUserSession = true;
IsActiveUserSessionSet = true;
}
else
{
sessionInfo.IsActiveUserSession = false;
}
userSessionsInfo.Add(sessionInfo);
}
}
}
TerminalSessionInfo[] userSessions = userSessionsInfo.ToArray();
if (IsActiveUserSessionSet == false)
{
foreach (TerminalSessionInfo userSession in userSessions)
{
if (userSession.NTAccount == firstActiveUserNTAccount)
{
userSession.IsActiveUserSession = true;
break;
}
}
}
return userSessions;
}
}
}
"@
## Add the custom types required for the toolkit
If (-not ([Management.Automation.PSTypeName]'PSADT.UiAutomation').Type)
{
[string[]]$ReferencedAssemblies = 'System.Drawing', 'System.Windows.Forms', 'System.DirectoryServices'
Add-Type -TypeDefinition $TypeDefinition -ReferencedAssemblies $ReferencedAssemblies -IgnoreWarnings -ErrorAction 'Stop'
}
Try
{
Write-Output -InputObject ([PSADT.QueryUser]::GetUserSessionInfo("$($ComputerName)"))
}
Catch
{
Write-Warning -Message "Unable to communicate with `'$($ComputerName)`'"
}
}
#endregion
@@ -0,0 +1,565 @@
## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
#region Function Invoke-FileDownloadWithProgress
Function Invoke-FileDownloadWithProgress
{
<#
.SYNOPSIS
Downloads the specified URL.
.DESCRIPTION
The file will only be downloaded if the last modified date of the source URL is different from the last modified date of the file that has already been downloaded or if the file have not already been downloaded.
.PARAMETER URL
The URL where the file is located.
.PARAMETER Destination
The directory path where the URL will be downloaded to. If not specified, a default value will be used.
.EXAMPLE
Invoke-FileDownloadWithProgress -URL 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -Destination "$($Env:ProgramData)\Dell\DriverPackCatalog" -FileName "DriverPackCatalog.cab" -Verbose
.EXAMPLE
$DownloadDetails = Invoke-FileDownloadWithProgress -URL 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -Destination "$($Env:ProgramData)\Dell\DriverPackCatalog" -Verbose
Write-Output -InputObject ($DownloadDetails)
.EXAMPLE
$InvokeFileDownloadWithProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$InvokeFileDownloadWithProgressParameters.URL = 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -As [System.URI]
$InvokeFileDownloadWithProgressParameters.Destination = "$($Env:ProgramData)\Dell\DriverPackCatalog" -As [System.IO.DirectoryInfo]
$InvokeFileDownloadWithProgressParameters.FileName = "DriverPackCatalog.cab"
$InvokeFileDownloadWithProgressParameters.ContinueOnError = $False
$InvokeFileDownloadWithProgressParameters.Verbose = $True
$InvokeFileDownloadWithProgressResult = Invoke-FileDownloadWithProgress @InvokeFileDownloadWithProgressParameters
Write-Output -InputObject ($InvokeFileDownloadWithProgressResult)
.NOTES
NEL : {"report_to":"network-errors","max_age":3600}
Report-To : {"group":"network-errors","max_age":3600,"endpoints":[{"url":"https://www.dell.com/support/onlineapi/nellogger/log"}]}
Accept-Ranges : bytes
Content-Type : application/vnd.ms-cab-compressed
ETag : "8043933683ddd81:0"
Last-Modified : Tue, 11 Oct 2022 15:07:43 GMT
Server : Microsoft-IIS/10.0
X-Powered-By : ASP.NET
x-arr-set : arr4
Content-Length : 270867
Date : Thu, 20 Oct 2022 15:51:44 GMT
Connection : keep-alive
Akamai-Request-BC : [a=23.207.199.174,b=78861523,c=g,n=US_VA_STERLING,o=20940]
.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.net.webrequest?view=netframework-4.8
.LINK
https://learn.microsoft.com/en-us/dotnet/api/system.net.webclient.downloadfiletaskasync?view=netframework-4.8
#>
[CmdletBinding(ConfirmImpact = 'Low', SupportsShouldProcess = $True)]
Param
(
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^http(s)\:\/\/.*\/.*\.(.{3,4})$')]
[System.URI]$URL,
[Parameter(Mandatory=$False, ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[System.IO.DirectoryInfo]$Destination,
[Parameter(Mandatory=$False, ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[ValidatePattern('^.*\.(.*)$')]
[String]$FileName,
[Parameter(Mandatory=$False)]
[Switch]$ContinueOnError
)
Begin
{
Try
{
Switch ($True)
{
{([String]::IsNullOrEmpty($Destination) -eq $True) -or ([String]::IsNullOrWhiteSpace($Destination) -eq $True)}
{
[System.IO.DirectoryInfo]$Destination = "$($Env:Windir)\Temp"
}
{([String]::IsNullOrEmpty($FileName) -eq $True) -or ([String]::IsNullOrWhiteSpace($FileName) -eq $True)}
{
[String]$FileName = [System.IO.Path]::GetFileName($URL.OriginalString)
}
}
[System.IO.FileInfo]$DestinationPath = "$($Destination.FullName)\$($FileName)"
$DateTimeLogFormat = 'dddd, MMMM dd, yyyy @ hh:mm:ss tt' ###Monday, January 01, 2019 @ 10:15:34 AM###
[ScriptBlock]$GetCurrentDateTimeLogFormat = {(Get-Date).ToString($DateTimeLogFormat)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$TextInfo = (Get-Culture).TextInfo
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.Add('LogMessage', $Null)
$LoggingDetails.Add('WarningMessage', $Null)
$LoggingDetails.Add('ErrorMessage', $Null)
$CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]'
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters)
$CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters)
[ScriptBlock]$ErrorHandlingDefinition = {
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('Message', $_.Exception.Message)
$ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
$ErrorMessageList.Add('Script', $_.InvocationInfo.ScriptName)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$LoggingDetails.ErrorMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - ERROR: $($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($LoggingDetails.ErrorMessage)
}
Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False))
{
{($_ -eq $True)}
{
Throw
}
}
}
#Determine the date and time we executed the function
$FunctionStartTime = (Get-Date)
[String]$CmdletName = $MyInvocation.MyCommand.Name
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is beginning. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Define Default Action Preferences
$ErrorActionPreference = 'Stop'
[String[]]$AvailableScriptParameters = (Get-Command -Name ($CmdletName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key):$($_.Value.GetType().Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.DownloadRequired = $False
#region Function Convert-FileSize
Function Convert-FileSize
{
<#
.SYSNOPSIS
Converts a size in bytes to its upper most value.
.PARAMETER Size
The size in bytes to convert
.EXAMPLE
$ConvertFileSizeParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ConvertFileSizeParameters.Size = 4294964
$ConvertFileSizeParameters.DecimalPlaces = 2
$ConvertFileSizeResult = Convert-FileSize @ConvertFileSizeParameters
Write-Output -InputObject ($ConvertFileSizeResult)
.EXAMPLE
$ConvertFileSizeResult = Convert-FileSize -Size 4294964
Write-Output -InputObject ($ConvertFileSizeResult)
.NOTES
Size : 429496456565656
DecimalPlaces : 0
Divisor : 1099511627776
SizeUnit : TB
SizeUnitAlias : Terabytes
CalculatedSize : 391
CalculatedSizeStr : 391 TB
#>
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[Alias("Length")]
$Size,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[Alias("DP")]
[Int]$DecimalPlaces
)
Try
{
Switch ($True)
{
{([String]::IsNullOrEmpty($DecimalPlaces) -eq $True) -or ([String]::IsNullOrWhiteSpace($DecimalPlaces) -eq $True)}
{
[Int]$DecimalPlaces = 2
}
}
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.Size = $Size
$OutputObjectProperties.DecimalPlaces = $DecimalPlaces
Switch ($Size)
{
{($_ -lt 1MB)}
{
$OutputObjectProperties.Divisor = 1KB
$OutputObjectProperties.SizeUnit = 'KB'
$OutputObjectProperties.SizeUnitAlias = 'Kilobytes'
Break
}
{($_ -lt 1GB)}
{
$OutputObjectProperties.Divisor = 1MB
$OutputObjectProperties.SizeUnit = 'MB'
$OutputObjectProperties.SizeUnitAlias = 'Megabytes'
Break
}
{($_ -lt 1TB)}
{
$OutputObjectProperties.Divisor = 1GB
$OutputObjectProperties.SizeUnit = 'GB'
$OutputObjectProperties.SizeUnitAlias = 'Gigabytes'
Break
}
{($_ -ge 1TB)}
{
$OutputObjectProperties.Divisor = 1TB
$OutputObjectProperties.SizeUnit = 'TB'
$OutputObjectProperties.SizeUnitAlias = 'Terabytes'
Break
}
}
$OutputObjectProperties.CalculatedSize = [System.Math]::Round(($Size / $OutputObjectProperties.Divisor), $OutputObjectProperties.DecimalPlaces)
$OutputObjectProperties.CalculatedSizeStr = "$($OutputObjectProperties.CalculatedSize) $($OutputObjectProperties.SizeUnit)"
}
Catch
{
Write-Error -Exception $_
}
Finally
{
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
Write-Output -InputObject ($OutputObject)
}
}
#endregion
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
}
}
Process
{
Try
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to create a web request for `"$($URL.OriginalString)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$WebRequest = [System.Net.WebRequest]::Create($URL.OriginalString)
$WebRequestResponse = $WebRequest.GetResponse()
$WebRequestResponseHeaders = $WebRequestResponse.Headers
$WebRequestHeaderProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
ForEach ($WebRequestResponseHeader In $WebRequestResponseHeaders.AllKeys)
{
$WebRequestHeaderProperties."$($WebRequestResponseHeader)" = ($WebRequestResponseHeaders.GetValues($WebRequestResponseHeader))[0]
}
$WebRequestHeaders = New-Object -TypeName 'PSObject' -Property ($WebRequestHeaderProperties)
$WebRequestHeaders.'Last-Modified' = (Get-Date -Date $WebRequestHeaders.'Last-Modified').ToUniversalTime()
$ContentLengthInMB = [System.Math]::Round(($WebRequestHeaders.'Content-Length' / 1MB), 2)
[ScriptBlock]$ExecuteDownload = {
Try
{
$DownloadExecutionStopwatch = New-Object -TypeName 'System.Diagnostics.Stopwatch'
$WebClient = New-Object -TypeName 'System.Net.WebClient'
$WebClient.UseDefaultCredentials = $True
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to download URL `"$($URL.OriginalString)`" to `"$($DestinationPath.FullName)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Download Size: $($ContentLengthInMB) MB"
Write-Verbose -Message ($LoggingDetails.LogMessage)
If ([System.IO.Directory]::Exists($DestinationPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DestinationPath.Directory.FullName)}
$Downloader = $WebClient.DownloadFileTaskAsync($URL.OriginalString, $DestinationPath.FullName)
$Null = Register-ObjectEvent -InputObject ($WebClient) -EventName 'DownloadProgressChanged' -SourceIdentifier 'WebClient.DownloadProgressChanged'
$Null = Start-Sleep -Seconds 3
$Null = $DownloadExecutionStopwatch.Start()
Switch ($Downloader.IsFaulted)
{
{($_ -eq $True)}
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A download error has occured. Attempting to generate the error record. Please Wait..."
Write-Warning -Message ($LoggingDetails.WarningMessage)
Write-Error -Message ($Downloader.GetAwaiter().GetResult())
}
}
While ($Downloader.IsCompleted -eq $False)
{
Switch ($Downloader.IsFaulted)
{
{($_ -eq $True)}
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A download error has occured. Attempting to generate the error record. Please Wait..."
Write-Warning -Message ($LoggingDetails.WarningMessage)
Write-Error -Message ($Downloader.GetAwaiter().GetResult())
Break
}
}
$EventData = Get-Event -SourceIdentifier 'WebClient.DownloadProgressChanged' | Select-Object -ExpandProperty 'SourceEventArgs' -Last 1
$EventDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$EventDetails.Received = Convert-FileSize -Size ($EventData.BytesReceived) -DecimalPlaces 2
$EventDetails.TotalToReceive = Convert-FileSize -Size ($EventData.TotalBytesToReceive) -DecimalPlaces 2
$EventDetails.ProgressPercentage = $EventData.ProgressPercentage
Switch ($DownloadExecutionStopwatch.Elapsed.Seconds -gt 0)
{
{($_ -eq $True)}
{
[Single]$TransferRate = [System.Math]::Round(($EventDetails.TotalToReceive.Size / $DownloadExecutionStopwatch.Elapsed.Seconds / 1MB), 2)
}
Default
{
[Single]$TransferRate = 0.00
}
}
$WriteProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$WriteProgressParameters.Activity = "Downloading `"$($DestinationPath.Name)`""
$WriteProgressParameters.Status = "Completion Percentage: $($EventDetails.ProgressPercentage)%"
$WriteProgressParameters.PercentComplete = $EventDetails.ProgressPercentage
$WriteProgressParameters.CurrentOperation = "Downloaded $($EventDetails.Received.CalculatedSizeStr) of $($EventDetails.TotalToReceive.CalculatedSizeStr) @ $($TransferRate) Mbps"
Write-Progress @WriteProgressParameters
}
}
Catch
{
Write-Error -Exception $_
$Null = Unregister-Event -SourceIdentifier 'WebClient.DownloadProgressChanged'
$Null = $DownloadExecutionStopwatch.Stop()
$Null = $DownloadExecutionStopwatch.Reset()
Break
}
Finally
{
$WriteProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$WriteProgressParameters.Activity = "Downloading `"$($DestinationPath.Name)`""
$WriteProgressParameters.Completed = $True
Write-Progress @WriteProgressParameters
$Null = Unregister-Event -SourceIdentifier 'WebClient.DownloadProgressChanged'
Switch (($Downloader.IsCompleted -eq $False) -or ($Downloader.IsFaulted -eq $True))
{
{($_ -eq $True)}
{
$Null = $Downloader.CancelAsync()
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Download failed. Attempting to remove incomplete file `"$($DestinationPath.FullName)`". Please Wait..."
Write-Warning -Message ($LoggingDetails.WarningMessage)
$Null = [System.IO.File]::Delete($DestinationPath.FullName)
}
}
$Null = $WebClient.Dispose()
$Null = $DownloadExecutionStopwatch.Stop()
}
$DownloadExecutionTimespan = $DownloadExecutionStopwatch.Elapsed
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Download completed in $($DownloadExecutionTimespan.Hours.ToString()) hour(s), $($DownloadExecutionTimespan.Minutes.ToString()) minute(s), $($DownloadExecutionTimespan.Seconds.ToString()) second(s), and $($DownloadExecutionTimespan.Milliseconds.ToString()) millisecond(s)."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$Null = $DownloadExecutionStopwatch.Reset()
[Int]$SecondsToWait = 3
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Pausing script execution for $($SecondsToWait) second(s). Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$Null = Start-Sleep -Seconds ($SecondsToWait)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to update the last modified date of `"$($DestinationPath.FullName)`" to match the last modified date of `"$($URL.OriginalString)`". Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date UTC (Local File): $($DestinationPath.LastWriteTimeUTC.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date UTC (URL Header): $($WebRequestHeaders.'Last-Modified'.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$DestinationPathDetails = Get-Item -Path $DestinationPath.FullName -Force
$Null = $DestinationPathDetails.LastWriteTimeUTC = $WebRequestHeaders.'Last-Modified'
}
Switch ([System.IO.File]::Exists($DestinationPath.FullName))
{
{($_ -eq $True)}
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Destination path `"$($DestinationPath.FullName)`" already exists."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to check the last modified date of `"$($DestinationPath.FullName)`" to see if a download is necessary."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date UTC (Local File): $($DestinationPath.LastWriteTimeUTC.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date UTC (URL Header): $($WebRequestHeaders.'Last-Modified'.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
Switch (($DestinationPath.LastWriteTimeUTC -ine $WebRequestHeaders.'Last-Modified'))
{
{($_ -eq $True)}
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A redownload of `"$($URL.OriginalString)`" is necessary."
Write-Warning -Message ($LoggingDetails.WarningMessage)
$OutputObjectProperties.DownloadRequired = $True
$ExecuteDownload.InvokeReturnAsIs()
}
Default
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A redownload of `"$($URL.OriginalString)`" is not necessary."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
}
}
Default
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Destination path `"$($DestinationPath.FullName)`" does not exist."
Write-Warning -Message ($LoggingDetails.WarningMessage)
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A download of `"$($URL.OriginalString)`" is necessary."
Write-Warning -Message ($LoggingDetails.WarningMessage)
$OutputObjectProperties.DownloadRequired = $True
$ExecuteDownload.InvokeReturnAsIs()
}
}
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
Try {$Null = $WebRequestResponse.Dispose()} Catch {}
}
}
End
{
Try
{
#Determine the date and time the function completed execution
$FunctionEndTime = (Get-Date)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Log the total script execution time
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is completed."
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
Catch
{
$ErrorHandlingDefinition.Invoke()
}
Finally
{
$DestinationPathDetails = Get-Item -Path $DestinationPath.FullName -Force
$OutputObjectProperties.DownloadPath = $DestinationPathDetails
$OutputObjectProperties.URL = $URL
$OutputObjectProperties.URLHeaders = $WebRequestHeaders
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
Write-Output -InputObject ($OutputObject)
}
}
}
#endregion
+443
View File
@@ -0,0 +1,443 @@
#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 ParseOutput
Enables the parsing of the command output into objects.
.PARAMETER ParsingExpression
A valid regular expression that will allow for the output to be parsed into objects.
.PARAMETER LogOutput
Your parameter description
.EXAMPLE
Start-ProcessWithOutput -FilePath 'cmd.exe' -ArgumentList '/c ipconfig /all' -AcceptableExitCodeList @('*') -CreateNoWindow -WindowStyle 'Hidden' -LogOutput -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.CreateNoWindow = $True
$StartProcessWithOutputParameters.ParseOutput = $True
$StartProcessWithOutputParameters.ParsingExpression = "(?:\s+)(?<PropertyName>.+)(?:\s+\:\s+)(?<PropertyValue>.+)"
$StartProcessWithOutputParameters.LogOutput = $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()]
Param
(
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[String]$FilePath,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[String]$WorkingDirectory,
[Parameter(Mandatory=$False)]
[AllowEmptyCollection()]
[AllowNull()]
[String[]]$ArgumentList,
[Parameter(Mandatory=$False)]
[AllowEmptyCollection()]
[AllowNull()]
[String[]]$AcceptableExitCodeList,
[Parameter(Mandatory=$False)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Normal', 'Hidden', 'Minimized', 'Maximized')]
[String]$WindowStyle,
[Parameter(Mandatory=$False)]
[Switch]$CreateNoWindow,
[Parameter(Mandatory=$False, ParameterSetName = 'ParseOutput')]
[Switch]$ParseOutput,
[Parameter(Mandatory=$False, ParameterSetName = 'ParseOutput')]
[ValidateNotNullOrEmpty()]
[Alias('StandardOutputParsingExpression')]
[Regex]$ParsingExpression,
[Parameter(Mandatory=$False)]
[Switch]$LogOutput
)
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)}
$DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347###
[ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)}
$DateFileFormat = 'yyyyMMdd' ###20190403###
[ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)}
$DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354###
[ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)}
$LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$LoggingDetails.LogMessage = $Null
$LoggingDetails.WarningMessage = $Null
$LoggingDetails.ErrorMessage = $Null
#Determine the date and time we executed the function
$FunctionStartTime = (Get-Date)
[String]$CmdletName = $MyInvocation.MyCommand.Name
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is beginning. Please Wait..."
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Define Default Action Preferences
$ErrorActionPreference = 'Stop'
[String[]]$AvailableScriptParameters = (Get-Command -Name ($CmdletName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Function Parameter(s) = $($AvailableScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
[String[]]$SuppliedScriptParameters = $PSBoundParameters.GetEnumerator() | ForEach-Object {"-$($_.Key):$($_.Value.GetType().Name)"}
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supplied Function Parameter(s) = $($SuppliedScriptParameters -Join ', ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Set default parameter values (If necessary)
Switch ($True)
{
{([String]::IsNullOrEmpty($WindowStyle) -eq $True) -or ([String]::IsNullOrWhiteSpace($WindowStyle) -eq $True)}
{
[String]$WindowStyle = 'Hidden'
}
{([String]::IsNullOrEmpty($ParsingExpression) -eq $True) -or ([String]::IsNullOrWhiteSpace($ParsingExpression) -eq $True)}
{
[Regex]$ParsingExpression = '(?:\s+)(?<PropertyName>.+)(?:\s+\:\s+)(?<PropertyValue>.+)'
}
}
$OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$OutputObjectProperties.ExitCode = $Null
$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
Switch ($True)
{
{([String]::IsNullOrEmpty($WorkingDirectory) -eq $False) -and ([String]::IsNullOrWhiteSpace($WorkingDirectory) -eq $False)}
{
$Process.StartInfo.WorkingDirectory = $WorkingDirectory
}
}
Switch ($CreateNoWindow.IsPresent)
{
{($_ -eq $True)}
{
$Process.StartInfo.CreateNoWindow = ($CreateNoWindow.IsPresent)
}
Default
{
$Process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::$($WindowStyle)
}
}
Switch (($Null -ieq $AcceptableExitCodeList) -or ($AcceptableExitCodeList.Count -eq 0))
{
{($_ -eq $True)}
{
$AcceptableExitCodeList = @()
$AcceptableExitCodeList += '0'
$AcceptableExitCodeList += '3010'
}
}
Switch (($Null -ine $ArgumentList) -and ($ArgumentList.Count -gt 0))
{
{($_ -eq $True)}
{
$Process.StartInfo.Arguments = $ArgumentList -Join ' '
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to execute the following command: `"$($Process.StartInfo.FileName)`" $($Process.StartInfo.Arguments)"
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
Default
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to execute the following command: `"$($Process.StartInfo.FileName)`""
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
}
$Null = $Process.Start()
$OutputObjectProperties.StandardOutput = $Process.StandardOutput.ReadToEnd()
$OutputObjectProperties.StandardError = $Process.StandardError.ReadToEnd()
$Null = $Process.WaitForExit()
$OutputObjectProperties.ExitCode = $Process.ExitCode
$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)]")}
$StartProcessExecutionTimespan = New-TimeSpan -Start ($Process.StartTime) -End ($Process.ExitTime)
$OutputObjectProperties.ProcessObject = $Process
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The command execution took $($StartProcessExecutionTimespan.Hours.ToString()) hour(s), $($StartProcessExecutionTimespan.Minutes.ToString()) minute(s), $($StartProcessExecutionTimespan.Seconds.ToString()) second(s), and $($StartProcessExecutionTimespan.Milliseconds.ToString()) millisecond(s)."
Write-Verbose -Message ($LoggingDetails.LogMessage)
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)}
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The command execution was successful. $($ExitCodeMessageList -Join ' ')"
Write-Verbose -Message ($LoggingDetails.LogMessage)
}
{($_ -eq $False)}
{
$LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The command execution was unsuccessful. $($ExitCodeMessageList -Join ' ')"
Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose
$ErrorMessage = "$($LoggingDetails.WarningMessage)"
$Exception = [System.Exception]::New($ErrorMessage)
$ErrorRecord = [System.Management.Automation.ErrorRecord]::New($Exception, [System.Management.Automation.ErrorCategory]::InvalidResult.ToString(), [System.Management.Automation.ErrorCategory]::InvalidResult, $Process)
$PSCmdlet.ThrowTerminatingError($ErrorRecord)
}
}
Switch (($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)}
{
[String]$CommandContents = $OutputObjectProperties.StandardOutput
Switch (($ParseOutput.IsPresent -eq $True) -and ([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([System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
$RegexOptions.Add([System.Text.RegularExpressions.RegexOptions]::Multiline)
[System.Text.RegularExpressions.Regex]$RegularExpression = [System.Text.RegularExpressions.Regex]::New($ParsingExpression, $RegexOptions.ToArray())
[String[]]$RegularExpressionGroups = $RegularExpression.GetGroupNames() | Where-Object {($_ -notin @('0'))}
[System.Text.RegularExpressions.MatchCollection]$RegularExpressionMatches = $RegularExpression.Matches($CommandContents)
$StandardOutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
For ($RegularExpressionMatchIndex = 0; $RegularExpressionMatchIndex -lt $RegularExpressionMatches.Count; $RegularExpressionMatchIndex++)
{
[System.Text.RegularExpressions.Match]$RegularExpressionMatch = $RegularExpressionMatches[$RegularExpressionMatchIndex]
For ($RegularExpressionGroupIndex = 0; $RegularExpressionGroupIndex -lt $RegularExpressionGroups.Count; $RegularExpressionGroupIndex++)
{
[String]$RegularExpressionGroup = $RegularExpressionGroups[$RegularExpressionGroupIndex]
Switch ($RegularExpressionGroup)
{
{($_ -imatch '(^PropertyName$)')}
{
$PropertyDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$PropertyDetails.Add('Name', $Null)
$PropertyDetails.Add('Value', $Null)
$PropertyDetails.Name = ($RegularExpressionMatch.Groups[$($RegularExpressionGroup)].Value) -ireplace '(\s+)|(\-)|(_)', ''
}
{($_ -imatch '(^PropertyValue$)')}
{
$PropertyDetails.Value = $RegularExpressionMatch.Groups[$($RegularExpressionGroup)].Value
Switch ($True)
{
{($PropertyDetails.Value -imatch '\+(\-){1,}\+')}
{
$PropertyDetails.Value = $Null
}
{($PropertyDetails.Value -imatch '(.+\,\s+.+){1,}')}
{
#$PropertyDetails.Value = $PropertyDetails.Value.Split(',').Trim()
}
{($PropertyDetails.Value -imatch '.+\(.+\).+')}
{
#$PropertyDetails.Value = ($PropertyDetails.Value.Split('()', [System.StringSplitOptions]::RemoveEmptyEntries) -ireplace 'bytes', '')[1]
}
}
Switch ($Null -ine $PropertyDetails.Value)
{
{($_ -eq $True)}
{
$PropertyDetails.Value = $PropertyDetails.Value.Trim()
}
}
}
}
}
Switch ($StandardOutputObjectProperties.Contains($PropertyDetails.Name))
{
{($_ -eq $False)}
{
$Null = $StandardOutputObjectProperties.Add($PropertyDetails.Name, $PropertyDetails.Value)
}
}
}
$OutputObjectProperties.StandardOutputObject = New-Object -TypeName 'PSObject' -Property ($StandardOutputObjectProperties)
}
}
}
}
}
Catch
{
$ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary'
$ErrorMessageList.Add('Message', $_.Exception.Message)
$ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID)
$ErrorMessageList.Add('Script', $_.InvocationInfo.ScriptName)
$ErrorMessageList.Add('LineNumber', $_.InvocationInfo.ScriptLineNumber)
$ErrorMessageList.Add('LinePosition', $_.InvocationInfo.OffsetInLine)
$ErrorMessageList.Add('Code', $_.InvocationInfo.Line.Trim())
ForEach ($ErrorMessage In $ErrorMessageList.GetEnumerator())
{
$LoggingDetails.ErrorMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - ERROR: $($ErrorMessage.Key): $($ErrorMessage.Value)"
Write-Warning -Message ($LoggingDetails.ErrorMessage) -Verbose
}
Throw "$($_.Exception.Message)"
}
Finally
{
#Dispose of the process object
Try {$Null = $Process.Dispose()} Catch {}
#Determine the date and time the function completed execution
$FunctionEndTime = (Get-Date)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))"
Write-Verbose -Message ($LoggingDetails.LogMessage)
#Log the total script execution time
$FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function execution took $($FunctionExecutionTimespan.Hours.ToString()) hour(s), $($FunctionExecutionTimespan.Minutes.ToString()) minute(s), $($FunctionExecutionTimespan.Seconds.ToString()) second(s), and $($FunctionExecutionTimespan.Milliseconds.ToString()) millisecond(s)."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is completed."
Write-Verbose -Message ($LoggingDetails.LogMessage)
$OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties)
Switch (($LogOutput.IsPresent -eq $True) -or ($LogOutput -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)}
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($Property.Name): $($Property.Value)"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
}
Default
{
$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($Property.Name): N/A"
Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose
}
}
}
}
}
}
}
Write-Output -InputObject ($OutputObject)
}
}
#endregion
<#
$ProcessOutput = Start-ProcessWithOutput -FilePath 'dsregcmd.exe' -ArgumentList '/status' -CreateNoWindow -Verbose
$ProcessOutput.StandardOutputObject | ConvertTo-JSON -Depth 10 -OutVariable 'AzureADDetails'
$ProcessOutput
#>
-1
View File
@@ -1 +0,0 @@
###
@@ -1 +0,0 @@
###
+848 -220
View File
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
'Set object values
Set oArguments = WScript.Arguments.Named
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oShell = CreateObject("WScript.Shell")
Set oCMD = oFSO.GetFile(oShell.ExpandEnvironmentStrings("%COMSPEC%"))
Set oPowershell = oFSO.GetFile(oShell.ExpandEnvironmentStrings("%WINDIR%") & "\System32\WindowsPowershell\v1.0\powershell.exe")
'Define ASCII Characters
chrSpace = Chr(32)
chrSingleQuote = Chr(39)
chrDoubleQuote = Chr(34)
'Define Variable(s)
ScriptInterpreter = CreateObject("Scripting.FileSystemObject").GetFileName(WScript.FullName)
If (oArguments.Exists("Debug") = True) Then DebugMode = True End If
'Dynamically convert named VBScript arguments to their eqivalent powershell format
oArgumentCount = oArguments.Count
If (oArgumentCount > 0) Then
StringBuilder = ""
oArgumentCounter = 1
Set ArgumentNameExpression = New RegExp
With ArgumentNameExpression
.Pattern = "^Debug$"
.IgnoreCase = True
.Global = False
.Multiline = False
End With
Set ArgumentValueExpression = New RegExp
With ArgumentValueExpression
.Pattern = "^.*True|.*False$"
.IgnoreCase = True
.Global = False
.Multiline = False
End With
For Each oArgument In oArguments
ArgumentName = Trim(oArgument)
ArgumentValue = oArguments.Item(oArgument)
If (Not(ArgumentNameExpression.Test(ArgumentName))) Then
Select Case ((Len(ArgumentValue) > 0) And (Not(ArgumentValueExpression.Test(ArgumentValue))))
Case True
If (InStr(ArgumentValue, ",") > 0) Then
ArgumentValueParts = Split(ArgumentValue, ",")
ArgumentValuePartsCount = UBound(ArgumentValueParts)
ArgumentValuePartStringBuilder = ""
ArgumentValuePartCounter = 0
For Each ArgumentValuePart In ArgumentValueParts
ArgumentValuePartFormat = chrDoubleQuote & ArgumentValuePart & chrDoubleQuote
ArgumentValuePartStringBuilder = ArgumentValuePartStringBuilder & ArgumentValuePartFormat
If ((ArgumentValuePartsCount > 0) And (ArgumentValuePartCounter < ArgumentValuePartsCount)) Then
ArgumentValuePartStringBuilder = ArgumentValuePartStringBuilder & ","
ArgumentValuePartCounter = ArgumentValuePartCounter + 1
End If
Next
ParameterFormat = "-" & ArgumentName & chrSpace & ArgumentValuePartStringBuilder
ElseIf (IsNumeric(ArgumentValue) = True) Then
ParameterFormat = "-" & ArgumentName & chrSpace & ArgumentValue
Else
ParameterFormat = "-" & ArgumentName & chrSpace & chrDoubleQuote & ArgumentValue & chrDoubleQuote
End If
Case False
ParameterFormat = "-" & ArgumentName
End Select
If (Len(ParameterFormat) > 0) Then
If ((oArgumentCount > 1) And (oArgumentCounter < oArgumentCount)) Then
StringBuilder = StringBuilder & chrSpace
End If
StringBuilder = StringBuilder & ParameterFormat
ArgumentCounter = oArgumentCounter + 1
ParameterFormat = Null
End If
End If
Next
StringBuilderResult = Trim(StringBuilder)
If (Len(StringBuilderResult) > 0) Then
PowershellScriptParameters = StringBuilderResult & ";" & chrSpace & "[System.Environment]::Exit(($LASTEXITCODE -Bor [Int](-Not $? -And -Not $LASTEXITCODE)))"
Else
If (DebugMode = True) Then
WScript.Echo("Debug mode is enabled. The specified command will NOT be executed.")
End If
PowershellScriptParameters = ";" & chrSpace & "[System.Environment]::Exit(($LASTEXITCODE -Bor [Int](-Not $? -And -Not $LASTEXITCODE)))"
End If
Set ArgumentNameExpression = Nothing
Set ArgumentValueExpression = Nothing
Else
If (DebugMode = True) Then
WScript.Echo(oArgumentCount & chrSpace & "arguments were specified.")
End If
End If
'Define Additional Variable(s)
Set ScriptPath = oFSO.GetFile(WScript.ScriptFullName)
ScriptDirectory = ScriptPath.ParentFolder
System32Directory = oShell.ExpandEnvironmentStrings("%WINDIR%") & "\System32"
CommandPromptExecutionParameters = oCMD.Name & chrSpace & "/c"
PowershellExecutionParameters = chrDoubleQuote & oPowershell.Path & chrDoubleQuote & chrSpace & "-ExecutionPolicy Bypass -NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command"
PowershellScriptPath = ScriptDirectory & "\" & oFSO.GetBaseName(ScriptPath) & ".ps1"
'Execute Powershell Script
If (Len(PowershellScriptParameters) > 0) Then
Command = PowershellExecutionParameters & chrSpace & chrDoubleQuote & "&" & chrSpace & chrSingleQuote & PowershellScriptPath & chrSingleQuote & chrSpace & PowershellScriptParameters & chrDoubleQuote
Else
Command = PowershellExecutionParameters & chrSpace & chrDoubleQuote & "&" & chrSpace & chrSingleQuote & PowershellScriptPath & chrSingleQuote & chrDoubleQuote
End If
If ((ScriptInterpreter = "cscript.exe") Or (DebugMode = True)) Then
WScript.Echo("Command:" & chrSpace & Command)
End If
If (DebugMode = False) Then
RunCommand = oShell.Run(Command, 0, True)
End If
If ((ScriptInterpreter = "cscript.exe") Or (DebugMode = True)) And (IsEmpty(RunCommand) = False) Then
WScript.Echo("Exit Code:" & chrSpace & RunCommand)
End If
If (DebugMode = False) Then
WScript.Quit(RunCommand)
End If