commit 3fcf06990c007359bdaabfc886d0ff15ee18fb38 Author: freedbygrace Date: Thu Dec 1 11:40:57 2022 -0500 Initial Upload diff --git a/Content/DBQueries/GetProductIDList.sql b/Content/DBQueries/GetProductIDList.sql new file mode 100644 index 0000000..f28e669 --- /dev/null +++ b/Content/DBQueries/GetProductIDList.sql @@ -0,0 +1,23 @@ +Declare @OperatingSystemFilter VARCHAR(Max) = '%NT%Workstation%'; +Declare @IsVirtualMachine INT = 0; +Declare @Manufacturer VARCHAR(Max) = '%'; +Declare @NullReplacementValue VARCHAR(Max) = 'N/A'; + +Select Distinct + dbo.v_GS_MS_SYSTEMINFORMATION.BaseBoardProduct0 As 'BaseboardProduct', + dbo.v_GS_MS_SYSTEMINFORMATION.SystemFamily0 As 'SystemFamily', + dbo.v_GS_MS_SYSTEMINFORMATION.SystemManufacturer0 As 'SystemManufacturer', + dbo.v_GS_MS_SYSTEMINFORMATION.SystemProductName0 As 'SystemProductName', + dbo.v_GS_MS_SYSTEMINFORMATION.SystemSKU0 As 'SystemSKU', + dbo.v_GS_MS_SYSTEMINFORMATION.SystemVersion0 As 'SystemVersion' +From + dbo.v_R_System + Inner Join dbo.v_GS_MS_SYSTEMINFORMATION On (dbo.v_R_System.ResourceID = dbo.v_GS_MS_SYSTEMINFORMATION.ResourceID) +Where + (dbo.v_R_System.Operating_System_Name_and0 Like @OperatingSystemFilter) + And + (dbo.v_R_System.Is_Virtual_Machine0 = @IsVirtualMachine) + And + ((dbo.v_GS_MS_SYSTEMINFORMATION.SystemManufacturer0 Like @Manufacturer)) +Order By + dbo.v_GS_MS_SYSTEMINFORMATION.SystemManufacturer0 \ No newline at end of file diff --git a/Content/Templates/SettingsTemplate.xml b/Content/Templates/SettingsTemplate.xml new file mode 100644 index 0000000..8ea193c --- /dev/null +++ b/Content/Templates/SettingsTemplate.xml @@ -0,0 +1,94 @@ + + + + + + + + + + DSW\amote + NBAMOTE + 2022-11-10T15:01 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Functions/Convert-FileSize.ps1 b/Functions/Convert-FileSize.ps1 new file mode 100644 index 0000000..35c3782 --- /dev/null +++ b/Functions/Convert-FileSize.ps1 @@ -0,0 +1,118 @@ +## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx + +#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 \ No newline at end of file diff --git a/Functions/Copy-ItemWithProgress.ps1 b/Functions/Copy-ItemWithProgress.ps1 new file mode 100644 index 0000000..f605ff2 --- /dev/null +++ b/Functions/Copy-ItemWithProgress.ps1 @@ -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) + + #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) + + [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 $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + #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) + + $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) + + $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-Warning -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) + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($FileListDetails.Count) file(s) to copy to `"$($Destination.FullName)`". No further action will be taken." + Write-Warning -Message ($LoggingDetails.WarningMessage) + } + } + } + + 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) + + #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 `'$($FunctionName)`' is completed." + Write-Verbose -Message ($LoggingDetails.LogMessage) + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + #Write the object to the powershell pipeline + $OutputObjectList = $OutputObjectList.ToArray() + + Write-Output -InputObject ($OutputObjectList) + } + } + } +#endregion \ No newline at end of file diff --git a/Functions/Get-WindowsReleaseHistory.ps1 b/Functions/Get-WindowsReleaseHistory.ps1 new file mode 100644 index 0000000..3bd99d6 --- /dev/null +++ b/Functions/Get-WindowsReleaseHistory.ps1 @@ -0,0 +1,300 @@ +## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx + +#region Function Get-WindowsReleaseHistory +Function Get-WindowsReleaseHistory + { + <# + .SYNOPSIS + Retrieves the Windows release history by scraping the release information from the internet + + .DESCRIPTION + Slightly more detailed description of what your function does + + .EXAMPLE + Get-WindowsReleaseHistory -Verbose + + .EXAMPLE + Get-WindowsReleaseHistory -Force -Verbose + + .NOTES + a global variable named 'WindowsReleaseHistory' will be created that will store the release details. To keep web request traffic to a minimum, repeat executions of this function will return cached data unless the '-Force' parameter is specified. + + $Global:WindowsReleaseHistory + + .LINK + https://learn.microsoft.com/en-us/windows/release-health/release-information + + .LINK + https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information + #> + + [CmdletBinding()] + Param + ( + [Parameter(Mandatory=$False)] + [Switch]$Force, + + [Parameter(Mandatory=$False)] + [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]$FunctionPath = $PSCommandPath + [System.IO.DirectoryInfo]$FunctionDirectory = "$($FunctionPath.Directory.FullName)" + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($FunctionName)`' is beginning. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + #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) + + [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 $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + #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('HtmlAgilityPack.dll') + + 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) -Verbose + + $Null = [System.Reflection.Assembly]::Load($LibraryBytes) + } + } + } + } + } + } + } + #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 -ieq $Global:WindowsReleaseHistory) -or ($Force.IsPresent -eq $True)) + { + {($_ -eq $True)} + { + $URLList = New-Object -TypeName 'System.Collections.Generic.List[System.URI]' + $URLList.Add('https://learn.microsoft.com/en-us/windows/release-health/release-information') + $URLList.Add('https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information') + + For ($URLListIndex = 0; $URLListIndex -lt $URLList.Count; $URLListIndex++) + { + $URL = $URLList[$URLListIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to retrieve data from `"$($URL.OriginalString)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $HTMLWebRequest = New-Object -TypeName 'HtmlAgilityPack.HtmlWeb' + + $HTMLWebRequestObject = $HTMLWebRequest.Load($URL.OriginalString) + + $HTMLDocumentObject = $HTMLWebRequestObject.DocumentNode + + $MetadataList = $HTMLDocumentObject.SelectNodes('/html/head//meta') | Where-Object {($_.OuterHTML -imatch '.*og\:title.*')} + + $OGTitle = ($MetadataList[0].Attributes | Where-Object {($_.Name -ieq 'Content')}).Value + + $HistoryTableList = $HTMLDocumentObject.SelectNodes('//table') | Where-Object {($_.ID -imatch '(^HistoryTable_\d+$)')} + + $RootNodeList = $HTMLDocumentObject.SelectNodes('//strong') + + For ($RootNodeListIndex = 0; $RootNodeListIndex -lt $RootNodeList.Count; $RootNodeListIndex++) + { + $RootNode = $RootNodeList[$RootNodeListIndex] + + $RootNodeRegexData = [Regex]::Match($RootNode.InnerText, '(?:.*Version\s+)(?\w{4,4})(?:.*)(?:\s+\(OS\s+build\s+)(?\d{5,5})(?:.+)') + + Switch ($RootNodeRegexData.Success) + { + {($_ -eq $True)} + { + $OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $OutputObjectProperties.Vendor = 'Microsoft' + $OutputObjectProperties.Name = [Regex]::Match($OGTitle, 'W.+\d+').Value + $OutputObjectProperties.ReleaseID = ($RootNodeRegexData.Groups | Where-Object {($_.Name -ieq 'OSReleaseID')}).Value + $OutputObjectProperties.Build = ($RootNodeRegexData.Groups | Where-Object {($_.Name -ieq 'OSBuild')}).Value + $OutputObjectProperties.Version = "10.0.$($OutputObjectProperties.Build)" -As [System.Version] + $OutputObjectProperties.ReleaseHistory = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + <# + $HistoryTableChildNodeList = ($HistoryTableList | Where-Object {($_.InnerText -imatch ".*$($OutputObjectProperties.Build).*")}).ChildNodes | Where-Object {([String]::IsNullOrEmpty($_.InnerText) -eq $False) -and ([String]::IsNullOrWhiteSpace($_.InnerText) -eq $False)} | Sort-Object -Property @('InnerText') -Unique + + For ($HistoryTableChildNodeIndex = 0; $HistoryTableChildNodeIndex -lt $HistoryTableChildNodeList.Count; $HistoryTableChildNodeIndex++) + { + $HistoryTableChildNode = $HistoryTableChildNodeList[$HistoryTableChildNodeIndex] + + $InnerTextLines = $HistoryTableChildNode.InnerText.Split("`n", [System.StringSplitOptions]::RemoveEmptyEntries).Trim() + + For ($InnerTextLineIndex = 0; $InnerTextLineIndex -lt $InnerTextLines.Count; $InnerTextLineIndex++) + { + $InnerTextLine = $InnerTextLines[$InnerTextLineIndex] + + #$InnerTextLine + } + } + #> + + $OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties) + + $OutputObjectList.Add($OutputObject) + } + } + } + + } + + #Write the object to the powershell pipeline + $OutputObjectList = $OutputObjectList.ToArray() + + $Global:WindowsReleaseHistory = $OutputObjectList | Sort-Object -Property @('Version') + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The Windows release history has already been retrieved. Returning cached results in order to reduce web request traffic." + Write-Warning -Message ($LoggingDetails.WarningMessage) + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + + } + } + + 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) + + #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 `'$($FunctionName)`' is completed." + Write-Verbose -Message ($LoggingDetails.LogMessage) + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + Write-Output -InputObject ($Global:WindowsReleaseHistory) + } + } + } +#endregion + +<# + Get-WindowsReleaseHistory -Force:$False -Verbose +#> \ No newline at end of file diff --git a/Functions/Invoke-FileDownload.ps1 b/Functions/Invoke-FileDownload.ps1 new file mode 100644 index 0000000..80fb98d --- /dev/null +++ b/Functions/Invoke-FileDownload.ps1 @@ -0,0 +1,343 @@ +## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx + +#region Function Invoke-FileDownload +Function Invoke-FileDownload + { + <# + .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 full file path where the cabinet will be downloaded to. If not specified, a default value will be used. + + .EXAMPLE + Invoke-FileDownload -URL 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -Destination "$($Env:ProgramData)\Dell\DriverPackCatalog\DriverPackCatalog.cab" -Verbose + + .EXAMPLE + $DownloadDetails = Invoke-FileDownload -URL 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -Destination "$($Env:ProgramData)\Dell\DriverPackCatalog\DriverPackCatalog.cab" -Verbose + + Write-Output -InputObject ($DownloadDetails) + + .EXAMPLE + $InvokeFileDownloadParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeFileDownloadParameters.URL = 'https://dl.dell.com/catalog/DriverPackCatalog.cab' -As [System.URI] + $InvokeFileDownloadParameters.Destination = "$($Env:ProgramData)\Dell\DriverPackCatalog\DriverPackCatalog.cab" -As [System.IO.FileInfo] + $InvokeFileDownloadParameters.ContinueOnError = $False + $InvokeFileDownloadParameters.Verbose = $True + + $InvokeFileDownloadResult = Invoke-FileDownload @InvokeFileDownloadParameters + + Write-Output -InputObject ($InvokeFileDownloadResult) + + .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.downloaddata?view=netframework-4.8#system-net-webclient-downloaddata(system-uri) + #> + + [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 + } + 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 = { + $WebClient = New-Object -TypeName 'System.Net.WebClient' + $WebClient.UseDefaultCredentials = $True + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to download data stream from `"$($URL.OriginalString)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Download Size: $($ContentLengthInMB) MegaBytes" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $Null = Measure-Command -Expression {[Byte[]]$DownloadedData = $WebClient.DownloadData($URL.OriginalString)} -OutVariable 'DownloadExecutionTimespan' + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Stream download took $($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) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to flush the downloaded data stream to file `"$($DestinationPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + If ([System.IO.Directory]::Exists($DestinationPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DestinationPath.Directory.FullName)} + + $Null = [System.IO.File]::WriteAllBytes($DestinationPath.FullName, $DownloadedData) + + [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 `"$($URL.OriginalString)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date (Local): $($DestinationPath.LastWriteTimeUTC.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date (Source): $($WebRequestHeaders.'Last-Modified'.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $Null = (Get-Item -Path $DestinationPath.FullName -Force).LastWriteTimeUTC = $WebRequestHeaders.'Last-Modified' + + $Null = $WebClient.Dispose() + } + + 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 (Source): $($WebRequestHeaders.'Last-Modified'.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Last Modified Date (Local): $($DestinationPath.LastWriteTimeUTC.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 \ No newline at end of file diff --git a/Functions/Invoke-FileDownloadWithProgress.ps1 b/Functions/Invoke-FileDownloadWithProgress.ps1 new file mode 100644 index 0000000..32384e9 --- /dev/null +++ b/Functions/Invoke-FileDownloadWithProgress.ps1 @@ -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 \ No newline at end of file diff --git a/Functions/Invoke-RegistryHiveAction.ps1 b/Functions/Invoke-RegistryHiveAction.ps1 new file mode 100644 index 0000000..57717f1 --- /dev/null +++ b/Functions/Invoke-RegistryHiveAction.ps1 @@ -0,0 +1,355 @@ +## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx + +#region Function Invoke-RegistryHiveAction +Function Invoke-RegistryHiveAction + { + <# + .SYNOPSIS + A brief overview of what your function does + + .DESCRIPTION + Slightly more detailed description of what your function does + + .PARAMETER HivePath + A valid file path to a valid registry hive. + + .PARAMETER KeyPath + One or more registry key paths that are relative to the specified registry hive. + + .PARAMETER ValueNameExpression + One or more regular expressions that will determine which registry value names will be extracted from the specified registry hive. + + .EXAMPLE + Invoke-RegistryHiveAction -HivePath "$($Env:Userprofile)\Downloads\RegistryHives\SOFTWARE" -KeyPath @('Root\Microsoft\Windows NT\CurrentVersion') -ValueNameExpression @('.*') -Verbose + + .EXAMPLE + $InvokeRegistryHiveActionParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeRegistryHiveActionParameters.HivePath = "$($Env:Userprofile)\Downloads\RegistryHives\SOFTWARE" + $InvokeRegistryHiveActionParameters.KeyPath = New-Object -TypeName 'System.Collections.Generic.List[String]' + $InvokeRegistryHiveActionParameters.KeyPath.Add('Root\Microsoft\Windows NT\CurrentVersion') + $InvokeRegistryHiveActionParameters.ValueNameExpression = New-Object -TypeName 'System.Collections.Generic.List[Regex]' + $InvokeRegistryHiveActionParameters.ValueNameExpression.Add('.*') + $InvokeRegistryHiveActionParameters.ContinueOnError = $False + $InvokeRegistryHiveActionParameters.Verbose = $True + + $InvokeRegistryHiveActionResult = Invoke-RegistryHiveAction @InvokeRegistryHiveActionParameters + + Write-Output -InputObject ($InvokeRegistryHiveActionResult) + + .NOTES + Please ensure that the specified registry hive is not in use! + + .LINK + https://github.com/EricZimmerman/Registry + #> + + [CmdletBinding()] + + Param + ( + [Parameter(Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [ValidateScript({(Test-Path -Path $_)})] + [System.IO.FileInfo]$HivePath, + + [Parameter(Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^Root\\.+')] + [String[]]$KeyPath, + + [Parameter(Mandatory=$False)] + [Regex[]]$ValueNameExpression, + + [Parameter(Mandatory=$False)] + [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) + [System.IO.DirectoryInfo]$System32Directory = [System.Environment]::SystemDirectory + $RegexOptionList = New-Object -TypeName 'System.Collections.Generic.List[System.Text.RegularExpressions.RegexOptions]' + $RegexOptionList.Add([System.Text.RegularExpressions.RegexOptions]::IgnoreCase) + $ValueNameExpressionList = New-Object -TypeName 'System.Collections.Generic.List[System.Text.RegularExpressions.Regex]' + + [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) + + #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) + + [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 $($FunctionName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + #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('NFluent.dll') + $LibraryPatternList.Add('NLog.dll') + $LibraryPatternList.Add('Registry.dll') + + 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 + + #Create an object that will contain the functions output. + $OutputObjectList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + #Set default parameter values + Switch ($True) + { + {($Null -ieq $ValueNameExpression) -or ($ValueNameExpression.Count -eq 0)} + { + $ValueNameExpression += '.*' + } + + {($Null -ine $ValueNameExpression) -or ($ValueNameExpression.Count -gt 0)} + { + $ValueNameExpression | ForEach-Object {$ValueNameExpressionList.Add((New-Object -TypeName 'System.Text.RegularExpressions.Regex' -ArgumentList @($_, $RegexOptionList)))} + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + + } + } + + Process + { + Try + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to load registry hive `"$($HivePath.FullName)`" on demand. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $RegistryHive = New-Object -TypeName 'Registry.RegistryHiveOnDemand' -ArgumentList ($HivePath.FullName) + + ForEach ($Key In $KeyPath) + { + Try + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to retrieve the details of registry key path `"$($Key)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $RegistryKeyProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $RegistryKeyProperties.Path = $Key + $RegistryKeyProperties.ValueList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + $RegistryKey = $RegistryHive.GetKey($Key) + + ForEach ($Item In $RegistryKey.Values) + { + ForEach ($Expression In $ValueNameExpressionList) + { + $ExpressionEvaluation = $Expression.Match($Item.ValueName) + + Switch ($ExpressionEvaluation.Success) + { + {($_ -eq $True)} + { + $ValueProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ValueProperties.Name = $Item.ValueName + $ValueProperties.Alias = $ValueProperties.Name -ireplace '(\s+)', '' + $ValueProperties.Type = $Item.ValueType + $ValueProperties.Value = $Null + $ValueProperties.ValueAsDecimal = $Null + $ValueProperties.ValueAsHex = $Null + + #$LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to retrieve the registry key value of `"$($ValueProperties.Name)`" because the value name matches the regular expression of `"$($Expression.ToString())`". Please Wait..." + #Write-Verbose -Message ($LoggingDetails.LogMessage) + + Switch ($Null -ine $Item.ValueData) + { + {($_ -eq $True)} + { + $ValueProperties.Value = $Item.ValueData + + Switch ($ValueProperties.Type) + { + {($_ -iin @('RegDword', 'RegQword'))} + { + $ValueProperties.ValueAsDecimal = Try {[System.Convert]::ToString($ValueProperties.Value, 10)} Catch {} + $ValueProperties.ValueAsHex = Try {'0x' + [System.Convert]::ToString($ValueProperties.Value, 16).PadLeft(8, '0').ToUpper()} Catch {} + } + + {($_ -iin @('RegBinary'))} + { + #$ValueProperties.ValueAsDecimal = Try {} Catch {} + #$ValueProperties.ValueAsHex = Try {} Catch {} + } + } + } + } + + $ValueObject = New-Object -TypeName 'PSObject' -Property ($ValueProperties) + + $RegistryKeyProperties.ValueList.Add($ValueObject) + } + } + } + } + + $RegistryKeyObject = New-Object -TypeName 'PSObject' -Property ($RegistryKeyProperties) + + $OutputObjectList.Add($RegistryKeyObject) + } + Catch + { + $ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ErrorMessageList.Add('Message', $_.Exception.Message) + $ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID) + $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) + } + } + Finally + { + + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + + } + } + + 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) + + #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 `'$($FunctionName)`' is completed." + Write-Verbose -Message ($LoggingDetails.LogMessage) + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + #Write the object to the powershell pipeline + $OutputObjectList = $OutputObjectList.ToArray() + + Write-Output -InputObject ($OutputObjectList) + } + } + } +#endregion \ No newline at end of file diff --git a/Functions/Invoke-SQLDBQuery.ps1 b/Functions/Invoke-SQLDBQuery.ps1 new file mode 100644 index 0000000..e8767d2 --- /dev/null +++ b/Functions/Invoke-SQLDBQuery.ps1 @@ -0,0 +1,436 @@ +## Microsoft Function Naming Convention: http://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx + +#region Function Invoke-SQLDBQuery +Function Invoke-SQLDBQuery + { + <# + .SYNOPSIS + This function is able to execute one or more queries against an the specified Microsoft SQL database using either Windows or SQL authentication. + + .DESCRIPTION + The database connection will be opened, each database query will be executed, the results will be stored, and the database connection will be closed. + + .PARAMETER Server + + .PARAMETER Instance + + .PARAMETER Port + + .PARAMETER UserID + + .PARAMETER Password + + .PARAMETER Database + + .PARAMETER DBQueryList + + .EXAMPLE + $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -UserID 'SQLAuthUser' -Password 'SQLAuthUserPassword' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName') -Verbose + + Write-Output -InputObject ($DBQueryResults) + + .EXAMPLE + $DBQueryList = [System.Collections.Generic.List[String]]@() + $DBQueryList.Add(@" +Select Distinct + dbo.v_Add_Remove_Programs.DisplayName0 As 'DisplayName' +From + dbo.v_Add_Remove_Programs +Order By + dbo.v_Add_Remove_Programs.DisplayName0 +"@) + + $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Database 'YourDatabaseName' -DBQueryList ($DBQueryList) -Verbose + + .EXAMPLE + $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Instance 'MyDBInstance' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName') + + Write-Output -InputObject ($DBQueryResults) + + .EXAMPLE + $DBQueryResults = Invoke-SQLDBQuery -Server 'MyDBServer.mydomain.com' -Port '4530' -Database 'YourDatabaseName' -DBQueryList @('Select * From YourTableName1', 'Select * From YourTableName2') + + Write-Output -InputObject ($DBQueryResults) + + .LINK + https://www.SQLshack.com/connecting-powershell-to-SQL-server/ + + .LINK + https://stackoverflow.com/questions/64681003/fetch-a-whole-row-from-SQL-server-with-powershell + #> + + [CmdletBinding(ConfirmImpact = 'Low', HelpURI = '', DefaultParameterSetName = '_AllParameterSets', SupportsShouldProcess = $True, PositionalBinding = $True)] + Param + ( + [Parameter(Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [String]$Server, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$Instance, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$Port, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$UserID, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$Password, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$Database, + + [Parameter(Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [String[]]$DBQueryList, + + [Parameter(Mandatory=$False, ParameterSetName = 'Export')] + [Switch]$ExportJSON, + + [Parameter(Mandatory=$False, ParameterSetName = 'Export')] + [System.IO.DirectoryInfo]$ExportDirectory, + + [Parameter(Mandatory=$False)] + [Switch]$ContinueOnError + ) + + Begin + { + [ScriptBlock]$ErrorHandlingDefinition = { + If ([String]::IsNullOrEmpty($_.Exception.Message)) {$ExceptionMessage = "$($_.Exception.Errors.Message -Join "`r`n`r`n")"} Else {$ExceptionMessage = "$($_.Exception.Message)"} + + [String]$ErrorMessage = "[Error Message: $($ExceptionMessage)]`r`n`r`n[ScriptName: $($_.InvocationInfo.ScriptName)]`r`n[Line Number: $($_.InvocationInfo.ScriptLineNumber)]`r`n[Line Position: $($_.InvocationInfo.OffsetInLine)]`r`n[Code: $($_.InvocationInfo.Line.Trim())]" + + If ($ContinueOnError.IsPresent -eq $True) + { + Write-Warning -Message ($ErrorMessage) + } + ElseIf ($ContinueOnError.IsPresent -eq $False) + { + Throw ($ErrorMessage) + } + } + + Try + { + $DateTimeMessageFormat = 'MM/dd/yyyy HH:mm:ss.FFF' ###03/23/2022 11:12:48.347### + [ScriptBlock]$GetCurrentDateTimeMessageFormat = {(Get-Date).ToString($DateTimeMessageFormat)} + $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)} + $DateFileFormat = 'yyyyMMdd' ###20190403### + [ScriptBlock]$GetCurrentDateFileFormat = {(Get-Date).ToString($DateFileFormat)} + $DateTimeFileFormat = 'yyyyMMdd_HHmmss' ###20190403_115354### + [ScriptBlock]$GetCurrentDateTimeFileFormat = {(Get-Date).ToString($DateTimeFileFormat)} + $TextInfo = (Get-Culture).TextInfo + + #Determine the date and time we executed the function + $FunctionStartTime = (Get-Date) + + [String]$CmdletName = $MyInvocation.MyCommand.Name + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is beginning. Please Wait..." + Write-Verbose -Message $LogMessage + + #Define Default Action Preferences + $ErrorActionPreference = 'Stop' + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The following parameters and values were provided to the `'$($CmdletName)`' function." + Write-Verbose -Message $LogMessage + + $FunctionProperties = Get-Command -Name $CmdletName + + $FunctionParameters = $FunctionProperties.Parameters.Keys | Where-Object {($_ -inotmatch '(^Password$)|(^UserID$)|(^DBQueryList$)')} + + ForEach ($Parameter In $FunctionParameters) + { + If (!([String]::IsNullOrEmpty($Parameter))) + { + $ParameterProperties = Get-Variable -Name $Parameter -ErrorAction SilentlyContinue + $ParameterValueCount = $ParameterProperties.Value | Measure-Object | Select-Object -ExpandProperty Count + + If ($ParameterValueCount -gt 1) + { + $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join ", " + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ParameterProperties.Name):`r`n`r`n$($ParameterValueStringFormat)" + } + Else + { + $ParameterValueStringFormat = ($ParameterProperties.Value | ForEach-Object {"`"$($_)`""}) -Join ', ' + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ParameterProperties.Name): $($ParameterValueStringFormat)" + } + + If (!([String]::IsNullOrEmpty($ParameterProperties.Name))) + { + Write-Verbose -Message $LogMessage + } + } + } + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) began on $($FunctionStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message $LogMessage + + #Create an array list that will contain the functions output + $OutputObjectProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + + #Define Variable(s) + $LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $LoggingDetails.Add('LogMessage', $Null) + $LoggingDetails.Add('WarningMessage', $Null) + $LoggingDetails.Add('ErrorMessage', $Null) + + #Create a database connection string builder + $DBConnectionStringBuilder = [System.Data.SQLClient.SQLConnectionStringBuilder]::New() + + #Create a database data source builder + $DataSourceBuilder = [System.Text.StringBuilder]::New() + + #Dynamically build the connection string based on parameter values + Switch ($True) + { + {([String]::IsNullOrEmpty($Server) -eq $False) -and ([String]::IsNullOrWhiteSpace($Server) -eq $False)} + { + $Null = $DataSourceBuilder.Append($Server) + } + + {([String]::IsNullOrEmpty($Instance) -eq $False) -and ([String]::IsNullOrWhiteSpace($Instance) -eq $False)} + { + $Null = $DataSourceBuilder.Append("\$($Instance)") + } + + {([String]::IsNullOrEmpty($Port) -eq $False) -and ([String]::IsNullOrWhiteSpace($Port) -eq $False)} + { + $Null = $DataSourceBuilder.Append(",$($Port)") + } + + {([String]::IsNullOrEmpty($Database) -eq $False) -and ([String]::IsNullOrWhiteSpace($Database) -eq $False)} + { + $DBConnectionStringBuilder.PSBase.InitialCatalog = $Database + } + + {([String]::IsNullOrEmpty($ExportDirectory) -eq $True) -and ([String]::IsNullOrWhiteSpace($ExportDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$ExportDirectory = "$($Env:Public)\Documents\Reports\$($CmdletName)" + } + } + + $DBConnection = [System.Data.SQLClient.SQLConnection]::New() + + Switch (([String]::IsNullOrEmpty($UserID) -eq $True) -or ([String]::IsNullOrWhiteSpace($UserID) -eq $True) -or ([String]::IsNullOrEmpty($Password) -eq $True) -or ([String]::IsNullOrWhiteSpace($Password) -eq $True)) + { + {($_ -eq $True)} + { + $DBConnectionStringBuilder.PSBase.IntegratedSecurity = $True + } + + Default + { + $DBConnectionStringBuilder.PSBase.IntegratedSecurity = $False + + $Credential = [System.Management.Automation.PSCredential]::New($UserID, (ConvertTo-SecureString -String $Password -AsPlainText -Force)) + + $Null = $Credential.Password.MakeReadOnly() + + $DBConnectionCredential = [System.Data.SQLClient.SQLCredential]::New($Credential.UserName, $Credential.Password) + + $DBConnection.Credential = ($DBConnectionCredential) + } + } + + $DBConnectionStringBuilder.PSBase.DataSource = $DataSourceBuilder.ToString() + + $DBConnectionString = $DBConnectionStringBuilder.ConnectionString + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Database Connection String = $($DBConnectionString)" + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $DBConnection.ConnectionString = ($DBConnectionString) + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to open the database connection. Please Wait..." + Write-Verbose -Message $LogMessage + + $DBConnection.Open() + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + } + + Process + { + For ($DBQueryListIndex = 0; $DBQueryListIndex -lt $DBQueryList.Count; $DBQueryListIndex++) + { + Try + { + [String]$DBQuery = $DBQueryList[$DBQueryListIndex] + [Int]$DBQueryNumber = $DBQueryListIndex + 1 + + $DBCommand = [System.Data.SQLClient.SQLCommand]::New() + $DBCommand.Connection = ($DBConnection) + $DBCommand.CommandText = ($DBQuery) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to execute database query #$($DBQueryNumber). Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $Null = $DBCommand.ExecuteScalar() + + $DBQueryResults = [System.Data.DataTable]::New() + + $DBDataAdapter = [System.Data.SQLClient.SQLDataAdapter]::New($DBCommand) + $Null = $DBDataAdapter.Fill($DBQueryResults) + + $DBQueryRowCount = $DBQueryResults.Rows.Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "$($DBQueryRowCount) row(s) were returned from the database query." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + Switch ($DBQueryRowCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Database Query Column Names = $($DBQueryResults.Columns.ColumnName -Join ', ')" + Write-Verbose -Message ($LoggingDetails.LogMessage) + } + } + + [String]$ResultSetNumber = ($DBQueryNumber).ToString('000') + [String]$ResultSetPropertyName = "ResultSet$($ResultSetNumber)" + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to add the result(s) from database query #$($DBQueryNumber) to property `"$($ResultSetPropertyName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $PropertyInclusionList = [System.Collections.Generic.List[Object]]@() + $Null = $PropertyInclusionList.Add('*') + + $PropertyExclusionList = [System.Collections.Generic.List[Object]]@() + $Null = $PropertyExclusionList.AddRange(('HasErrors', 'ItemArray', 'RowError', 'RowState', 'Table')) + + $DBQueryRows = $DBQueryResults.Rows | Select-Object -Property ($PropertyInclusionList) -ExcludeProperty ($PropertyExclusionList) + + $Null = $OutputObjectProperties.Add($ResultSetPropertyName, $DBQueryRows) + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + Try + { + $Null = $DBQueryResults.Dispose() + $Null = $DBDataAdapter.Dispose() + } + Catch + { + + } + } + } + } + + End + { + Try + { + #Close the database connection + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to close the database connection. Please Wait..." + Write-Verbose -Message $LogMessage + + $Null = $DBConnection.Close() + + #Create the powershell object that will be exported to the powershell pipeline + $OutputObject = New-Object -TypeName 'PSObject' -Property ($OutputObjectProperties) + + #If specified, export each result set to a separate JSON file + Switch ($True) + { + {($ExportJSON.IsPresent)} + { + $ExportParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ExportParameters.Add('Compress', $False) + $ExportParameters.Add('Encoding', [System.Text.Encoding]::Default) + $ExportParameters.Add('Depth', 10) + + [String[]]$OutputObjectPropertyList = $OutputObject.PSObject.Properties.Name + + For ($OutputObjectPropertyListIndex = 0; $OutputObjectPropertyListIndex -lt $OutputObjectPropertyList.Count; $OutputObjectPropertyListIndex++) + { + [String]$OutputObjectPropertyName = $OutputObjectPropertyList[$OutputObjectPropertyListIndex] + + [System.IO.FileInfo]$OutputObjectExportPath = "$($ExportDirectory.FullName)\$($OutputObjectPropertyName).json" + + Switch ([System.IO.File]::Exists($OutputObjectExportPath.FullName)) + { + {($_ -eq $True)} + { + [String]$JSONContents = [System.IO.File]::ReadAllText($OutputObjectExportPath.FullName, $ExportParameters.Encoding) + + $JSONObject = ConvertFrom-JSON -InputObject ($JSONContents) + + $OutputObjectPropertyValue = $OutputObject.$($OutputObjectPropertyName) + + $JSONObject.$($OutputObjectPropertyName) = ($OutputObjectPropertyValue) + + [String]$OutputObjectPropertyValueAsJSON = $JSONObject | ConvertTo-JSON -Depth ($ExportParameters.Depth) -Compress:($ExportParameters.Compress) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to update the result set `"$($OutputObjectPropertyName)`" within the file of `"$($OutputObjectExportPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $Null = [System.IO.File]::WriteAllText($OutputObjectExportPath.FullName, $OutputObjectPropertyValueAsJSON, $ExportParameters.Encoding) + } + + {($_ -eq $False)} + { + $OutputObjectPropertyValue = $OutputObject | Select-Object -Property @($OutputObjectPropertyName) + + [String]$OutputObjectPropertyValueAsJSON = $OutputObjectPropertyValue | ConvertTo-JSON -Depth ($ExportParameters.Depth) -Compress:($ExportParameters.Compress) + + Switch ([System.IO.Directory]::Exists($OutputObjectExportPath.Directory.FullName)) + { + {($_ -eq $False)} + { + $Null = [System.IO.Directory]::CreateDirectory($OutputObjectExportPath.Directory.FullName) + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - " + "Attempting to export the result set `"$($OutputObjectPropertyName)`" to the file of `"$($OutputObjectExportPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + + $Null = [System.IO.File]::WriteAllText($OutputObjectExportPath.FullName, $OutputObjectPropertyValueAsJSON, $ExportParameters.Encoding) + } + } + } + } + } + + #Write the object to the powershell pipeline + Write-Output -InputObject ($OutputObject) + + #Determine the date and time the function completed execution + $FunctionEndTime = (Get-Date) + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of $($CmdletName) ended on $($FunctionEndTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message $LogMessage + + #Log the total script execution time + $FunctionExecutionTimespan = New-TimeSpan -Start ($FunctionStartTime) -End ($FunctionEndTime) + + $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 $LogMessage + + $LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Function `'$($CmdletName)`' is completed." + Write-Verbose -Message $LogMessage + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + } + } +#endregion \ No newline at end of file diff --git a/Functions/Libraries/AlphaFS.dll b/Functions/Libraries/AlphaFS.dll new file mode 100644 index 0000000..0a7d7c4 Binary files /dev/null and b/Functions/Libraries/AlphaFS.dll differ diff --git a/Functions/Libraries/HtmlAgilityPack.dll b/Functions/Libraries/HtmlAgilityPack.dll new file mode 100644 index 0000000..b6dc725 Binary files /dev/null and b/Functions/Libraries/HtmlAgilityPack.dll differ diff --git a/Functions/Libraries/NFluent.dll b/Functions/Libraries/NFluent.dll new file mode 100644 index 0000000..74b2e8e Binary files /dev/null and b/Functions/Libraries/NFluent.dll differ diff --git a/Functions/Libraries/NLog.dll b/Functions/Libraries/NLog.dll new file mode 100644 index 0000000..ae95cbe Binary files /dev/null and b/Functions/Libraries/NLog.dll differ diff --git a/Functions/Libraries/Registry.dll b/Functions/Libraries/Registry.dll new file mode 100644 index 0000000..9f84ebb Binary files /dev/null and b/Functions/Libraries/Registry.dll differ diff --git a/Functions/Start-ProcessWithOutput.ps1 b/Functions/Start-ProcessWithOutput.ps1 new file mode 100644 index 0000000..4747419 --- /dev/null +++ b/Functions/Start-ProcessWithOutput.ps1 @@ -0,0 +1,366 @@ +#region Start-ProcessWithOutput +Function Start-ProcessWithOutput + { + [CmdletBinding()] + Param + ( + [Parameter(Mandatory=$True)] + [ValidateNotNullOrEmpty()] + [String]$FilePath, + + [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+)(?.+)(?:\s+\:\s+)(?.+)' + } + } + + $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 ($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 += '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 (($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($StandardOutputParsingExpression) -eq $False) -and ([String]::IsNullOrWhiteSpace($StandardOutputParsingExpression) -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($StandardOutputParsingExpression, $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 +#> \ No newline at end of file diff --git a/Invoke-ConfigurationGenerator.ps1 b/Invoke-ConfigurationGenerator.ps1 new file mode 100644 index 0000000..24eb5d8 --- /dev/null +++ b/Invoke-ConfigurationGenerator.ps1 @@ -0,0 +1,913 @@ +#Requires -Version 3 + +<# + .SYNOPSIS + Dynamically generates the XML required for the Invoke-DriverPackageCreation powershell script. + + .DESCRIPTION + This script greatly reduces the amount of time it would take to build the XML by hand after determining how many models are deployed within the current environment. + + .PARAMETER QuerySQLDatabase + Determines whether or not to enable the SQL database querying functionality. + + .PARAMETER SQLDatabaseFQDN + The fully qualified domain name of the SQL database that will be queried. Windows authentication will be used by default, but SQL authentication can be used if modifications are made to the 'Invoke-SQLDBQuery' parameters. + + .PARAMETER SQLDatabaseBName + The name of the SQL database that will be queried. + + .PARAMETER LogDir + A valid folder path. If the folder does not exist, it will be created. This parameter can also be specified by the alias "LogPath". + + .PARAMETER ContinueOnError + Ignore failures. + + .EXAMPLE + Use this command to execute a VBSCript that will launch this powershell script automatically with the specified parameters. This is useful to avoid powershell execution complexities. + + cscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + wscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "%FolderPathContainingScript%\%ScriptName%.ps1" -SwitchParameter -ScriptParameter "%ScriptParameterValue%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command "& '%FolderPathContainingScript%\%ScriptName%.ps1' -ScriptParameter1 '%ScriptParameter1Value%' -ScriptParameter2 %ScriptParameter2Value% -SwitchParameter" + + .NOTES + If using the database query functionality to get the model list, just ensure that the following columns are returned in order for the XML schema to be generated correctly. + + BaseboardProduct + SystemFamily + SystemManufacturer + SystemProductName + SystemSKU + SystemVersion + + .LINK + Place any useful link here where your function or cmdlet can be referenced +#> + +[CmdletBinding(SupportsShouldProcess=$True)] + Param + ( + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Switch]$QuerySQLDatabase, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$SQLDatabaseFQDN, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [String]$SQLDatabaseBName, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('LogDir', 'LogPath')] + [System.IO.DirectoryInfo]$LogDirectory, + + [Parameter(Mandatory=$False)] + [Switch]$ContinueOnError + ) + +Function Get-AdministrativePrivilege + { + $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity) + Write-Output -InputObject ($Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) + } + +If ((Get-AdministrativePrivilege) -eq $False) + { + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Path)" + + $ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $ArgumentList.Add('-ExecutionPolicy Bypass') + $ArgumentList.Add('-NoProfile') + $ArgumentList.Add('-NoExit') + $ArgumentList.Add('-NoLogo') + $ArgumentList.Add("-File `"$($ScriptPath.FullName)`"") + + $Null = Start-Process -FilePath "$([System.Environment]::SystemDirectory)\WindowsPowershell\v1.0\powershell.exe" -WorkingDirectory "$([System.Environment]::SystemDirectory)" -ArgumentList ($ArgumentList.ToArray()) -WindowStyle Normal -Verb RunAs -PassThru + } +Else + { + #Determine the date and time we executed the function + $ScriptStartTime = (Get-Date) + + #Define Default Action Preferences + $Script:DebugPreference = 'SilentlyContinue' + $Script:ErrorActionPreference = 'Stop' + $Script:VerbosePreference = 'SilentlyContinue' + $Script:WarningPreference = 'Continue' + $Script:ConfirmPreference = 'None' + $Script:WhatIfPreference = $False + + #Load WMI Classes + $Baseboard = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Baseboard" -Property * + $Bios = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Bios" -Property * + $ComputerSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_ComputerSystem" -Property * + $OperatingSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_OperatingSystem" -Property * + $MSSystemInformation = Get-WmiObject -Namespace "root\WMI" -Class "MS_SystemInformation" -Property * + + #Retrieve property values + $OSArchitecture = $($OperatingSystem.OSArchitecture).Replace("-bit", "").Replace("32", "86").Insert(0,"x").ToUpper() + + #Define variable(s) + $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)} + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Definition)" + [System.IO.DirectoryInfo]$ScriptDirectory = "$($ScriptPath.Directory.FullName)" + [System.IO.DirectoryInfo]$ContentDirectory = "$($ScriptDirectory.FullName)\Content" + [System.IO.DirectoryInfo]$FunctionsDirectory = "$($ScriptDirectory.FullName)\Functions" + [System.IO.DirectoryInfo]$ModulesDirectory = "$($ScriptDirectory.FullName)\Modules" + [System.IO.DirectoryInfo]$ToolsDirectory = "$($ScriptDirectory.FullName)\Tools" + [System.IO.DirectoryInfo]$ToolsDirectory_OSAll = "$($ToolsDirectory.FullName)\All" + [System.IO.DirectoryInfo]$ToolsDirectory_OSArchSpecific = "$($ToolsDirectory.FullName)\$($OSArchitecture)" + [System.IO.DirectoryInfo]$System32Directory = [System.Environment]::SystemDirectory + [System.IO.DirectoryInfo]$ProgramFilesDirectory = "$($Env:SystemDrive)\Program Files" + [System.IO.DirectoryInfo]$ProgramFilesx86Directory = "$($Env:SystemDrive)\Program Files (x86)" + [System.IO.FileInfo]$PowershellPath = "$($System32Directory.FullName)\WindowsPowershell\v1.0\powershell.exe" + [System.IO.DirectoryInfo]$System32Directory = "$([System.Environment]::SystemDirectory)" + $IsWindowsPE = Test-Path -Path 'HKLM:\SYSTEM\ControlSet001\Control\MiniNT' -ErrorAction SilentlyContinue + [System.Text.RegularExpressions.RegexOptions[]]$RegexOptions = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase, [System.Text.RegularExpressions.RegexOptions]::Multiline + [ScriptBlock]$GetRandomGUID = {[System.GUID]::NewGUID().GUID.ToString().ToUpper()} + [String]$ParameterSetName = "$($PSCmdlet.ParameterSetName)" + $TextInfo = (Get-Culture).TextInfo + $Script:LASTEXITCODE = 0 + $TerminationCodes = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $TerminationCodes.Add('Success', @(0)) + $TerminationCodes.Add('Warning', @(5000..5999)) + $TerminationCodes.Add('Error', @(6000..6999)) + $Script:WarningCodeIndex = 0 + [ScriptBlock]$GetAvailableWarningCode = {$TerminationCodes.Warning[$Script:WarningCodeIndex]; $Script:WarningCodeIndex++} + $Script:ErrorCodeIndex = 0 + [ScriptBlock]$GetAvailableErrorCode = {$TerminationCodes.Error[$Script:ErrorCodeIndex]; $Script:ErrorCodeIndex++} + $LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $LoggingDetails.Add('LogMessage', $Null) + $LoggingDetails.Add('WarningMessage', $Null) + $LoggingDetails.Add('ErrorMessage', $Null) + $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] + $CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters) + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters) + $HashAlgorithm = 'SHA256' + + #Define the error handling definition + [ScriptBlock]$ErrorHandlingDefinition = { + If (($Null -ieq $Script:LASTEXITCODE) -or ($Script:LASTEXITCODE -eq 0)) + { + [Int]$Script:LASTEXITCODE = $GetAvailableErrorCode.InvokeReturnAsIs() + } + + $ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ErrorMessageList.Add('Message', $_.Exception.Message) + $ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID) + $ErrorMessageList.Add('ExitCode', $Script:LASTEXITCODE) + $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 + } + + Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False)) + { + {($_ -eq $True)} + { + Throw + } + } + } + + #Determine default parameter value(s) + Switch ($True) + { + {([String]::IsNullOrEmpty($LogDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($LogDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$ApplicationDataRootDirectory = "$($Env:ProgramData)\Invoke-DriverPackageCreator" + + [System.IO.DirectoryInfo]$LogDirectory = "$($ApplicationDataRootDirectory.FullName)\Logs" + } + } + + #Start transcripting (Logging) + [System.IO.FileInfo]$ScriptLogPath = "$($LogDirectory.FullName)\$($ScriptPath.BaseName)_$($GetCurrentDateFileFormat.Invoke()).log" + If ($ScriptLogPath.Directory.Exists -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($ScriptLogPath.Directory.FullName)} + Start-Transcript -Path "$($ScriptLogPath.FullName)" -Force -WhatIf:$False + + #Log any useful information + [String]$CmdletName = $MyInvocation.MyCommand.Name + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of script `"$($CmdletName)`" began on $($ScriptStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script Path = $($ScriptPath.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [String[]]$AvailableScriptParameters = (Get-Command -Name ($ScriptPath.FullName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"} + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Script 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 Script Parameter(s) = $($SuppliedScriptParameters -Join ', ')" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($True) + { + {([String]::IsNullOrEmpty($ParameterSetName) -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Parameter Set Name = $($ParameterSetName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Command Line: $((Get-WMIObject -Namespace 'Root\CIMv2' -Class 'Win32_Process' -Filter "ProcessID = $($PID)").CommandLine)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($PSBoundParameters.Count) command line parameter(s) were specified." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $OperatingSystemDetailsTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $OperatingSystemDetailsTable.ProductName = $OperatingSystem.Caption -ireplace '(Microsoft\s+)', '' + $OperatingSystemDetailsTable.Version = $OperatingSystem.Version + $OperatingSystemDetailsTable.Architecture = $OperatingSystem.OSArchitecture + + $OperatingSystemRegistryDetails = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = ''; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'UBR'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseVersion'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'ReleaseID'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseID'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'DisplayVersion'; Value = $Null})) + + ForEach ($OperatingSystemRegistryDetail In $OperatingSystemRegistryDetails) + { + $OperatingSystemRegistryDetail.Value = Try {(Get-Item -Path $OperatingSystemRegistryDetail.Path).GetValue($OperatingSystemRegistryDetail.ValueName)} Catch {} + + :NextOSDetail Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Value) -eq $False)) + { + {($_ -eq $True)} + { + Switch ($OperatingSystemRegistryDetail.ValueName) + { + {($_ -ieq 'UBR')} + { + $OperatingSystemDetailsTable.Version = $OperatingSystemDetailsTable.Version + '.' + $OperatingSystemRegistryDetail.Value + + Break NextOSDetail + } + } + + Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Alias) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Alias) -eq $False)) + { + {($_ -eq $True)} + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.Alias) = $OperatingSystemRegistryDetail.Value + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + ForEach ($OperatingSystemDetail In $OperatingSystemDetailsTable.GetEnumerator()) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($OperatingSystemDetail.Key): $($OperatingSystemDetail.Value)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Powershell Version: $($PSVersionTable.PSVersion.ToString())" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ExecutionPolicyList = Get-ExecutionPolicy -List + + For ($ExecutionPolicyListIndex = 0; $ExecutionPolicyListIndex -lt $ExecutionPolicyList.Count; $ExecutionPolicyListIndex++) + { + $ExecutionPolicy = $ExecutionPolicyList[$ExecutionPolicyListIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The powershell execution policy is currently set to `"$($ExecutionPolicy.ExecutionPolicy.ToString())`" for the `"$($ExecutionPolicy.Scope.ToString())`" scope." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + #Log hardware information + $MSSystemInformationMembers = $MSSystemInformation.PSObject.Properties | Where-Object {($_.MemberType -imatch '^NoteProperty$|^Property$') -and ($_.Name -imatch '^Base.*|Bios.*|System.*$') -and ($_.Name -inotmatch '^.*Major.*|.*Minor.*|.*Properties.*$')} | Sort-Object -Property @('Name') + + Switch ($MSSystemInformationMembers.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to display device information properties from the `"$($MSSystemInformation.__CLASS)`" WMI class located within the `"$($MSSystemInformation.__NAMESPACE)`" WMI namespace. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + ForEach ($MSSystemInformationMember In $MSSystemInformationMembers) + { + [String]$MSSystemInformationMemberName = ($MSSystemInformationMember.Name) + [String]$MSSystemInformationMemberValue = $MSSystemInformation.$($MSSystemInformationMemberName) + + Switch ([String]::IsNullOrEmpty($MSSystemInformationMemberValue)) + { + {($_ -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($MSSystemInformationMemberName) = $($MSSystemInformationMemberValue)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The `"MSSystemInformation`" WMI class could not be found." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #region Log Cleanup + [Int]$MaximumLogHistory = 3 + + $LogList = Get-ChildItem -Path ($LogDirectory.FullName) -Filter "$($ScriptPath.BaseName)_*" -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $SortedLogList = $LogList | Sort-Object -Property @('LastWriteTime') -Descending | Select-Object -Skip ($MaximumLogHistory) + + Switch ($SortedLogList.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + For ($SortedLogListIndex = 0; $SortedLogListIndex -lt $SortedLogList.Count; $SortedLogListIndex++) + { + Try + { + $Log = $SortedLogList[$SortedLogListIndex] + + $LogAge = New-TimeSpan -Start ($Log.LastWriteTime) -End (Get-Date) + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to cleanup log file `"$($Log.FullName)`". Please Wait... [Last Modified: $($Log.LastWriteTime.ToString($DateTimeMessageFormat))] [Age: $($LogAge.Days.ToString()) day(s); $($LogAge.Hours.ToString()) hours(s); $($LogAge.Minutes.ToString()) minute(s); $($LogAge.Seconds.ToString()) second(s)]." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $Null = [System.IO.File]::Delete($Log.FullName) + } + Catch + { + + } + } + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + #endregion + + #region Import Dependency Modules + If (($ModulesDirectory.Exists -eq $True) -and ($ModulesDirectory.GetDirectories().Count -gt 0)) + { + $Modules = Get-Module -Name "$($ModulesDirectory.FullName)\*" -ListAvailable -ErrorAction Stop + + $ModuleGroups = $Modules | Group-Object -Property @('Name') + + ForEach ($ModuleGroup In $ModuleGroups) + { + $LatestModuleVersion = $ModuleGroup.Group | Sort-Object -Property @('Version') -Descending | Select-Object -First 1 + + If ($Null -ine $LatestModuleVersion) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to import dependency powershell module `"$($LatestModuleVersion.Name)`" [Version: $($LatestModuleVersion.Version.ToString())]. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Try {$Null = Import-Module -Name "$($LatestModuleVersion.Path)" -Global -DisableNameChecking -Force -Verbose:$False} Catch {} + } + } + } + #endregion + + #region Dot Source Dependency Scripts + #Dot source any additional script(s) from the functions directory. This will provide flexibility to add additional functions without adding complexity to the main script and to maintain function consistency. + Try + { + If ($FunctionsDirectory.Exists -eq $True) + { + $AdditionalFunctionsFilter = New-Object -TypeName 'System.Collections.Generic.List[String]' + $AdditionalFunctionsFilter.Add('*.ps1') + + $AdditionalFunctionsToImport = Get-ChildItem -Path "$($FunctionsDirectory.FullName)" -Include ($AdditionalFunctionsFilter) -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $AdditionalFunctionsToImportCount = $AdditionalFunctionsToImport | Measure-Object | Select-Object -ExpandProperty Count + + If ($AdditionalFunctionsToImportCount -gt 0) + { + ForEach ($AdditionalFunctionToImport In $AdditionalFunctionsToImport) + { + Try + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to dot source the functions contained within the dependency script `"$($AdditionalFunctionToImport.Name)`". Please Wait... [Script Path: `"$($AdditionalFunctionToImport.FullName)`"]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + . "$($AdditionalFunctionToImport.FullName)" + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + } + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + #endregion + + #region Load any required libraries + [System.IO.DirectoryInfo]$LibariesDirectory = "$($FunctionsDirectory.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) -Verbose + + $Null = [System.Reflection.Assembly]::Load($LibraryBytes) + } + } + } + } + } + } + } + #endregion + + #Perform script action(s) + Try + { + #region Define the driver package creation settings XML schema + $GetXMLDateCreated = {(Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm')} + + $ManufacturerList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + $ManufacturerListEntry = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.Enabled = $True + $ManufacturerListEntry.Name = 'Dell' + $ManufacturerListEntry.EligibilityExpression = '(^.*Dell.*$)' + $ManufacturerListEntry.ProductIDExpression = @{Name = 'BaseboardProduct'; Expression = {If ($_.SystemSKU.Length -lt 6) {$_.SystemSKU} Else {$Null}}} + $ManufacturerListEntry.ProductIDPropertyName = 'SystemSKU' + $ManufacturerListEntry.URLs = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.URLs.DriverPackCatalog = 'https://dl.dell.com/catalog/DriverPackCatalog.cab' + $ManufacturerListEntry.URLs.DownloadBase = 'https://dl.dell.com' + $ManufacturerListEntry.ModelList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + If ($QuerySQLDatabase.IsPresent -eq $False) + { + $ManufacturerListEntry.ModelList.Add((New-Object -TypeName 'PSObject' -Property @{Enabled = $False; SystemProductName = 'Latitude 5430'; ProductID = '0B04'; BaseboardProduct = '01Y2TP'; SystemSKU = '0B04'; SystemVersion = ''; SystemFamily = 'Latitude'; SystemManufacturer = $ManufacturerListEntry.Name})) + } + + $ManufacturerListObject = New-Object -TypeName 'PSObject' -Property ($ManufacturerListEntry) + $ManufacturerList.Add($ManufacturerListEntry) + + $ManufacturerListEntry = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.Enabled = $True + $ManufacturerListEntry.Name = 'HP' + $ManufacturerListEntry.EligibilityExpression = '(^.*HP.*$)|(^.*Hewlett.*Packard.*$)' + $ManufacturerListEntry.ProductIDExpression = @{Name = 'BaseboardProduct'; Expression = {[Regex]::Match($_.BaseboardProduct, '^\w{4}').Value.ToUpper()}} + $ManufacturerListEntry.ProductIDPropertyName = 'BaseboardProduct' + $ManufacturerListEntry.URLs = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.URLs.DriverPackCatalog = 'https://ftp.hp.com/pub/caps-softpaq/cmit/HPClientDriverPackCatalog.cab' + $ManufacturerListEntry.URLs.DownloadBase = '' + $ManufacturerListEntry.ModelList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + If ($QuerySQLDatabase.IsPresent -eq $False) + { + $ManufacturerListEntry.ModelList.Add((New-Object -TypeName 'PSObject' -Property @{Enabled = $False; SystemProductName = 'HP ZBook Studio G7 Mobile Workstation'; ProductID = '8736'; BaseboardProduct = '8736'; SystemSKU = '8YP41AV'; SystemVersion = ''; SystemFamily = '103C_5336AN HP ZBook'; SystemManufacturer = $ManufacturerListEntry.Name})) + } + + $ManufacturerListObject = New-Object -TypeName 'PSObject' -Property ($ManufacturerListEntry) + $ManufacturerList.Add($ManufacturerListEntry) + + $ManufacturerListEntry = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.Enabled = $True + $ManufacturerListEntry.Name = 'Lenovo' + $ManufacturerListEntry.EligibilityExpression = '(^.*LENOVO.*$)' + $ManufacturerListEntry.ProductIDExpression = @{Name = 'BaseboardProduct'; Expression = {[Regex]::Match($_.SystemProductName, '^\w{4}').Value.ToUpper()}} + $ManufacturerListEntry.ProductIDPropertyName = 'SystemProductName' + $ManufacturerListEntry.URLs = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerListEntry.URLs.DriverPackCatalog = 'https://download.lenovo.com/cdrt/td/catalogv2.xml' + $ManufacturerListEntry.URLs.DownloadBase = '' + $ManufacturerListEntry.ModelList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + If ($QuerySQLDatabase.IsPresent -eq $False) + { + $ManufacturerListEntry.ModelList.Add((New-Object -TypeName 'PSObject' -Property @{Enabled = $False; SystemProductName = '10AXS2CQ00'; ProductID = '10AX'; BaseboardProduct = '10AXS2CQ00'; SystemSKU = 'LENOVO_MT_10AX'; SystemVersion = 'ThinkCentre M73'; SystemFamily = 'To be filled by O.E.M.'; SystemManufacturer = $ManufacturerListEntry.Name})) + } + + $ManufacturerListObject = New-Object -TypeName 'PSObject' -Property ($ManufacturerListEntry) + $ManufacturerList.Add($ManufacturerListObject) + + Switch ($QuerySQLDatabase.IsPresent) + { + {($_ -eq $True)} + { + Switch (Test-Connection -ComputerName $SQLDatabaseFQDN -Count 1 -Quiet) + { + {($_ -eq $True)} + { + $ModelListConfigurationDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ModelListConfigurationDetails.ModelListQueryPath = "$($ContentDirectory.FullName)\DBQueries\GetProductIDList.sql" -As [System.IO.FileInfo] + + Switch ([System.IO.File]::Exists($ModelListConfigurationDetails.ModelListQueryPath.FullName)) + { + {($_ -eq $True)} + { + $ModelListConfigurationDetails.ModelListQueryContents = [System.IO.File]::ReadAllText($ModelListConfigurationDetails.ModelListQueryPath.FullName) + + $InvokeSQLDBQueryParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeSQLDBQueryParameters.Server = $SQLDatabaseFQDN + $InvokeSQLDBQueryParameters.Database = $SQLDatabaseBName + $InvokeSQLDBQueryParameters.DBQueryList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $InvokeSQLDBQueryParameters.DBQueryList.Add($ModelListConfigurationDetails.ModelListQueryContents) + $InvokeSQLDBQueryParameters.ContinueOnError = $False + $InvokeSQLDBQueryParameters.Verbose = $True + + $SQLDBQueryResultSet = (Invoke-SQLDBQuery @InvokeSQLDBQueryParameters).ResultSet001 | Sort-Object -Property {$_.SystemProductName.Length} -Descending + + $SQLDBQueryResultSetCount = ($SQLDBQueryResultSet | Measure-Object).Count + + Switch ($SQLDBQueryResultSetCount -gt 0) + { + {($_ -eq $True)} + { + ForEach ($SQLDBQueryResult In $SQLDBQueryResultSet) + { + $SQLDBQueryResultDetails = $ManufacturerList | Where-Object {($SQLDBQueryResult.SystemManufacturer -imatch $_.EligibilityExpression.ToString())} + + Switch ($Null -ine $SQLDBQueryResultDetails) + { + {($_ -eq $True)} + { + $ProductID = Try {($SQLDBQueryResult | Select-Object -Property ($SQLDBQueryResultDetails.ProductIDExpression)).BaseBoardProduct} Catch {$Null} + + Switch (([String]::IsNullOrEmpty($ProductID) -eq $False) -and ([String]::IsNullOrWhiteSpace($ProductID) -eq $False)) + { + {($_ -eq $True)} + { + $SQLDBQueryResult.SystemManufacturer = $SQLDBQueryResultDetails.Name + + $SQLDBQueryResult | Add-Member -Name 'Enabled' -Value $True -MemberType NoteProperty + $SQLDBQueryResult | Add-Member -Name 'ProductID' -Value ($ProductID) -MemberType NoteProperty + + Switch ($SQLDBQueryResultDetails.ModelList.ProductID -inotcontains $SQLDBQueryResult.ProductID) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add product ID `"$($SQLDBQueryResult.ProductID)`" [Model: $($SQLDBQueryResult.SystemProductName)] manufactured by `"$($SQLDBQueryResultDetails.Name)`" to the model list. Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $SQLDBQueryResultDetails.ModelList.Add($SQLDBQueryResult) + } + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Manufacturer `"$($SQLDBQueryResult.SystemManufacturer)`" is currently unsupported." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The SQL query file does not exist. [Path: $($ModelListConfigurationDetails.ModelListQueryPath.FullName)]" + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Unable to contact the specified SQL database server. [Server: $($SQLDatabaseFQDN)]" + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + + $XMLWriterSettings = New-Object -TypeName 'System.XML.XMLWriterSettings' + $XMLWriterSettings.Indent = $True + $XMLWriterSettings.IndentChars = "`t" * 1 + $XMLWriterSettings.Encoding = [System.Text.Encoding]::Default + $XMLWriterSettings.NewLineHandling = [System.XML.NewLineHandling]::None + $XMLWriterSettings.ConformanceLevel = [System.XML.ConformanceLevel]::Auto + + $XMLStringBuilder = New-Object -TypeName 'System.Text.StringBuilder' + + $XMLWriter = [System.XML.XMLTextWriter]::Create($XMLStringBuilder, $XMLWritersettings) + + [ScriptBlock]$AddXMLWriterNewLine = {$XMLWriter.WriteWhitespace(("`r`n" * 2))} + + $XMLWriter.WriteStartDocument() + + $AddXMLWriterNewLine.Invoke() + + $XMLWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl'") + + $AddXMLWriterNewLine.Invoke() + + $XMLWriter.WriteComment(@' +Please specify the configuration settings that will be used by the driver package creation powershell script. +'@) + + $AddXMLWriterNewLine.Invoke() + + $XMLWriter.WriteComment(@' + + +By default, the settings XML will be automatically modified by the 'Invoke-DriverPackageCreation' script to include the model you execute the script on, but only if it matches one of the manufacturers within the XML. + +To add a model manually, copy and paste the following code into the Powershell ISE on the specific model you want to add to this XML. + +The text will be automatically copied to the clipboard. + +Add the model under the desired manufacturer in order to the entry to be processed correctly. + +### Begin Code Snippet ### + $PropertyList = New-Object -TypeName 'System.Collections.Generic.List[Object]' + $PropertyList.Add(@{Name = 'Enabled'; Expression = {$True}}) + $PropertyList.Add('SystemProductName') + $PropertyList.Add(@{Name = 'ProductID'; Expression = {$_.BaseboardProduct}}) + $PropertyList.Add('BaseboardProduct') + $PropertyList.Add('SystemSKU') + $PropertyList.Add('SystemVersion') + $PropertyList.Add('SystemFamily') + $PropertyList.Add('SystemManufacturer') + + $MSSystemInformation = Get-CIMInstance -Namespace "root\WMI" -Class "MS_SystemInformation" | Select-Object -Property ($PropertyList) + + $XMLAttributes = $MSSystemInformation.PSObject.Properties | ForEach-Object {"$($_.Name)=`"$($_.Value)`""} + + $XMLNode = "" + + Write-Output -InputObject ($XMLNode) + + $Null = $XMLNode | Set-Clipboard -Verbose +### End Code Snippet ### + + + ### Copy and paste the generated text from the clipboard in between the 'ModelList' section for the desired manufacturer ### + + +As an example, the following details are available from the following powershell command + +Get-CIMInstance -Namespace 'Root\WMI' -Class 'MS_SystemInformation' + +BaseBoardManufacturer : Dell Inc. +BaseBoardProduct : 01Y2TP +BaseBoardVersion : A00 +BiosMajorRelease : 1 +BiosMinorRelease : 6 +BIOSReleaseDate : 09/05/2022 +BIOSVendor : Dell Inc. +BIOSVersion : 1.6.1 +ECFirmwareMajorRelease : 255 +ECFirmwareMinorRelease : 255 +InstanceName : ROOT\mssmbios\0000_0 +SystemFamily : Latitude +SystemManufacturer : Dell Inc. +SystemProductName : Latitude 5430 +SystemSKU : 0B04 +SystemVersion : + + +'@) + + $AddXMLWriterNewLine.Invoke() + + $XMLWriter.WriteStartElement('Settings') + + $XMLWriter.WriteElementString('GeneratedBy', [System.Security.Principal.WindowsIdentity]::GetCurrent().Name) + $XMLWriter.WriteElementString('GeneratedOn', $Env:ComputerName.ToUpper()) + $XMLWriter.WriteElementString('GeneratedDate', $GetXMLDateCreated.InvokeReturnAsIs()) + + $XMLWriter.WriteStartElement('OperatingSystemList') + + $OperatingSystemList = Get-WindowsReleaseHistory | Sort-Object -Property @('Name') -Unique + + ForEach ($OperatingSystem In $OperatingSystemList) + { + $XMLWriter.WriteStartElement('OperatingSystem') + + $XMLWriter.WriteAttributeString('Enabled', $True) + $XMLWriter.WriteAttributeString('Vendor', $OperatingSystem.Vendor) + $XMLWriter.WriteAttributeString('Name', $OperatingSystem.Name) + $XMLWriter.WriteAttributeString('NameExpression', ".*$([Regex]::Match($OperatingSystem.Name, '\d+').Value).*") + $XMLWriter.WriteAttributeString('ArchitectureExpression', '.*64.*') + $XMLWriter.WriteAttributeString('ReleaseExpression', '.*') + $XMLWriter.WriteAttributeString('LatestReleaseOnly', $True) + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteStartElement('ManufacturerList') + + $ManufacturerListGroups = $ManufacturerList | Group-Object -Property {$_.Name} + + ForEach ($ManufacturerListGroup In $ManufacturerListGroups) + { + $ManufacturerListGroupDetails = $ManufacturerListGroup.Group + + $XMLWriter.WriteStartElement('Manufacturer') + + $XMLWriter.WriteAttributeString('Enabled', $ManufacturerListGroupDetails.Enabled) + $XMLWriter.WriteAttributeString('Name', $ManufacturerListGroupDetails.Name) + $XMLWriter.WriteAttributeString('EligibilityExpression', $ManufacturerListGroupDetails.EligibilityExpression) + $XMLWriter.WriteAttributeString('ProductIDPropertyName', $ManufacturerListGroupDetails.ProductIDPropertyName) + + $XMLWriter.WriteStartElement('URLs') + + $XMLWriter.WriteAttributeString('DriverPackCatalog', $ManufacturerListGroupDetails.URLs.DriverPackCatalog) + $XMLWriter.WriteAttributeString('DownloadBase', $ManufacturerListGroupDetails.URLs.DownloadBase) + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteStartElement('ModelList') + + ForEach ($Model In $ManufacturerListGroupDetails.ModelList) + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add a XML node for product ID `"$($Model.ProductID)`" [Model: $($Model.SystemProductName)] manufactured by `"$($ManufacturerListGroupDetails.Name)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $XMLWriter.WriteStartElement('Model') + + $XMLWriter.WriteAttributeString('Enabled', $Model.Enabled) + $XMLWriter.WriteAttributeString('SystemProductName', $Model.SystemProductName) + $XMLWriter.WriteAttributeString('ProductID', $Model.ProductID) + $XMLWriter.WriteAttributeString('BaseboardProduct', $Model.BaseboardProduct) + $XMLWriter.WriteAttributeString('SystemSKU', $Model.SystemSKU) + $XMLWriter.WriteAttributeString('SystemVersion', $Model.SystemVersion) + $XMLWriter.WriteAttributeString('SystemFamily', $Model.SystemFamily) + $XMLWriter.WriteAttributeString('SystemManufacturer', $Model.SystemManufacturer) + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndDocument() + + $XMLWriter.Flush() + + $XMLWriter.Close() + + $DriverPackageCreationXMLContents = $XMLStringBuilder.ToString() + + [System.IO.FileInfo]$DriverPackageCreationXMLExportPath = "$($ContentDirectory.FullName)\Templates\SettingsTemplate.xml" + + [Scriptblock]$ExportDriverPackageCreationXML = { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to export the driver package settings XML to `"$($DriverPackageCreationXMLExportPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + If ([System.IO.Directory]::Exists($DriverPackageCreationXMLExportPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DriverPackageCreationXMLExportPath.Directory.FullName)} + + $Null = [System.IO.File]::WriteAllText($DriverPackageCreationXMLExportPath.FullName, $DriverPackageCreationXMLContents, $XMLWriterSettings.Encoding) + } + + Switch ([System.IO.File]::Exists($DriverPackageCreationXMLExportPath.FullName)) + { + {($_ -eq $True)} + { + $MemoryStream = New-Object -TypeName 'System.IO.MemoryStream' + $StreamWriter = New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList ($MemoryStream) + $Null = $StreamWriter.Write($DriverPackageCreationXMLContents) + $Null = $StreamWriter.Flush() + $Null = $MemoryStream.Position = 0 + + $DriverPackageCreationXMLContentHash = Get-FileHash -InputStream $MemoryStream -Algorithm ($HashAlgorithm) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - New Driver Package Settings XML Hash: $($DriverPackageCreationXMLContentHash.Hash)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageCreationXMLExportPathHash = Get-FileHash -Path ($DriverPackageCreationXMLExportPath.FullName) -Algorithm ($HashAlgorithm) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Current Driver Package Settings XML Hash: $($DriverPackageCreationXMLExportPathHash.Hash)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackageCreationXMLContentHash.Hash -ne $DriverPackageCreationXMLExportPathHash.Hash) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The XML metadata file `"$($DriverPackageCreationXMLExportPath.FullName)`" requires an update." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $Null = $ExportDriverPackageCreationXML.InvokeReturnAsIs() + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The XML metadata file `"$($DriverPackageCreationXMLExportPath.FullName)`" does not require an update." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + + Default + { + $Null = $ExportDriverPackageCreationXML.InvokeReturnAsIs() + } + } + #endregion + + $Script:LASTEXITCODE = $TerminationCodes.Success[0] + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + Try + { + #Determine the date and time the function completed execution + $ScriptEndTime = (Get-Date) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution of `"$($CmdletName)`" ended on $($ScriptEndTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + #Log the total script execution time + $ScriptExecutionTimespan = New-TimeSpan -Start ($ScriptStartTime) -End ($ScriptEndTime) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution took $($ScriptExecutionTimespan.Hours.ToString()) hour(s), $($ScriptExecutionTimespan.Minutes.ToString()) minute(s), $($ScriptExecutionTimespan.Seconds.ToString()) second(s), and $($ScriptExecutionTimespan.Milliseconds.ToString()) millisecond(s)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Exiting script `"$($ScriptPath.FullName)`" with exit code $($Script:LASTEXITCODE)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Stop-Transcript + } + Catch + { + + } + } + } \ No newline at end of file diff --git a/Invoke-ConfigurationGenerator.vbs b/Invoke-ConfigurationGenerator.vbs new file mode 100644 index 0000000..2190820 --- /dev/null +++ b/Invoke-ConfigurationGenerator.vbs @@ -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 \ No newline at end of file diff --git a/Invoke-DriverPackageCreator.ps1 b/Invoke-DriverPackageCreator.ps1 new file mode 100644 index 0000000..050928f --- /dev/null +++ b/Invoke-DriverPackageCreator.ps1 @@ -0,0 +1,1974 @@ +#Requires -Version 3 + +<# + .SYNOPSIS + A brief overview of what your function does + + .DESCRIPTION + Slightly more detailed description of what your function does + + .PARAMETER LogDir + A valid folder path. If the folder does not exist, it will be created. This parameter can also be specified by the alias "LogPath". + + .PARAMETER ContinueOnError + Ignore failures. + + .EXAMPLE + Use this command to execute a VBSCript that will launch this powershell script automatically with the specified parameters. This is useful to avoid powershell execution complexities. + + cscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + wscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "%FolderPathContainingScript%\%ScriptName%.ps1" -SwitchParameter -ScriptParameter "%ScriptParameterValue%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command "& '%FolderPathContainingScript%\%ScriptName%.ps1' -ScriptParameter1 '%ScriptParameter1Value%' -ScriptParameter2 %ScriptParameter2Value% -SwitchParameter" + + .NOTES + Any useful tidbits + + .LINK + Place any useful link here where your function or cmdlet can be referenced +#> + +[CmdletBinding(SupportsShouldProcess=$True)] + Param + ( + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [ValidateScript({Test-Path -Path $_})] + [Alias('SP')] + [System.IO.FileInfo]$SettingsPath, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('CatalogRoot', 'CD', 'CDR')] + [System.IO.DirectoryInfo]$CatalogDirectory, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('DLD')] + [System.IO.DirectoryInfo]$DownloadDirectory, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('DPD')] + [System.IO.DirectoryInfo]$DriverPackageDirectory, + + [Parameter(Mandatory=$False)] + [Alias('DD', 'DDL')] + [Switch]$DisableDownload, + + [Parameter(Mandatory=$False)] + [Alias('ERI')] + [Switch]$EnableRobocopyIPG, + + [Parameter(Mandatory=$False)] + [Alias('F')] + [Switch]$Force, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('LogDir', 'LogPath')] + [System.IO.DirectoryInfo]$LogDirectory, + + [Parameter(Mandatory=$False)] + [Switch]$ContinueOnError + ) + +Function Get-AdministrativePrivilege + { + $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity) + Write-Output -InputObject ($Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) + } + +If ((Get-AdministrativePrivilege) -eq $False) + { + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Path)" + + $ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $ArgumentList.Add('-ExecutionPolicy Bypass') + $ArgumentList.Add('-NoProfile') + $ArgumentList.Add('-NoExit') + $ArgumentList.Add('-NoLogo') + $ArgumentList.Add("-File `"$($ScriptPath.FullName)`"") + + $Null = Start-Process -FilePath "$([System.Environment]::SystemDirectory)\WindowsPowershell\v1.0\powershell.exe" -WorkingDirectory "$([System.Environment]::SystemDirectory)" -ArgumentList ($ArgumentList.ToArray()) -WindowStyle Normal -Verb RunAs -PassThru + } +Else + { + #Determine the date and time we executed the function + $ScriptStartTime = (Get-Date) + + #Define Default Action Preferences + $Script:DebugPreference = 'SilentlyContinue' + $Script:ErrorActionPreference = 'Stop' + $Script:VerbosePreference = 'SilentlyContinue' + $Script:WarningPreference = 'Continue' + $Script:ConfirmPreference = 'None' + $Script:WhatIfPreference = $False + + #Load WMI Classes + $Baseboard = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Baseboard" -Property * + $Bios = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Bios" -Property * + $ComputerSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_ComputerSystem" -Property * + $OperatingSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_OperatingSystem" -Property * + $MSSystemInformation = Get-WmiObject -Namespace "root\WMI" -Class "MS_SystemInformation" -Property * + + #Retrieve property values + $OSArchitecture = $($OperatingSystem.OSArchitecture).Replace("-bit", "").Replace("32", "86").Insert(0,"x").ToUpper() + + #Define variable(s) + $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)} + $DateTimeXMLFormat = 'yyyy-MM-ddTHH:mm:ss' ###2022-10-24T12:45:15### + [ScriptBlock]$GetCurrentDateTimeXMLFormat = {(Get-Date).ToString($DateTimeXMLFormat)} + $DateDriverPackReleaseFormat = 'yyyy-MM-dd' ###2019-04-03### + [ScriptBlock]$GetCurrentDateDriverPackReleaseFormat = {(Get-Date).ToString($DateDriverPackReleaseFormat)} + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Definition)" + [System.IO.DirectoryInfo]$ScriptDirectory = "$($ScriptPath.Directory.FullName)" + [System.IO.DirectoryInfo]$ContentDirectory = "$($ScriptDirectory.FullName)\Content" + [System.IO.DirectoryInfo]$FunctionsDirectory = "$($ScriptDirectory.FullName)\Functions" + [System.IO.DirectoryInfo]$ModulesDirectory = "$($ScriptDirectory.FullName)\Modules" + [System.IO.DirectoryInfo]$ToolsDirectory = "$($ScriptDirectory.FullName)\Tools" + [System.IO.DirectoryInfo]$ToolsDirectory_OSAll = "$($ToolsDirectory.FullName)\All" + [System.IO.DirectoryInfo]$ToolsDirectory_OSArchSpecific = "$($ToolsDirectory.FullName)\$($OSArchitecture)" + [System.IO.DirectoryInfo]$System32Directory = [System.Environment]::SystemDirectory + [System.IO.DirectoryInfo]$ProgramFilesDirectory = "$($Env:SystemDrive)\Program Files" + [System.IO.DirectoryInfo]$ProgramFilesx86Directory = "$($Env:SystemDrive)\Program Files (x86)" + [System.IO.FileInfo]$PowershellPath = "$($System32Directory.FullName)\WindowsPowershell\v1.0\powershell.exe" + [System.IO.DirectoryInfo]$System32Directory = "$([System.Environment]::SystemDirectory)" + $IsWindowsPE = Test-Path -Path 'HKLM:\SYSTEM\ControlSet001\Control\MiniNT' -ErrorAction SilentlyContinue + [System.Text.RegularExpressions.RegexOptions[]]$RegexOptions = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase, [System.Text.RegularExpressions.RegexOptions]::Multiline + [ScriptBlock]$GetRandomGUID = {[System.GUID]::NewGUID().GUID.ToString().ToUpper()} + [String]$ParameterSetName = "$($PSCmdlet.ParameterSetName)" + $TextInfo = (Get-Culture).TextInfo + $Script:LASTEXITCODE = 0 + $TerminationCodes = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $TerminationCodes.Add('Success', @(0)) + $TerminationCodes.Add('Warning', @(5000..5999)) + $TerminationCodes.Add('Error', @(6000..6999)) + $Script:WarningCodeIndex = 0 + [ScriptBlock]$GetAvailableWarningCode = {$TerminationCodes.Warning[$Script:WarningCodeIndex]; $Script:WarningCodeIndex++} + $Script:ErrorCodeIndex = 0 + [ScriptBlock]$GetAvailableErrorCode = {$TerminationCodes.Error[$Script:ErrorCodeIndex]; $Script:ErrorCodeIndex++} + $LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $LoggingDetails.Add('LogMessage', $Null) + $LoggingDetails.Add('WarningMessage', $Null) + $LoggingDetails.Add('ErrorMessage', $Null) + $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] + $CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters) + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters) + [System.IO.FileInfo]$7ZipPath = "$($ToolsDirectory_OSArchSpecific.FullName)\7z.exe" + [String]$HashAlgorithm = 'SHA256' + + #Define the error handling definition + [ScriptBlock]$ErrorHandlingDefinition = { + If (($Null -ieq $Script:LASTEXITCODE) -or ($Script:LASTEXITCODE -eq 0)) + { + [Int]$Script:LASTEXITCODE = $GetAvailableErrorCode.InvokeReturnAsIs() + } + + $ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ErrorMessageList.Add('Message', $_.Exception.Message) + $ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID) + $ErrorMessageList.Add('ExitCode', $Script:LASTEXITCODE) + $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 + } + + Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False)) + { + {($_ -eq $True)} + { + Throw + } + } + } + + #Determine default parameter value(s) + [System.IO.DirectoryInfo]$ApplicationDataRootDirectory = "$($Env:ProgramData)\$($ScriptPath.BaseName)" + + [System.IO.DirectoryInfo]$StagingDirectory = "$($Env:Windir)\Temp\$($ScriptPath.BaseName)" + + [System.IO.DirectoryInfo]$LocalDriverPackageDirectory = "$($StagingDirectory.FullName)\Packages" + + Switch ($True) + { + {([String]::IsNullOrEmpty($SettingsPath) -eq $True) -or ([String]::IsNullOrWhiteSpace($SettingsPath) -eq $True)} + { + [System.IO.FileInfo]$SettingsTemplate = "$($ContentDirectory.FullName)\Templates\SettingsTemplate.xml" + + [System.IO.FileInfo]$SettingsPath = "$($ApplicationDataRootDirectory.FullName)\Settings\$($ScriptPath.BaseName.Split('-')[1])Settings.xml" + + Switch ([System.IO.File]::Exists($SettingsPath.FullName)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The settings XML configuration file `"$($SettingsPath.FullName)`" already exists." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + {($_ -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to create settings XML configuration file `"$($SettingsPath.FullName)`" from the settings XML template file `"$($SettingsTemplate.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ([System.IO.Directory]::Exists($SettingsPath.Directory.FullName)) + { + {($_ -eq $False)} + { + $Null = [System.IO.Directory]::CreateDirectory($SettingsPath.Directory.FullName) + } + } + + $Null = Copy-Item -Path ($SettingsTemplate.FullName) -Destination ($SettingsPath.FullName) -Force + } + } + } + + {([String]::IsNullOrEmpty($CatalogDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($CatalogDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$CatalogDirectory = "$($ApplicationDataRootDirectory.FullName)\Catalogs" + } + + {([String]::IsNullOrEmpty($DownloadDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($DownloadDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$DownloadDirectory = "$($StagingDirectory.FullName)\Downloads" + } + + {([String]::IsNullOrEmpty($DriverPackageDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($DriverPackageDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$DriverPackageDirectory = "$($ApplicationDataRootDirectory.FullName)\Out-Of-Box-Driver-Packages" + } + + {([String]::IsNullOrEmpty($LogDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($LogDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$LogDirectory = "$($ApplicationDataRootDirectory.FullName)\Logs" + } + } + + #Start transcripting (Logging) + [System.IO.FileInfo]$ScriptLogPath = "$($LogDirectory.FullName)\$($ScriptPath.BaseName)_$($GetCurrentDateFileFormat.Invoke()).log" + If ($ScriptLogPath.Directory.Exists -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($ScriptLogPath.Directory.FullName)} + Start-Transcript -Path "$($ScriptLogPath.FullName)" -Force -WhatIf:$False + + #Log any useful information + [String]$CmdletName = $MyInvocation.MyCommand.Name + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of script `"$($CmdletName)`" began on $($ScriptStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script Path = $($ScriptPath.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [String[]]$AvailableScriptParameters = (Get-Command -Name ($ScriptPath.FullName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"} + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Script 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 Script Parameter(s) = $($SuppliedScriptParameters -Join ', ')" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($True) + { + {([String]::IsNullOrEmpty($ParameterSetName) -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Parameter Set Name = $($ParameterSetName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Command Line: $((Get-WMIObject -Namespace 'Root\CIMv2' -Class 'Win32_Process' -Filter "ProcessID = $($PID)").CommandLine)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($PSBoundParameters.Count) command line parameter(s) were specified." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $OperatingSystemDetailsTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $OperatingSystemDetailsTable.ProductName = $OperatingSystem.Caption -ireplace '(Microsoft\s+)', '' + $OperatingSystemDetailsTable.Version = $OperatingSystem.Version + $OperatingSystemDetailsTable.Architecture = $OperatingSystem.OSArchitecture + + $OperatingSystemRegistryDetails = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = ''; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'UBR'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseVersion'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'ReleaseID'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseID'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'DisplayVersion'; Value = $Null})) + + ForEach ($OperatingSystemRegistryDetail In $OperatingSystemRegistryDetails) + { + $OperatingSystemRegistryDetail.Value = Try {(Get-Item -Path $OperatingSystemRegistryDetail.Path).GetValue($OperatingSystemRegistryDetail.ValueName)} Catch {} + + :NextOSDetail Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Value) -eq $False)) + { + {($_ -eq $True)} + { + Switch ($OperatingSystemRegistryDetail.ValueName) + { + {($_ -ieq 'UBR')} + { + $OperatingSystemDetailsTable.Version = $OperatingSystemDetailsTable.Version + '.' + $OperatingSystemRegistryDetail.Value + + Break NextOSDetail + } + } + + Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Alias) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Alias) -eq $False)) + { + {($_ -eq $True)} + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.Alias) = $OperatingSystemRegistryDetail.Value + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + ForEach ($OperatingSystemDetail In $OperatingSystemDetailsTable.GetEnumerator()) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($OperatingSystemDetail.Key): $($OperatingSystemDetail.Value)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Powershell Version: $($PSVersionTable.PSVersion.ToString())" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ExecutionPolicyList = Get-ExecutionPolicy -List + + For ($ExecutionPolicyListIndex = 0; $ExecutionPolicyListIndex -lt $ExecutionPolicyList.Count; $ExecutionPolicyListIndex++) + { + $ExecutionPolicy = $ExecutionPolicyList[$ExecutionPolicyListIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The powershell execution policy is currently set to `"$($ExecutionPolicy.ExecutionPolicy.ToString())`" for the `"$($ExecutionPolicy.Scope.ToString())`" scope." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + #Log hardware information + $MSSystemInformationMembers = $MSSystemInformation.PSObject.Properties | Where-Object {($_.MemberType -imatch '^NoteProperty$|^Property$') -and ($_.Name -imatch '^Base.*|Bios.*|System.*$') -and ($_.Name -inotmatch '^.*Major.*|.*Minor.*|.*Properties.*$')} | Sort-Object -Property @('Name') + + Switch ($MSSystemInformationMembers.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to display device information properties from the `"$($MSSystemInformation.__CLASS)`" WMI class located within the `"$($MSSystemInformation.__NAMESPACE)`" WMI namespace. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $MSSystemInformationTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + + ForEach ($MSSystemInformationMember In $MSSystemInformationMembers) + { + [String]$MSSystemInformationMemberName = ($MSSystemInformationMember.Name) + [String]$MSSystemInformationMemberValue = $MSSystemInformation.$($MSSystemInformationMemberName) + + $MSSystemInformationTable.$($MSSystemInformationMemberName) = $MSSystemInformationMemberValue + + Switch ([String]::IsNullOrEmpty($MSSystemInformationMemberValue)) + { + {($_ -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($MSSystemInformationMemberName) = $($MSSystemInformationMemberValue)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The `"MSSystemInformation`" WMI class could not be found." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #region Log Cleanup + [Int]$MaximumLogHistory = 3 + + $LogList = Get-ChildItem -Path ($LogDirectory.FullName) -Filter "$($ScriptPath.BaseName)_*" -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $SortedLogList = $LogList | Sort-Object -Property @('LastWriteTime') -Descending | Select-Object -Skip ($MaximumLogHistory) + + Switch ($SortedLogList.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + For ($SortedLogListIndex = 0; $SortedLogListIndex -lt $SortedLogList.Count; $SortedLogListIndex++) + { + Try + { + $Log = $SortedLogList[$SortedLogListIndex] + + $LogAge = New-TimeSpan -Start ($Log.LastWriteTime) -End (Get-Date) + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to cleanup log file `"$($Log.FullName)`". Please Wait... [Last Modified: $($Log.LastWriteTime.ToString($DateTimeMessageFormat))] [Age: $($LogAge.Days.ToString()) day(s); $($LogAge.Hours.ToString()) hours(s); $($LogAge.Minutes.ToString()) minute(s); $($LogAge.Seconds.ToString()) second(s)]." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $Null = [System.IO.File]::Delete($Log.FullName) + } + Catch + { + + } + } + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + #endregion + + #region Import Dependency Modules + If (($ModulesDirectory.Exists -eq $True) -and ($ModulesDirectory.GetDirectories().Count -gt 0)) + { + $Modules = Get-Module -Name "$($ModulesDirectory.FullName)\*" -ListAvailable -ErrorAction Stop + + $ModuleGroups = $Modules | Group-Object -Property @('Name') + + ForEach ($ModuleGroup In $ModuleGroups) + { + $LatestModuleVersion = $ModuleGroup.Group | Sort-Object -Property @('Version') -Descending | Select-Object -First 1 + + If ($Null -ine $LatestModuleVersion) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to import dependency powershell module `"$($LatestModuleVersion.Name)`" [Version: $($LatestModuleVersion.Version.ToString())]. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Try {$Null = Import-Module -Name "$($LatestModuleVersion.Path)" -Global -DisableNameChecking -Force -Verbose:$False} Catch {} + } + } + } + #endregion + + #region Dot Source Dependency Scripts + #Dot source any additional script(s) from the functions directory. This will provide flexibility to add additional functions without adding complexity to the main script and to maintain function consistency. + Try + { + If ($FunctionsDirectory.Exists -eq $True) + { + $AdditionalFunctionsFilter = New-Object -TypeName 'System.Collections.Generic.List[String]' + $AdditionalFunctionsFilter.Add('*.ps1') + + $AdditionalFunctionsToImport = Get-ChildItem -Path "$($FunctionsDirectory.FullName)" -Include ($AdditionalFunctionsFilter) -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $AdditionalFunctionsToImportCount = $AdditionalFunctionsToImport | Measure-Object | Select-Object -ExpandProperty Count + + If ($AdditionalFunctionsToImportCount -gt 0) + { + ForEach ($AdditionalFunctionToImport In $AdditionalFunctionsToImport) + { + Try + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to dot source the functions contained within the dependency script `"$($AdditionalFunctionToImport.Name)`". Please Wait... [Script Path: `"$($AdditionalFunctionToImport.FullName)`"]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + . "$($AdditionalFunctionToImport.FullName)" + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + } + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + #endregion + + #region Load any required libraries + [System.IO.DirectoryInfo]$LibariesDirectory = "$($FunctionsDirectory.FullName)\Libraries" + + Switch ([System.IO.Directory]::Exists($LibariesDirectory.FullName)) + { + {($_ -eq $True)} + { + $LibraryPatternList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $LibraryPatternList.Add('AlphaFS.dll') + + 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) -Verbose + + $Null = [System.Reflection.Assembly]::Load($LibraryBytes) + } + } + } + } + } + } + } + #endregion + + #Perform script action(s) + Try + { + $ProcessExecutionTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ProcessExecutionTable.CurrentProcessToken = [Security.Principal.WindowsIdentity]::GetCurrent() -As [Security.Principal.WindowsIdentity] + $ProcessExecutionTable.CurrentProcessSID = $ProcessExecutionTable.CurrentProcessToken.User -As [Security.Principal.SecurityIdentifier] + $ProcessExecutionTable.ProcessNTAccount = $ProcessExecutionTable.CurrentProcessToken.Name -As [String] + $ProcessExecutionTable.ProcessNTAccountSID = $ProcessExecutionTable.CurrentProcessSID.Value -As [String] + $ProcessExecutionTable.IsAdmin = ($ProcessExecutionTable.CurrentProcessToken.Groups -contains [Security.Principal.SecurityIdentifier]'S-1-5-32-544') -As [Boolean] + $ProcessExecutionTable.IsLocalSystemAccount = $ProcessExecutionTable.CurrentProcessSID.IsWellKnown([Security.Principal.WellKnownSidType]'LocalSystemSid') -As [Boolean] + $ProcessExecutionTable.IsLocalServiceAccount = $ProcessExecutionTable.CurrentProcessSID.IsWellKnown([Security.Principal.WellKnownSidType]'LocalServiceSid') -As [Boolean] + $ProcessExecutionTable.IsNetworkServiceAccount = $ProcessExecutionTable.CurrentProcessSID.IsWellKnown([Security.Principal.WellKnownSidType]'NetworkServiceSid') -As [Boolean] + $ProcessExecutionTable.IsServiceAccount = ($ProcessExecutionTable.CurrentProcessToken.Groups -contains [Security.Principal.SecurityIdentifier]'S-1-5-6') -As [Boolean] + $ProcessExecutionTable.IsProcessUserInteractive = [System.Environment]::UserInteractive -As [Boolean] + $ProcessExecutionTable.LocalSystemNTAccount = (New-Object -TypeName 'System.Security.Principal.SecurityIdentifier' -ArgumentList ([Security.Principal.WellKnownSidType]::'LocalSystemSid', $Null)).Translate([Security.Principal.NTAccount]).Value -As [String] + $ProcessExecutionTable.LocalUsersGroup = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList ([Security.Principal.WellKnownSidType]::'BuiltinUsersSid', $Null)).Translate([System.Security.Principal.NTAccount]).Value -As [String] + $ProcessExecutionTable.LocalAdministratorsGroup = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList ([Security.Principal.WellKnownSidType]::'BuiltinAdministratorsSid', $Null)).Translate([System.Security.Principal.NTAccount]).Value -As [String] + $ProcessExecutionTable.IsSessionZero = If (($ProcessExecutionTable.IsLocalSystemAccount -eq $True) -or ($ProcessExecutionTable.IsLocalServiceAccount -eq $True) -or ($ProcessExecutionTable.IsNetworkServiceAccount -eq $True) -or ($ProcessExecutionTable.IsServiceAccount -eq $True)) {$True} Else {$False} + $ProcessExecutionTable.Is64BitOperatingSystem = [System.Environment]::Is64BitOperatingSystem -As [Boolean] + $ProcessExecutionTable.Is64BitProcess = [System.Environment]::Is64BitProcess -As [Boolean] + $ProcessExecutionTable.CommandLine = [System.Environment]::CommandLine -As [String] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Settings Path: $($SettingsPath.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Script:SettingsTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $Script:SettingsTable.XMLModificationCount = 0 + $Script:SettingsTable.GetXMLDateAdded = {(Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss')} + + [ScriptBlock]$ReadXMLContent = { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to load the configuration settings from `"$($SettingsPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Script:SettingsXMLContent = [System.IO.File]::ReadAllText($SettingsPath.FullName) + + $Script:SettingsXMLObject = New-Object -TypeName 'System.Xml.XmlDocument' + $Script:SettingsXMLObject.PreserveWhitespace = $True + + $Null = $Script:SettingsXMLObject.LoadXml($Script:SettingsXMLContent) + + $Script:SettingsTable.OperatingSytemList = $Script:SettingsXMLObject.SelectNodes('/Settings/OperatingSystemList//OperatingSystem') | Where-Object {($_.Enabled -eq $True)} + $Script:SettingsTable.ManufacturerList = $Script:SettingsXMLObject.SelectNodes('/Settings/ManufacturerList//Manufacturer') + } + + $Null = $ReadXMLContent.InvokeReturnAsIs() + + $WindowsReleaseHistory = Get-WindowsReleaseHistory -Verbose + + $DriverPackDownloadList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + ForEach ($Manufacturer In $SettingsTable.ManufacturerList) + { + Try + { + Switch ($Manufacturer.Enabled) + { + {($_ -eq $True)} + { + $ManufacturerSettingsTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ManufacturerSettingsTable.ModelList = ($Script:SettingsTable.ManufacturerList | Where-Object {($_.Name -ieq $Manufacturer.Name)}).ModelList.Model + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process the driver pack catalog for `"$($Manufacturer.Name)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $DownloadDriverPackCatalog = Invoke-FileDownload -URL ($Manufacturer.URLs.DriverPackCatalog) -Destination "$($CatalogDirectory.FullName)\$($Manufacturer.Name)" -Verbose + + Switch ($DownloadDriverPackCatalog.DownloadPath.Extension -imatch '\.(cab)') + { + {($_ -eq $True)} + { + [System.IO.FileInfo]$DriverPackCatalogExtractionPath = "$($DownloadDriverPackCatalog.DownloadPath.Directory.FullName)\$($DownloadDriverPackCatalog.DownloadPath.BaseName).xml" + + Switch (($DownloadDriverPackCatalog.DownloadRequired -eq $True) -or ([System.IO.File]::Exists($DriverPackCatalogExtractionPath.FullName) -eq $False)) + { + {($_ -eq $True)} + { + $Null = Start-ProcessWithOutput -FilePath "$($System32Directory.FullName)\expand.exe" -ArgumentList "`"$($DownloadDriverPackCatalog.DownloadPath.FullName)`" `"$($DriverPackCatalogExtractionPath.FullName)`"" -AcceptableExitCodeList @('0') -CreateNoWindow -Verbose + } + } + } + } + + $DriverPackCatalogPath = Get-ChildItem -Path ($DownloadDriverPackCatalog.DownloadPath.Directory.FullName) -Filter '*.xml' -Force | Where-Object {($_ -is [System.IO.FileInfo])} | Sort-Object -Property @('LastWriteTime') -Descending | Select-Object -First 1 + + $DriverPackCatalogContents = [System.IO.File]::ReadAllText($DriverPackCatalogPath.FullName) + + $DriverPackCatalog = New-Object -TypeName 'System.Xml.XmlDocument' + $DriverPackCatalog.PreserveWhitespace = $True + $DriverPackCatalog.LoadXml($DriverPackCatalogContents) + + ForEach ($Model In $ManufacturerSettingsTable.ModelList) + { + Switch ($Model.Enabled) + { + {($_ -eq $True)} + { + ForEach ($OperatingSystemEntry In $Script:SettingsTable.OperatingSytemList) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to search the `"$($Manufacturer.Name)`" driver pack catalog for `"$($OperatingSystemEntry.Name)`" drivers for model `"$($Model.BaseboardProduct)`" [Manufacturer: $($Model.SystemManufacturer)] [Model: $($model.SystemProductName)] [Alias: $($Model.SystemVersion)] [SKU: $($Model.SystemSKU)]. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $OperatingSystemEntryReleaseHistory = $WindowsReleaseHistory | Where-Object {($_.Name -imatch $OperatingSystemEntry.NameExpression)} + + $OperatingSystemEntryVersion = $OperatingSystemEntryReleaseHistory[0].Version + + Switch ($Manufacturer.Name) + { + {($_ -iin @('Dell'))} + { + $DriverPackSearchResults = $DriverPackCatalog.DriverPackManifest.DriverPackage | Where-Object {($_.Type -inotmatch '(^.*WinPE.*$)') -and ($_.SupportedSystems.Brand.Model.SystemID -ieq $Model.ProductID) -and ($_.SupportedOperatingSystems.OperatingSystem.OSCode -imatch $OperatingSystemEntry.NameExpression) -and ($_.SupportedOperatingSystems.OperatingSystem.OSArch -imatch $OperatingSystemEntry.ArchitectureExpression)} + + $DriverPackSearchResultCount = ($DriverPackSearchResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchResultCount) result(s) were returned from the driver pack search." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($OperatingSystemEntry.LatestReleaseOnly) + { + {($_ -eq $True)} + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults | Sort-Object {(Get-Date -Date ($_.DateTime))} -Descending | Select-Object -First 1 + } + + Default + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults | Sort-Object {(Get-Date -Date ($_.DateTime))} -Descending + } + } + + $DriverPackSearchFilteredResultCount = ($DriverPackSearchFilteredResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchFilteredResultCount) result(s) remain after filtering, and sorting." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackSearchFilteredResultCount -gt 0) + { + {($_ -eq $True)} + { + ForEach ($DriverPackSearchResult In $DriverPackSearchFilteredResults) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add driver pack `"$($DriverPackSearchResult.Name.Display.'#cdata-section')`" [Release: $($DriverPackSearchResult.releaseID)] [Version: $($DriverPackSearchResult.dellVersion)] released on $((Get-Date -Date $DriverPackSearchResult.DateTime).ToString($DateTimeLogFormat)). Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackDownloadProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.Enabled = $True + $DriverPackDownloadProperties.Manufacturer = $Manufacturer.Name + $DriverPackDownloadProperties.BaseboardProduct = $Model.BaseboardProduct + $DriverPackDownloadProperties.SystemFamily = $Model.SystemFamily + $DriverPackDownloadProperties.SystemManufacturer = $Model.SystemManufacturer + $DriverPackDownloadProperties.SystemProductName = $Model.SystemProductName + $DriverPackDownloadProperties.SystemSKU = $Model.SystemSKU + $DriverPackDownloadProperties.SystemVersion = $Model.SystemVersion + $DriverPackDownloadProperties.ProductIDList = $DriverPackSearchResult.SupportedSystems.Brand.Model.SystemID + $DriverPackDownloadProperties.ProductID = $DriverPackDownloadProperties.ProductIDList[0] + $DriverPackDownloadProperties.OSName = "$($OperatingSystemEntry.Name)" + $DriverPackDownloadProperties.OSAlias = "W$($OperatingSystemEntry.Name -ireplace '(\D+)', '')" + $DriverPackDownloadProperties.OSVersionMinimum = $OperatingSystemEntryVersion.ToString() + $DriverPackDownloadProperties.OSReleaseIDMinimum = "All" + $DriverPackDownloadProperties.OSArchitecture = $DriverPackSearchResult.SupportedOperatingSystems.OperatingSystem.OSArch.ToUpper() + $DriverPackDownloadProperties.DriverPackReleaseID = "$($DriverPackSearchResult.ReleaseID)" + $DriverPackDownloadProperties.DriverPackReleaseVersion = "$($DriverPackSearchResult.DellVersion)" + $DriverPackDownloadProperties.DriverPackReleaseDate = Try {(Get-Date -Date $DriverPackSearchResult.DateTime).ToString($DateDriverPackReleaseFormat)} Catch {$DriverPackSearchResult.DateTime} + $DriverPackDownloadProperties.DriverPackInfoURL = "$($DriverPackSearchResult.ImportantInfo.URL)" + $DriverPackDownloadProperties.DirectoryPath = "$($DriverPackDownloadProperties.Manufacturer)\$($DriverPackDownloadProperties.SystemSKU)\$($DriverPackDownloadProperties.OSAlias)\$($DriverPackDownloadProperties.OSArchitecture)" + $DriverPackDownloadProperties.FileBaseName = "$($DriverPackDownloadProperties.Manufacturer)-$($DriverPackDownloadProperties.SystemSKU)-$($DriverPackDownloadProperties.DriverPackReleaseID)-$($DriverPackDownloadProperties.DriverPackReleaseVersion)-$($DriverPackDownloadProperties.OSAlias)-$($DriverPackDownloadProperties.OSArchitecture)-$($DriverPackDownloadProperties.OSReleaseIDMinimum)" + + $DriverPackDownloadProperties.FileBaseName = $DriverPackDownloadProperties.FileBaseName.Split([System.IO.Path]::GetInvalidFileNameChars()) -Join '' + + $DriverPackDownloadProperties.FileName = "$($DriverPackDownloadProperties.FileBaseName).wim" + $DriverPackDownloadProperties.FilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.FileName)" + $DriverPackDownloadProperties.MetadataFileName = "$($DriverPackDownloadProperties.FileBaseName).json" + $DriverPackDownloadProperties.MetadataFilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.MetadataFileName)" + $DriverPackDownloadProperties.DownloadLinkList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.DownloadLinkList.DriverPack = "$($Manufacturer.URLs.DownloadBase)/$($DriverPackSearchResult.Path)" + $DriverPackDownloadProperties.DownloadLinkList.DriverPackMetadata = "$($Manufacturer.URLs.DownloadBase)/$($DriverPackSearchResult.DriverPackMetadataInfo.Path)" + + $DriverPackDownloadObject = New-Object -TypeName 'PSObject' -Property ($DriverPackDownloadProperties) + + $DriverPackDownloadList.Add($DriverPackDownloadObject) + } + } + } + } + + {($_ -iin @('HP'))} + { + $DriverPackSearchResults = New-Object -TypeName 'System.Collections.Generic.List[Object]' + + $CatalogSearchResults = $DriverPackCatalog.SelectNodes('/*/*/ProductOSDriverPackList/ProductOSDriverPack') | Where-Object {($_.SystemID.Split(',').Trim() -icontains $Model.ProductID) -and ($_.OSName -imatch $OperatingSystemEntry.NameExpression) -and ($_.OSName -imatch $OperatingSystemEntry.ReleaseExpression) -and ($_.Architecture -imatch $OperatingSystemEntry.ArchitectureExpression)} | Sort-Object -Property @('SoftpaqID') -Unique + + $CatalogList = $DriverPackCatalog.SelectNodes('/*/*/SoftPaqList/SoftPaq') + + ForEach ($CatalogSearchResult In $CatalogSearchResults) + { + $CatalogSearchResultItem = $CatalogList | Where-Object {($_.ID -ieq $CatalogSearchResult.SoftpaqID)} + $CatalogSearchResultItem | Add-Member -Name 'SystemModel' -Value ($CatalogSearchResult.SystemName) -MemberType NoteProperty -Force + $CatalogSearchResultItem | Add-Member -Name 'SystemIDList' -Value ($CatalogSearchResult.SystemID.Split(',').Trim()) -MemberType NoteProperty -Force + $CatalogSearchResultItem | Add-Member -Name 'OperatingSystemName' -Value ($CatalogSearchResult.OSName) -MemberType NoteProperty -Force + $CatalogSearchResultItem | Add-Member -Name 'OperatingSystemArchitecture' -Value ($CatalogSearchResult.Architecture) -MemberType NoteProperty -Force + $CatalogSearchResultItem | Add-Member -Name 'OperatingSystemID' -Value ($CatalogSearchResult.OSID) -MemberType NoteProperty -Force + + $DriverPackSearchResults.Add($CatalogSearchResultItem) + } + + $DriverPackSearchResultCount = ($DriverPackSearchResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchResultCount) result(s) were returned from the driver pack search." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($OperatingSystemEntry.LatestReleaseOnly) + { + {($_ -eq $True)} + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults | Sort-Object {(Get-Date -Date ($_.DateReleased))} -Descending | Select-Object -First 1 + } + + Default + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults | Sort-Object {(Get-Date -Date ($_.DateReleased))} -Descending + } + } + + $DriverPackSearchFilteredResultCount = ($DriverPackSearchFilteredResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchFilteredResultCount) result(s) remain after filtering, and sorting." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackSearchFilteredResultCount -gt 0) + { + {($_ -eq $True)} + { + ForEach ($DriverPackSearchResult In $DriverPackSearchFilteredResults) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add driver pack `"$($DriverPackSearchResult.Name)`" [Version: $($DriverPackSearchResult.Version)] released on $((Get-Date -Date $DriverPackSearchResult.DateReleased).ToString($DateTimeLogFormat)). Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackDownloadProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.Enabled = $True + $DriverPackDownloadProperties.Manufacturer = $Manufacturer.Name + $DriverPackDownloadProperties.BaseboardProduct = $Model.BaseboardProduct + $DriverPackDownloadProperties.SystemFamily = $Model.SystemFamily + $DriverPackDownloadProperties.SystemManufacturer = $Model.SystemManufacturer + $DriverPackDownloadProperties.SystemProductName = $Model.SystemProductName + $DriverPackDownloadProperties.SystemSKU = $Model.SystemSKU + $DriverPackDownloadProperties.SystemVersion = $Model.SystemVersion + $DriverPackDownloadProperties.ProductIDList = $DriverPackSearchResult.SystemIDList | ForEach-Object {$_.ToUpper()} + $DriverPackDownloadProperties.ProductID = $DriverPackDownloadProperties.ProductIDList[0] + $DriverPackDownloadProperties.OSName = "$($OperatingSystemEntry.Name)" + $DriverPackDownloadProperties.OSAlias = "W$($OperatingSystemEntry.Name -ireplace '(\D+)', '')" + + $DriverPackOSDetails = $OperatingSystemEntryReleaseHistory | Where-Object {($_.ReleaseID -ieq [Regex]::Match($DriverPackSearchResult.OperatingSystemName, $_.ReleaseID).Value)} | Select-Object -First 1 + + Switch ($Null -ine $DriverPackOSDetails) + { + {($_ -eq $True)} + { + $DriverPackDownloadProperties.OSVersionMinimum = $DriverPackOSDetails.Version.ToString() + + $DriverPackDownloadProperties.OSReleaseIDMinimum = $DriverPackOSDetails.ReleaseID + } + + Default + { + $DriverPackDownloadProperties.OSVersionMinimum = $OperatingSystemEntryVersion.ToString() + + $DriverPackDownloadProperties.OSReleaseIDMinimum = "All" + } + } + + $DriverPackDownloadProperties.OSArchitecture = 'NA' + + Switch ($DriverPackSearchResult.OperatingSystemArchitecture) + { + {($_ -imatch '(^.*32.*$)')} + { + $DriverPackDownloadProperties.OSArchitecture = 'X86' + } + + {($_ -imatch '(^.*64.*$)')} + { + $DriverPackDownloadProperties.OSArchitecture = 'X64' + } + } + + $DriverPackDownloadProperties.DriverPackReleaseID = "$($DriverPackSearchResult.ID.ToUpper())" + $DriverPackDownloadProperties.DriverPackReleaseVersion = "$($DriverPackSearchResult.Version)" + $DriverPackDownloadProperties.DriverPackReleaseDate = Try {(Get-Date -Date $DriverPackSearchResult.DateReleased).ToString($DateDriverPackReleaseFormat)} Catch {$DriverPackSearchResult.DateReleased} + $DriverPackDownloadProperties.DriverPackInfoURL = "$($DriverPackSearchResult.ReleaseNotesURL)" + $DriverPackDownloadProperties.DirectoryPath = "$($DriverPackDownloadProperties.Manufacturer)\$($DriverPackDownloadProperties.BaseboardProduct)\$($DriverPackDownloadProperties.OSAlias)\$($DriverPackDownloadProperties.OSArchitecture)" + + Switch ($Null -ine $DriverPackOSDetails) + { + {($_ -eq $True)} + { + $DriverPackDownloadProperties.FileBaseName = "$($DriverPackDownloadProperties.Manufacturer)-$($DriverPackDownloadProperties.BaseboardProduct)-$($DriverPackDownloadProperties.DriverPackReleaseID)-$($DriverPackDownloadProperties.OSAlias)-$($DriverPackDownloadProperties.OSArchitecture)-$($DriverPackDownloadProperties.OSReleaseIDMinimum)" + } + + Default + { + $DriverPackDownloadProperties.FileBaseName = "$($DriverPackDownloadProperties.Manufacturer)-$($DriverPackDownloadProperties.BaseboardProduct)-$($DriverPackDownloadProperties.DriverPackReleaseID)-$($DriverPackDownloadProperties.OSAlias)-$($DriverPackDownloadProperties.OSArchitecture)" + } + } + + $DriverPackDownloadProperties.FileBaseName = $DriverPackDownloadProperties.FileBaseName.Split([System.IO.Path]::GetInvalidFileNameChars()) -Join '' + + $DriverPackDownloadProperties.FileName = "$($DriverPackDownloadProperties.FileBaseName).wim" + $DriverPackDownloadProperties.FilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.FileName)" + $DriverPackDownloadProperties.MetadataFileName = "$($DriverPackDownloadProperties.FileBaseName).json" + $DriverPackDownloadProperties.MetadataFilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.MetadataFileName)" + $DriverPackDownloadProperties.DownloadLinkList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.DownloadLinkList.DriverPack = "$($DriverPackSearchResult.URL)" + $DriverPackDownloadProperties.DownloadLinkList.DriverPackMetadata = "$($DriverPackSearchResult.CVAFileURL)" + + $DriverPackDownloadObject = New-Object -TypeName 'PSObject' -Property ($DriverPackDownloadProperties) + + $DriverPackDownloadList.Add($DriverPackDownloadObject) + } + } + } + } + + {($_ -iin @('Lenovo'))} + { + $ModelID = [Regex]::Match($Model.ProductID, '^\w{4}') + + Switch ($ModelID.Success) + { + {($_ -eq $True)} + { + $DriverPackSearchResults = New-Object -TypeName 'System.Collections.Generic.List[Object]' + + $CatalogSearchResults = $DriverPackCatalog.SelectNodes('/ModelList/Model') | Where-Object {($_.Types.Type -icontains $ModelID.Value)} + + ForEach ($CatalogSearchResult In $CatalogSearchResults) + { + $CatalogSearchURLList = $CatalogSearchResult.SCCM | Where-Object {($_.OS -imatch $OperatingSystemEntry.NameExpression) -and ($_.Version -imatch $OperatingSystemEntry.ReleaseExpression)} + + ForEach ($CatalogSearchURL In $CatalogSearchURLList) + { + $CatalogSearchMetadataParser = [Regex]::Match($CatalogSearchURL.'#text', '.*(?w\d{1,2})(?\d{2,2})?.*(?\d{6,8}).*') + + Switch ($CatalogSearchMetadataParser.Success) + { + {($_ -eq $True)} + { + $CatalogSearchResultProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $CatalogSearchResultProperties.Name = $CatalogSearchResult.Name -ireplace '\s+Type.*', '' + $CatalogSearchResultProperties.SystemTypeList = $CatalogSearchResult.Types.Type + $CatalogSearchResultProperties.OperatingSystemRelease = $CatalogSearchURL.Version + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to parse metadata for `"$($CatalogSearchResultProperties.Name)`" from URL `"$($CatalogSearchMetadataParser.ToString())`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + ForEach ($CatalogSearchMetadataCaptureGroup In ($CatalogSearchMetadataParser.Groups | Where-Object {($_.Name -inotin @('0'))})) + { + Switch (([String]::IsNullOrEmpty($CatalogSearchMetadataCaptureGroup.Value) -eq $False) -or ([String]::IsNullOrWhiteSpace($CatalogSearchMetadataCaptureGroup.Value) -eq $False)) + { + {($_ -eq $True)} + { + Switch ($CatalogSearchMetadataCaptureGroup.Name) + { + {($_ -iin @('DateReleased'))} + { + Switch ($CatalogSearchMetadataCaptureGroup.Success) + { + {($_ -eq $True)} + { + $DateTimeFormatList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $Null = $DateTimeFormatList.Add('yyyyMM') + + $DateTime = New-Object -TypeName 'DateTime' + + $DateTimeTryParseExactProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DateTimeTryParseExactProperties.Input = $CatalogSearchMetadataCaptureGroup.Value + $DateTimeTryParseExactProperties.FormatList = $DateTimeFormatList.ToArray() + $DateTimeTryParseExactProperties.Styles = New-Object -TypeName 'System.Collections.Generic.List[System.Globalization.DateTimeStyles]' + $DateTimeTryParseExactProperties.Styles.Add([System.Globalization.DateTimeStyles]::AdjustToUniversal) + $DateTimeTryParseExactProperties.Styles.Add([System.Globalization.DateTimeStyles]::AllowWhiteSpaces) + $DateTimeTryParseExactProperties.DateTime = $Null + $DateTimeTryParseExactProperties.Successful = [DateTime]::TryParseExact($DateTimeTryParseExactProperties.Input, $DateTimeTryParseExactProperties.FormatList, $DateTimeTryParseExactProperties.Culture, $DateTimeTryParseExactProperties.Styles, [Ref]$DateTime) + + Switch ($DateTimeTryParseExactProperties.Successful) + { + {($_ -eq $True)} + { + $DateTimeTryParseExactProperties.DateTime = ($DateTime) + } + } + + $DateTimeTryParseExactObject = New-Object -TypeName 'PSObject' -Property ($DateTimeTryParseExactProperties) + + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = $DateTimeTryParseExactObject.DateTime + } + + Default + { + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = $CatalogSearchMetadataCaptureGroup.Value + } + } + } + + {($_ -iin @('OperatingSystemArchitecture'))} + { + Switch ($True) + { + {($CatalogSearchMetadataCaptureGroup.Value -inotmatch '^X')} + { + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = "X$($CatalogSearchMetadataCaptureGroup.Value)" + } + } + } + + Default + { + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = $CatalogSearchMetadataCaptureGroup.Value + } + } + } + + Default + { + Switch ($CatalogSearchMetadataCaptureGroup.Name) + { + {($_ -iin @('OperatingSystemArchitecture'))} + { + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = 'X64' + } + + Default + { + $CatalogSearchResultProperties.$($CatalogSearchMetadataCaptureGroup.Name) = $Null + } + } + } + } + } + + $CatalogSearchResultProperties.URL = $CatalogSearchURL.'#text' + + $CatalogSearchResultObject = New-Object -TypeName 'PSObject' -Property ($CatalogSearchResultProperties) + + $DriverPackSearchResults.Add($CatalogSearchResultObject) + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Unable to parse metadata for `"$($CatalogSearchResultProperties.Name)`" from URL `"$($CatalogSearchMetadataParser.ToString())`". Skipping..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + + $DriverPackSearchResultCount = ($DriverPackSearchResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchResultCount) result(s) were returned from the driver pack search." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($OperatingSystemEntry.LatestReleaseOnly) + { + {($_ -eq $True)} + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults | Select-Object -Last 1 + } + + Default + { + $DriverPackSearchFilteredResults = $DriverPackSearchResults + } + } + + $DriverPackSearchFilteredResultCount = ($DriverPackSearchFilteredResults | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackSearchFilteredResultCount) result(s) remain after filtering, and sorting." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackSearchFilteredResultCount -gt 0) + { + {($_ -eq $True)} + { + ForEach ($DriverPackSearchResult In $DriverPackSearchFilteredResults) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add driver pack for `"$($DriverPackSearchResult.Name)`" released on $($DriverPackSearchResult.DateReleased.ToString($DateTimeLogFormat)). Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackDownloadProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.Enabled = $True + $DriverPackDownloadProperties.Manufacturer = $Manufacturer.Name + $DriverPackDownloadProperties.BaseboardProduct = $Model.BaseboardProduct + $DriverPackDownloadProperties.SystemFamily = $Model.SystemFamily + $DriverPackDownloadProperties.SystemManufacturer = $Model.SystemManufacturer + $DriverPackDownloadProperties.SystemProductName = $Model.SystemProductName + $DriverPackDownloadProperties.SystemSKU = $Model.SystemSKU + $DriverPackDownloadProperties.SystemVersion = $Model.SystemVersion + $DriverPackDownloadProperties.ProductIDList = $CatalogSearchResultProperties.SystemTypeList + $DriverPackDownloadProperties.ProductID = $DriverPackDownloadProperties.ProductIDList[0] + $DriverPackDownloadProperties.OSName = "$($OperatingSystemEntry.Name)" + $DriverPackDownloadProperties.OSAlias = "W$($OperatingSystemEntry.Name -ireplace '(\D+)', '')" + + $DriverPackOSDetails = $OperatingSystemEntryReleaseHistory | Where-Object {($_.ReleaseID -ieq [Regex]::Match($DriverPackSearchResult.OperatingSystemRelease, $_.ReleaseID).Value)} | Select-Object -First 1 + + Switch ($Null -ine $DriverPackOSDetails) + { + {($_ -eq $True)} + { + $DriverPackDownloadProperties.OSVersionMinimum = $DriverPackOSDetails.Version.ToString() + + $DriverPackDownloadProperties.OSReleaseIDMinimum = $DriverPackOSDetails.ReleaseID + } + + Default + { + $DriverPackDownloadProperties.OSVersionMinimum = $OperatingSystemEntryVersion.ToString() + + $DriverPackDownloadProperties.OSReleaseIDMinimum = "All" + } + } + + $DriverPackDownloadProperties.OSArchitecture = $DriverPackSearchResult.OperatingSystemArchitecture + + Switch ($Null -ine $DriverPackOSDetails) + { + {($_ -eq $True)} + { + $DriverPackDownloadProperties.DriverPackReleaseID = $DriverPackOSDetails.ReleaseID + } + + Default + { + $DriverPackDownloadProperties.DriverPackReleaseID = 'NA' + } + } + + $DriverPackDownloadProperties.DriverPackReleaseVersion = 'NA' + $DriverPackDownloadProperties.DriverPackReleaseDate = Try {(Get-Date -Date $DriverPackSearchResult.DateReleased).ToString($DateDriverPackReleaseFormat)} Catch {$DriverPackSearchResult.DateReleased} + $DriverPackDownloadProperties.DriverPackInfoURL = "" + $DriverPackDownloadProperties.DirectoryPath = "$($DriverPackDownloadProperties.Manufacturer)\$($ModelID.Value)\$($DriverPackDownloadProperties.OSAlias)\$($DriverPackDownloadProperties.OSArchitecture)" + $DriverPackDownloadProperties.FileBaseName = "$($DriverPackDownloadProperties.Manufacturer)-$($ModelID.Value)-$($DriverPackDownloadProperties.OSAlias)-$($DriverPackDownloadProperties.OSArchitecture)-$($DriverPackDownloadProperties.OSReleaseIDMinimum)" + + $DriverPackDownloadProperties.FileBaseName = $DriverPackDownloadProperties.FileBaseName.Split([System.IO.Path]::GetInvalidFileNameChars()) -Join '' + + $DriverPackDownloadProperties.FileName = "$($DriverPackDownloadProperties.FileBaseName).wim" + $DriverPackDownloadProperties.FilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.FileName)" + $DriverPackDownloadProperties.MetadataFileName = "$($DriverPackDownloadProperties.FileBaseName).json" + $DriverPackDownloadProperties.MetadataFilePath = "$($DriverPackDownloadProperties.DirectoryPath)\$($DriverPackDownloadProperties.MetadataFileName)" + $DriverPackDownloadProperties.DownloadLinkList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackDownloadProperties.DownloadLinkList.DriverPack = "$($DriverPackSearchResult.URL)" + $DriverPackDownloadProperties.DownloadLinkList.DriverPackMetadata = "" + + $DriverPackDownloadObject = New-Object -TypeName 'PSObject' -Property ($DriverPackDownloadProperties) + + $DriverPackDownloadList.Add($DriverPackDownloadObject) + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Unable to parse metadata for `"$($CatalogSearchResultProperties.Name)`" from URL `"$($CatalogSearchMetadataParser.ToString())`". Skipping..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + } + } + + {($_ -eq $False)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The XML node for model `"$($Model.SystemProductName)`" manufactured by `"$($Model.SystemManufacturer)`" is not enabled. Skipping..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + + {($_ -eq $False)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The XML node for device manufacturer `"$($Manufacturer.Name)`" is not enabled. Skipping..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + 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 + } + } + Finally + { + + } + } + + Switch ($DisableDownload.IsPresent) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The downloading and processing of driver pack(s) is disabled! Please remove the '-DisableDownload' parameter to enable downloading." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + + Default + { + Try + { + $DriverPackDownloadList = $DriverPackDownloadList.ToArray() + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A total of $($DriverPackDownloadList.Count) driver pack download(s) will be processed. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + #Process Driver Pack Downloads + $ManufacturerGroupList = $DriverPackDownloadList | Group-Object -Property @('Manufacturer') | Sort-Object -Property @('Name') + + :ManufacturerGroupLoop ForEach ($ManufacturerGroup In $ManufacturerGroupList) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process $($ManufacturerGroup.Count) manufacturer specific download(s) for manufacturer `"$($ManufacturerGroup.Name)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ProductGroupProperty = 'ProductID' + + $ProductGroupAliasProperty = 'SystemProductName' + + Switch ($ManufacturerGroup.Name) + { + {($_ -iin @('Lenovo'))} + { + $ProductGroupAliasProperty = 'SystemVersion' + } + } + + $ProductGroupList = $ManufacturerGroup.Group | Group-Object -Property ($ProductGroupProperty) | Sort-Object -Property @('Name') + + :ProductGroupLoop ForEach ($ProductGroup In $ProductGroupList) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process $($ProductGroup.Count) product ID specific download(s) for product ID `"$($ProductGroup.Name)`" manufactured by `"$($ManufacturerGroup.Name)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + :ProductGroupMemberLoop ForEach ($ProductGroupMember In $ProductGroup.Group) + { + $NewWindowsImageDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $NewWindowsImageDetails.ImageStagingPath = "$($LocalDriverPackageDirectory.FullName)\$($ProductGroupMember.DirectoryPath)\$($ProductGroupMember.FileName)" -As [System.IO.FileInfo] + $NewWindowsImageDetails.ImageFinalPath = $NewWindowsImageDetails.ImageStagingPath.FullName.Replace($LocalDriverPackageDirectory.FullName, $DriverPackageDirectory.FullName) -As [System.IO.FileInfo] + $NewWindowsImageDetails.LogPath = "$($LogDirectory.FullName)\DISM\$($ManufacturerGroup.Name)\$($ProductGroupMember.$($ProductGroupProperty))\$($NewWindowsImageDetails.ImageStagingPath.BaseName).log" -As [System.IO.FileInfo] + + Switch (([System.IO.File]::Exists($NewWindowsImageDetails.ImageFinalPath.FullName) -eq $False) -or ($Force.IsPresent -eq $True)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process the `"$($ProductGroupMember.OSName) $($ProductGroupMember.OSArchitecture)`" download list for product ID `"$($ProductGroup.Name)`" [Model: $($ProductGroupMember.$($ProductGroupAliasProperty))] manufactured by `"$($ManufacturerGroup.Name)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + :ProductGroupMemberDownloadLoop ForEach ($ProductGroupMemberDownload In $ProductGroupMember.DownloadLinkList.GetEnumerator()) + { + Switch (([String]::IsNullOrEmpty($ProductGroupMemberDownload.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($ProductGroupMemberDownload.Value) -eq $False)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process the URL for `"$($ProductGroupMemberDownload.Key)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($ProductGroupMemberDownload.Key) + { + {($_ -iin @('DriverPack'))} + { + $InvokeFileDownloadParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeFileDownloadParameters.URL = $ProductGroupMemberDownload.Value + $InvokeFileDownloadParameters.Destination = "$($DownloadDirectory.FullName)\$($ProductGroupMember.Manufacturer)\$($ProductGroupMember.$($ProductGroupProperty))" + $InvokeFileDownloadParameters.ContinueOnError = $False + $InvokeFileDownloadParameters.Verbose = $True + + $InvokeFileDownloadResult = Invoke-FileDownloadWithProgress @InvokeFileDownloadParameters + + $FileDownloadExtractionParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $FileDownloadExtractionParameters.ExtractionPath = "$($DownloadDirectory.FullName)\$($ProductGroupMember.Manufacturer)\$($ProductGroupMember.$($ProductGroupProperty))\$($ProductGroupMember.OSAlias)-$($ProductGroupMember.OSArchitecture)-$($ProductGroupMember.DriverPackReleaseID)" -As [System.IO.DirectoryInfo] + $FileDownloadExtractionParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $FileDownloadExtractionParameters.CopyDriverPackMetadata = $True + $FileDownloadExtractionParameters.CreateWindowsImageDriverPack = $True + + Switch ($ProductGroupMember.Manufacturer) + { + {($_ -iin @('Lenovo'))} + { + $FileDownloadExtractionParameters.FilePath = $InvokeFileDownloadResult.DownloadPath.FullName -As [System.IO.FileInfo] + + $FileDownloadExtractionParameters.ArgumentList.Add("/VERYSILENT") + $FileDownloadExtractionParameters.ArgumentList.Add("/DIR=`"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`"") + $FileDownloadExtractionParameters.ArgumentList.Add("/EXTRACT=`"YES`"") + } + + Default + { + $FileDownloadExtractionParameters.FilePath = $7ZipPath.FullName -As [System.IO.FileInfo] + + $FileDownloadExtractionParameters.ArgumentList.Add("x") + $FileDownloadExtractionParameters.ArgumentList.Add("`"$($InvokeFileDownloadResult.DownloadPath.FullName)`"") + $FileDownloadExtractionParameters.ArgumentList.Add("-o`"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`"") + $FileDownloadExtractionParameters.ArgumentList.Add("*") + $FileDownloadExtractionParameters.ArgumentList.Add("-r") + } + } + + Switch ([System.IO.Directory]::Exists($FileDownloadExtractionParameters.ExtractionPath.FullName)) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove existing directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`" prior to extraction. Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + [String]$DeletionPath = $FileDownloadExtractionParameters.ExtractionPath.FullName + + $Null = [Alphaleonis.Win32.Filesystem.Directory]::Delete($DeletionPath, $True, $True) + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to create non-existent directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`" prior to extraction. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Null = [System.IO.Directory]::CreateDirectory($FileDownloadExtractionParameters.ExtractionPath.FullName) + } + } + + $StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $StartProcessWithOutputParameters.FilePath = $FileDownloadExtractionParameters.FilePath.FullName + $StartProcessWithOutputParameters.ArgumentList = $FileDownloadExtractionParameters.ArgumentList + $StartProcessWithOutputParameters.AcceptableExitCodeList = @(0, 3010) + $StartProcessWithOutputParameters.CreateNoWindow = $True + $StartProcessWithOutputParameters.LogOutput = $False + $StartProcessWithOutputParameters.Verbose = $True + + $FileDownloadExtractionResult = Start-ProcessWithOutput @StartProcessWithOutputParameters + + Switch ($FileDownloadExtractionParameters.ExtractionPath.GetDirectories().Count -gt 2) + { + {($_ -eq $True)} + { + $WindowsImageRootFolder = $FileDownloadExtractionParameters.ExtractionPath + } + + Default + { + $WindowsImageRootFolderList = Get-ChildItem -Path ($FileDownloadExtractionParameters.ExtractionPath.FullName) -Recurse | Where-Object {($_ -is [System.IO.DirectoryInfo]) -and ($_.GetDirectories().Count -gt 2)} + + Switch ($Null -ine $WindowsImageRootFolderList) + { + {($_ -eq $True)} + { + $WindowsImageRootFolder = $WindowsImageRootFolderList | Sort-Object -Property @{Expression = {($_.FullName.Length)}} | Select-Object -First 1 + } + + {($_ -eq $False)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The windows image root folder could not located within folder `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`"." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Cleaning up and moving to the next driver pack. Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $FileDownloadExtractionParameters.CopyDriverPackMetadata = $False + + $FileDownloadExtractionParameters.CreateWindowsImageDriverPack = $False + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove driver pack extraction directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + #Cleanup the extracted driver package content (If necessary) + If ([System.IO.Directory]::Exists($FileDownloadExtractionParameters.ExtractionPath.FullName) -eq $True) + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove driver pack extraction directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + [String]$DeletionPath = $FileDownloadExtractionParameters.ExtractionPath.FullName + + $Null = [Alphaleonis.Win32.Filesystem.Directory]::Delete($DeletionPath, $True, $True) + } + + Break ProductGroupMemberLoop + } + } + } + } + } + + {($_ -iin @('DriverPackMetaData'))} + { + Switch ($FileDownloadExtractionParameters.CopyDriverPackMetadata) + { + {($_ -eq $True)} + { + $DriverPackMetaDataFileList = New-Object -TypeName 'System.Collections.Generic.List[System.IO.FileInfo]' + + $DriverPackMetaDataExtensionList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $DriverPackMetaDataExtensionList.Add('.xml') + $DriverPackMetaDataExtensionList.Add('.html') + $DriverPackMetaDataExtensionList.Add('.cva') + $DriverPackMetaDataExtensionList.Add('.json') + $DriverPackMetaDataExtensionList.Add('.txt') + + $DriverPackMetaDataFiles = Get-ChildItem -Path "$($FileDownloadExtractionParameters.ExtractionPath.FullName)\" -Depth 1 -Force -ErrorAction SilentlyContinue | Where-Object {($_ -is [System.IO.FileInfo]) -and ($_.Extension -iin $DriverPackMetaDataExtensionList.ToArray())} + + $DriverPackMetaDataFileCount = ($DriverPackMetaDataFiles | Measure-Object).Count + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackMetaDataFileCount) metadata file(s) matching `"$($DriverPackMetaDataExtensionList -Join ' | ')`" were found in directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`"." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackMetaDataFileCount -gt 0) + { + {($_ -eq $True)} + { + $DriverPackMetaDataFiles | ForEach-Object {$DriverPackMetaDataFileList.Add($_.FullName)} + } + } + + [System.IO.DirectoryInfo]$DriverPackMetaDataDirectory = "$($WindowsImageRootFolder.FullName)\Metadata" + + If ([System.IO.Directory]::Exists($DriverPackMetaDataDirectory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DriverPackMetaDataDirectory.FullName)} + + ForEach ($DriverPackMetaDataFile In $DriverPackMetaDataFileList) + { + [System.IO.FileInfo]$DriverPackMetaDataFileDestination = "$($DriverPackMetaDataDirectory.FullName)\$($DriverPackMetaDataFile.Name)" + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to copy metadata file `"$($DriverPackMetaDataFile.FullName)`" to `"$($DriverPackMetaDataFileDestination.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Null = [System.IO.File]::Copy($DriverPackMetaDataFile.FullName, $DriverPackMetaDataFileDestination.FullName, $True) + } + + $InvokeFileDownloadParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeFileDownloadParameters.URL = $ProductGroupMemberDownload.Value + $InvokeFileDownloadParameters.Destination = "$($DownloadDirectory.FullName)\$($ProductGroupMember.Manufacturer)\$($ProductGroupMember.$($ProductGroupProperty))" + $InvokeFileDownloadParameters.ContinueOnError = $False + $InvokeFileDownloadParameters.Verbose = $True + + $InvokeFileDownloadResult = Invoke-FileDownloadWithProgress @InvokeFileDownloadParameters + + $Null = [System.IO.File]::Copy($InvokeFileDownloadResult.DownloadPath.FullName, "$($DriverPackMetaDataFileDestination.Directory.FullName)\$($InvokeFileDownloadResult.DownloadPath.Name)", $True) + } + } + } + + Default + { + $InvokeFileDownloadParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $InvokeFileDownloadParameters.URL = $ProductGroupMemberDownload.Value + $InvokeFileDownloadParameters.Destination = $FileDownloadExtractionParameters.ExtractionPath.FullName + $InvokeFileDownloadParameters.ContinueOnError = $False + $InvokeFileDownloadParameters.Verbose = $True + + $InvokeFileDownloadResult = Invoke-FileDownloadWithProgress @InvokeFileDownloadParameters + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The `"$($ProductGroupMemberDownload.Key)`" URL will not be processed because the value does not contain any data. Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + #Create a windows image from the driver content if one does not already exist + Switch ([System.IO.File]::Exists($NewWindowsImageDetails.ImageStagingPath.FullName)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The windows image file `"$($NewWindowsImageDetails.ImageStagingPath.FullName)`" already exists and will not be created. Skipping." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + {($_ -eq $False)} + { + Switch ($True) + { + {([System.IO.Directory]::Exists($NewWindowsImageDetails.ImageStagingPath.Directory.FullName) -eq $False)} + { + $Null = [System.IO.Directory]::CreateDirectory($NewWindowsImageDetails.ImageStagingPath.Directory.FullName) + } + + {([System.IO.Directory]::Exists($NewWindowsImageDetails.LogPath.Directory.FullName) -eq $False)} + { + $Null = [System.IO.Directory]::CreateDirectory($NewWindowsImageDetails.LogPath.Directory.FullName) + } + } + + $NewWindowsImageParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $NewWindowsImageParameters.CapturePath = $WindowsImageRootFolder.FullName + $NewWindowsImageParameters.ImagePath = $NewWindowsImageDetails.ImageStagingPath.FullName + $NewWindowsImageParameters.Name = $ProductGroupMember.FileBaseName + $NewWindowsImageParameters.Description = $ProductGroupMember | Select-Object -Property @('*') -ExcludeProperty @('Enabled') | ConvertTo-JSON -Depth 10 -Compress:$True + $NewWindowsImageParameters.CompressionType = 'Max' + $NewWindowsImageParameters.Verify = $False + $NewWindowsImageParameters.LogPath = $NewWindowsImageDetails.LogPath.FullName + $NewWindowsImageParameters.LogLevel = 'WarningsInfo' + $NewWindowsImageParameters.Verbose = $False + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to compress the extracted content for the `"$($ProductGroupMember.OSName) $($ProductGroupMember.OSArchitecture)`" driver pack `"$($ProductGroupMember.DriverPackReleaseID)`" for product ID `"$($ProductGroupMember.BaseboardProduct)`" [Model: $($ProductGroupMember.$($ProductGroupAliasProperty))] manufactured by `"$($ManufacturerGroup.Name)`" into the windows image format. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Windows Image Capture Path: $($NewWindowsImageParameters.CapturePath)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Windows Image Export Path: $($NewWindowsImageParameters.ImagePath)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Windows Image Log Path: $($NewWindowsImageParameters.LogPath)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $CommandExecutionTimespan = Measure-Command -Expression {$Null = New-WindowsImage @NewWindowsImageParameters} + + If ($? -eq $True) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Extracted content compression completed in $($CommandExecutionTimespan.Hours.ToString()) hour(s), $($CommandExecutionTimespan.Minutes.ToString()) minute(s), $($CommandExecutionTimespan.Seconds.ToString()) second(s), and $($CommandExecutionTimespan.Milliseconds.ToString()) millisecond(s)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $NewWindowsImageDetails.HashDetails = Get-FileHash -Path ($NewWindowsImageParameters.ImagePath) -Algorithm ($HashAlgorithm) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Windows Image Hash: $($NewWindowsImageDetails.HashDetails.Hash) [Algorithm: $($NewWindowsImageDetails.HashDetails.Algorithm)]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ExtractedContentSize = Get-ChildItem -Path "$($NewWindowsImageParameters.CapturePath)\*" -Recurse | Where-Object {($_ -is [System.IO.FileInfo])} | Measure-Object -Property @('Length') -Sum | Select-Object -ExpandProperty 'Sum' + $ExtractedContentSizeDetails = Convert-FileSize -Size ($ExtractedContentSize) -DecimalPlaces 2 + + $WindowsImageDetails = Get-Item -Path ($NewWindowsImageParameters.ImagePath) -Force + $WindowsImageDetailsSizeDetails = Convert-FileSize -Size ($WindowsImageDetails.Length) -DecimalPlaces 2 + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The extracted content was reduced from its original size of $($ExtractedContentSizeDetails.CalculatedSizeStr) to its compressed size of $($WindowsImageDetailsSizeDetails.CalculatedSizeStr)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + #Export the Windows image metadata + $WindowsImageJSONTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $WindowsImageJSONTable.Cryptography = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $WindowsImageJSONTable.Cryptography.Hash = $NewWindowsImageDetails.HashDetails.Hash + $WindowsImageJSONTable.Cryptography.Algorithm = $NewWindowsImageDetails.HashDetails.Algorithm + $WindowsImageJSONTable.Content = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $WindowsImageJSONTable.Content.OriginalSize = $ExtractedContentSize + $WindowsImageJSONTable.Content.CompressedSize = $WindowsImageDetails.Length + $WindowsImageJSONTable.Metadata = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $WindowsImageJSONTable.Metadata = $ProductGroupMember | Select-Object -Property @('*') -ExcludeProperty @('Enabled', 'BaseboardProduct', 'SystemFamily', 'SystemManufacturer', 'SystemProductName', 'SystemSKU', 'SystemVersion', 'DirectoryPath', 'FileBaseName', 'FileName', 'MetadataFileName') + + $WindowsImageJSONContents = $WindowsImageJSONTable | ConvertTo-JSON -Depth 10 -Compress:$False + + [System.IO.FileInfo]$WindowsImageJSONExportPath = "$($NewWindowsImageDetails.ImageStagingPath.Directory.FullName)\$($NewWindowsImageDetails.ImageStagingPath.BaseName).json" + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to export the driver package metadata to `"$($WindowsImageJSONExportPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + If ([System.IO.Directory]::Exists($WindowsImageExportPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($WindowsImageJSONExportPath.Directory.FullName)} + + $Null = [System.IO.File]::WriteAllText($WindowsImageJSONExportPath.FullName, $WindowsImageJSONContents, [System.Text.Encoding]::Default) + } + } + } + + #Cleanup the extracted driver package content (If necessary) + If ([System.IO.Directory]::Exists($FileDownloadExtractionParameters.ExtractionPath.FullName) -eq $True) + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove driver pack extraction directory `"$($FileDownloadExtractionParameters.ExtractionPath.FullName)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + [String]$DeletionPath = $FileDownloadExtractionParameters.ExtractionPath.FullName + + $Null = [Alphaleonis.Win32.Filesystem.Directory]::Delete($DeletionPath, $True, $True) + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A driver pack windows image already exists for `"$($ProductGroupMember.OSName) $($ProductGroupMember.OSArchitecture)`" for product ID `"$($ProductGroup.Name)`" [Model: $($ProductGroupMember.$($ProductGroupAliasProperty))] manufactured by `"$($ManufacturerGroup.Name)`". Skipping..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Windows Image Path: $($NewWindowsImageDetails.ImageFinalPath.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + } + } + + #Create the specified generic driver package folder(s) + $GenericDriverPackageCategoryList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $GenericDriverPackageCategoryList.Add('Printers') + $GenericDriverPackageCategoryList.Add('Docks') + $GenericDriverPackageCategoryList.Add('Adapters') + $GenericDriverPackageCategoryList.Add('Miscellaneous') + + $GenericDriverPackageFolderList = New-Object -TypeName 'System.Collections.Generic.List[System.IO.DirectoryInfo]' + $GenericDriverPackageFolderList.Add("$($LocalDriverPackageDirectory.FullName)\Generic") + + $GenericDriverPackageTableList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + $Null = $GenericDriverPackageCategoryList.Sort() + + ForEach ($GenericDriverPackageCategory In $GenericDriverPackageCategoryList) + { + $GenericDriverPackageJSON = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $GenericDriverPackageJSON.Enabled = $False + $GenericDriverPackageJSON.Metadata = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $GenericDriverPackageJSON.Metadata.Category = $GenericDriverPackageCategory + $GenericDriverPackageJSON.Metadata.Name = "$($GenericDriverPackageFolderList[0].Name)-$($GenericDriverPackageCategory)" + $GenericDriverPackageJSON.Metadata.ManufacturerInclusionExpression = '.*' + $GenericDriverPackageJSON.Metadata.ManufacturerExclusionExpression = '(^.{0,0}$)' + $GenericDriverPackageJSON.Metadata.ProductIDInclusionExpression = '.*' + $GenericDriverPackageJSON.Metadata.ProductIDExclusionExpression = '(^.*Virtual.*$)' + $GenericDriverPackageJSON.Metadata.OSVersionMinimum = ($WindowsReleaseHistory | Select-Object -First 1).Version.ToString() + $GenericDriverPackageJSON.Metadata.OSArchitectureExpression = '.*' + $GenericDriverPackageJSON.Metadata.FilePath = "$($GenericDriverPackageCategory)\$($GenericDriverPackageFolderList[0].Name)-$($GenericDriverPackageCategory).wim" + $GenericDriverPackageJSON.Metadata.MetadataPath = "$($GenericDriverPackageCategory)\$($GenericDriverPackageFolderList[0].Name)-$($GenericDriverPackageCategory).json" + + $GenericDriverPackageTableProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $GenericDriverPackageTableProperties.Path = "$($GenericDriverPackageFolderList[0].FullName)\$($GenericDriverPackageJSON.Metadata.MetadataPath)" -As [System.IO.FileInfo] + $GenericDriverPackageTableProperties.Contents = $GenericDriverPackageJSON | ConvertTo-JSON -Depth 10 -Compress:$False + + $GenericDriverPackageTableObject = New-Object -TypeName 'PSObject' -Property ($GenericDriverPackageTableProperties) + + $GenericDriverPackageTableList.Add($GenericDriverPackageTableObject) + } + + ForEach ($GenericDriverPackageTable In $GenericDriverPackageTableList) + { + Switch ($True) + { + {([System.IO.Directory]::Exists($GenericDriverPackageTable.Path.Directory.FullName) -eq $False)} + { + $Null = [System.IO.Directory]::CreateDirectory($GenericDriverPackageTable.Path.Directory.FullName) + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to create JSON metadata file `"$($GenericDriverPackageTable.Path.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Null = [System.IO.File]::WriteAllText($GenericDriverPackageTable.Path.FullName, $GenericDriverPackageTable.Contents, [System.Text.Encoding]::Default) + } + + #Copy the locally created driver packages to the final destination + Switch ([System.IO.Directory]::Exists($LocalDriverPackageDirectory.FullName)) + { + {($_ -eq $True)} + { + $LocalDriverPackageInclusionList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $LocalDriverPackageInclusionList.Add('*.wim') + $LocalDriverPackageInclusionList.Add('*.json') + + $LocalDriverPackages = Get-ChildItem -Path ($LocalDriverPackageDirectory.FullName) -Include ($LocalDriverPackageInclusionList.ToArray()) -Recurse -ErrorAction SilentlyContinue | Where-Object {($_ -is [System.IO.FileInfo])} + + $LocalDriverPackageCount = ($LocalDriverPackages | Measure-Object).Count + + Switch ($LocalDriverPackageCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($LocalDriverPackageCount) windows image driver packages to copy from `"$($LocalDriverPackageDirectory.FullName)`" to `"$($DriverPackageDirectory.FullName)`"." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [System.IO.FileInfo]$CopyDriverPackagesLogPath = "$($LogDirectory.FullName)\Robocopy\CopyDriverPackages-ToFinalLocation.log" + + If ([System.IO.Directory]::Exists($CopyDriverPackagesLogPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($CopyDriverPackagesLogPath.Directory.FullName)} + + $StartProcessParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $StartProcessParameters.FilePath = "$($System32Directory.FullName)\robocopy.exe" + $StartProcessParameters.ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $StartProcessParameters.ArgumentList.Add("`"$($LocalDriverPackageDirectory.FullName)`"") + $StartProcessParameters.ArgumentList.Add("`"$($DriverPackageDirectory.FullName)`"") + $StartProcessParameters.ArgumentList.Add("/E") + $StartProcessParameters.ArgumentList.Add("/Z") + $StartProcessParameters.ArgumentList.Add("/ZB") + $StartProcessParameters.ArgumentList.Add("/W:5") + $StartProcessParameters.ArgumentList.Add("/R:3") + $StartProcessParameters.ArgumentList.Add("/J") + $StartProcessParameters.ArgumentList.Add("/FP") + $StartProcessParameters.ArgumentList.Add("/NDL") + $StartProcessParameters.ArgumentList.Add("/TEE") + $StartProcessParameters.ArgumentList.Add("/XX") + $StartProcessParameters.ArgumentList.Add("/MT:16") + If ($EnableRobocopyIPG.IsPresent) {$StartProcessParameters.ArgumentList.Add("/IPG:125")} + $StartProcessParameters.ArgumentList.Add("/LOG:`"$($CopyDriverPackagesLogPath.FullName)`"") + $StartProcessParameters.PassThru = $True + $StartProcessParameters.Wait = $True + + Switch ($ProcessExecutionTable.IsSessionZero) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Session 0 was detected [User: $($ProcessExecutionTable.ProcessNTAccount)]. The command window will be hidden." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $StartProcessParameters.WindowStyle = [System.Diagnostics.Processwindowstyle]::Hidden + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Session 0 was not detected [User: $($ProcessExecutionTable.ProcessNTAccount)]. The command window will be show." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $StartProcessParameters.WindowStyle = [System.Diagnostics.Processwindowstyle]::Normal + } + } + + $CopyDriverPackagesResult = Start-Process @StartProcessParameters + + $AcceptableExitCodeList = @(0, 1, 2, 3, 4, 5, 6, 7, 8) + + Switch ($CopyDriverPackagesResult.ExitCode -in $AcceptableExitCodeList) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The command execution was successful. [Exit Code: $($CopyDriverPackagesResult.ExitCode)]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + {($_ -eq $False)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The command execution was unsuccessful. [Exit Code: $($CopyDriverPackagesResult.ExitCode)]" + 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) + } + } + + #Cleanup the local driver package directory (If necessary) + If ([System.IO.Directory]::Exists($LocalDriverPackageDirectory.FullName) -eq $True) + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove local driver package directory `"$($LocalDriverPackageDirectory.FullName)`". Please Wait..." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + [String]$DeletionPath = $LocalDriverPackageDirectory.FullName + + $Null = [Alphaleonis.Win32.Filesystem.Directory]::Delete($DeletionPath, $True, $True) + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($LocalDriverPackageCount) windows image driver packages to copy from `"$($LocalDriverPackageDirectory.FullName)`" to `"$($DriverPackageDirectory.FullName)`"." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The directory `"$($LocalDriverPackageDirectory.FullName)`" does not exist. The windows image driver packages will not be copied." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + Catch + { + Throw + } + Finally + { + + } + } + } + + #region Export Driver Pack Metadata to XML + $XMLWriterSettings = New-Object -TypeName 'System.XML.XMLWriterSettings' + $XMLWriterSettings.Indent = $True + $XMLWriterSettings.IndentChars = "`t" * 1 + $XMLWriterSettings.Encoding = [System.Text.Encoding]::Default + $XMLWriterSettings.NewLineHandling = [System.XML.NewLineHandling]::None + $XMLWriterSettings.ConformanceLevel = [System.XML.ConformanceLevel]::Auto + + $XMLStringBuilder = New-Object -TypeName 'System.Text.StringBuilder' + + $XMLWriter = [System.XML.XMLTextWriter]::Create($XMLStringBuilder, $XMLWritersettings) + + [ScriptBlock]$AddXMLWriterNewLine = {$XMLWriter.WriteWhitespace(("`r`n" * 2))} + + $XMLWriter.WriteStartDocument() + + $AddXMLWriterNewLine.Invoke() + + $XMLWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl'") + + $AddXMLWriterNewLine.Invoke() + + $DriverPackagePropertyInclusionList = New-Object -TypeName 'System.Collections.Generic.List[Object]' + $DriverPackagePropertyInclusionList.Add(@{Name = 'Enabled'; Expression = {$_.Enabled}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'MinimumOSVersion'; Expression = {$_.OSVersionMinimum}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'MinimumOSVersionAlias'; Expression = {$_.OSReleaseIDMinimum}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'ReleaseID'; Expression = {$_.DriverPackReleaseID}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'ReleaseVersion'; Expression = {$_.DriverPackReleaseVersion}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'ReleaseDate'; Expression = {$_.DriverPackReleaseDate}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'FilePath'; Expression = {$_.FilePath}}) + $DriverPackagePropertyInclusionList.Add(@{Name = 'MetadataPath'; Expression = {$_.MetadataFilePath}}) + + $XMLWriter.WriteStartElement('Metadata') + + $XMLWriter.WriteElementString('GeneratedBy', $ProcessExecutionTable.ProcessNTAccount) + $XMLWriter.WriteElementString('GeneratedOn', $Env:ComputerName.ToUpper()) + $XMLWriter.WriteElementString('GeneratedDate', $GetCurrentDateTimeXMLFormat.InvokeReturnAsIs()) + + $XMLWriter.WriteStartElement('ManufacturerList') + + $ManufacturerGroupList = $DriverPackDownloadList | Group-Object -Property @('Manufacturer') | Sort-Object -Property @('Name') + + ForEach ($ManufacturerGroup In $ManufacturerGroupList) + { + $ProductNameProperty = 'SystemProductName' + + Switch ($ManufacturerGroup.Name) + { + {($_ -iin @('Dell'))} + { + [Object]$ProductIDExpression = @{Name = 'ProductID'; Expression = {$_.SystemSKU.ToUpper()}} + } + + {($_ -iin @('HP'))} + { + [Object]$ProductIDExpression = @{Name = 'ProductID'; Expression = {[Regex]::Match($_.BaseboardProduct, '^\w{4}').Value.ToUpper()}} + } + + {($_ -iin @('Lenovo'))} + { + [Object]$ProductIDExpression = @{Name = 'ProductID'; Expression = {[Regex]::Match($_.SystemProductName, '^\w{4}').Value.ToUpper()}} + + $ProductNameProperty = 'SystemVersion' + } + + Default + { + [Object]$ProductIDExpression = @{Name = 'ProductID'; Expression = {$_.BaseboardProduct}} + } + } + + $XMLWriter.WriteStartElement('Manufacturer') + + $XMLWriter.WriteAttributeString('Enabled', $True) + $XMLWriter.WriteAttributeString('Name', $ManufacturerGroup.Name) + $XMLWriter.WriteAttributeString('EligibilityExpression', ($Script:SettingsTable.ManufacturerList | Where-Object {($_.Name -ieq $ManufacturerGroup.Name)}).EligibilityExpression) + $XMLWriter.WriteAttributeString('ProductIDExpression', "@{Name = '$($ProductIDExpression.Name)'; Expression = {$($ProductIDExpression.Expression)}}") + + $XMLWriter.WriteStartElement('ModelList') + + $ProductGroupList = $ManufacturerGroup.Group | Select-Object -Property @('*') | Group-Object -Property 'ProductID' | Sort-Object -Property @('Name') + + ForEach ($ProductGroup In $ProductGroupList) + { + $XMLWriter.WriteStartElement('Model') + + $XMLWriter.WriteAttributeString('Enabled', $True) + $XMLWriter.WriteAttributeString('Name', $ProductGroup.Group[0].$($ProductNameProperty)) + + $XMLWriter.WriteStartElement('ProductIDList') + + ForEach ($ProductID In ($ProductGroup.Group.ProductIDList | Sort-Object -Unique)) + { + $XMLWriter.WriteElementString('ProductID', $ProductID) + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteStartElement('OperatingSystemList') + + $OperatingSystemGroupProperty = 'OSName' + + $OperatingSystemGroupList = $ProductGroup.Group | Sort-Object -Property {[Regex]::Match($_.OSAlias, '\d+').Value} | Group-Object -Property ($OperatingSystemGroupProperty) + + ForEach ($OperatingSystemGroup In $OperatingSystemGroupList) + { + $XMLWriter.WriteStartElement('OperatingSystem') + + $XMLWriter.WriteAttributeString('Enabled', $True) + $XMLWriter.WriteAttributeString('Name', $OperatingSystemGroup.Name) + $XMLWriter.WriteAttributeString('Architecture', $OperatingSystemGroup.Group[0].OSArchitecture) + $XMLWriter.WriteAttributeString('MinimumVersion', ($WindowsReleaseHistory | Where-Object {($_.Name -ieq $OperatingSystemGroup.Name)} | Select-Object -First 1).Version.ToString()) + + $XMLWriter.WriteStartElement('DriverPackageList') + + $DriverPackageList = $OperatingSystemGroupList.Group | Where-Object {($_.$($OperatingSystemGroupProperty) -ieq $OperatingSystemGroup.Name)} | Sort-Object -Property {[Version]$_.MinimumOSVersion} -Descending + + ForEach ($DriverPackage In $DriverPackageList) + { + $DriverPackageEntry = $DriverPackage | Select-Object -Property ($DriverPackagePropertyInclusionList) + + $XMLWriter.WriteStartElement('DriverPackage') + + ForEach ($DriverPackageProperty In $DriverPackageEntry.PSObject.Properties) + { + $XMLWriter.WriteAttributeString($DriverPackageProperty.Name, $DriverPackageProperty.Value) + } + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + } + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndElement() + + $XMLWriter.WriteEndDocument() + + $XMLWriter.Flush() + + $XMLWriter.Close() + + [String]$DriverPackageListContents = $XMLStringBuilder.ToString() + + [System.IO.FileInfo]$DriverPackageListExportPath = "$($DriverPackageDirectory.FullName)\Metadata\DriverPackageList.xml" + + [Scriptblock]$ExportDriverPackageList = { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to export the driver package list to `"$($DriverPackageListExportPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + If ([System.IO.Directory]::Exists($DriverPackageListExportPath.Directory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DriverPackageListExportPath.Directory.FullName)} + + $Null = [System.IO.File]::WriteAllText($DriverPackageListExportPath.FullName, $DriverPackageListContents, $XMLWriterSettings.Encoding) + } + + Switch ([System.IO.File]::Exists($DriverPackageListExportPath.FullName)) + { + {($_ -eq $True)} + { + $MemoryStream = New-Object -TypeName 'System.IO.MemoryStream' + $StreamWriter = New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList ($MemoryStream) + $Null = $StreamWriter.Write($DriverPackageListContents) + $Null = $StreamWriter.Flush() + $Null = $MemoryStream.Position = 0 + + $DriverPackageListContentHash = Get-FileHash -InputStream $MemoryStream -Algorithm ($HashAlgorithm) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - New driver package list hash: $($DriverPackageListContentHash.Hash)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageListExportPathHash = Get-FileHash -Path ($DriverPackageListExportPath.FullName) -Algorithm ($HashAlgorithm) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Current driver package list hash: $($DriverPackageListExportPathHash.Hash)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($DriverPackageListContentHash.Hash -ne $DriverPackageListExportPathHash.Hash) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The driver package list `"$($DriverPackageListExportPath.FullName)`" requires an update." + Write-Warning -Message ($LoggingDetails.WarningMessage) -Verbose + + $Null = $ExportDriverPackageList.InvokeReturnAsIs() + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The driver package list `"$($DriverPackageListExportPath.FullName)`" does not require an update." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + + Default + { + $Null = $ExportDriverPackageList.InvokeReturnAsIs() + } + } + #endregion + + $Script:LASTEXITCODE = $TerminationCodes.Success[0] + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + Try + { + #Determine the date and time the function completed execution + $ScriptEndTime = (Get-Date) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution of `"$($CmdletName)`" ended on $($ScriptEndTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + #Log the total script execution time + $ScriptExecutionTimespan = New-TimeSpan -Start ($ScriptStartTime) -End ($ScriptEndTime) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution took $($ScriptExecutionTimespan.Hours.ToString()) hour(s), $($ScriptExecutionTimespan.Minutes.ToString()) minute(s), $($ScriptExecutionTimespan.Seconds.ToString()) second(s), and $($ScriptExecutionTimespan.Milliseconds.ToString()) millisecond(s)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Exiting script `"$($ScriptPath.FullName)`" with exit code $($Script:LASTEXITCODE)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Stop-Transcript + } + Catch + { + + } + } + } \ No newline at end of file diff --git a/Invoke-DriverPackageCreator.vbs b/Invoke-DriverPackageCreator.vbs new file mode 100644 index 0000000..2190820 --- /dev/null +++ b/Invoke-DriverPackageCreator.vbs @@ -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 \ No newline at end of file diff --git a/Invoke-DriverPackageDownload.ps1 b/Invoke-DriverPackageDownload.ps1 new file mode 100644 index 0000000..1f53681 --- /dev/null +++ b/Invoke-DriverPackageDownload.ps1 @@ -0,0 +1,1388 @@ +#Requires -Version 3 + +<# + .SYNOPSIS + A brief overview of what your function does + + .DESCRIPTION + Slightly more detailed description of what your function does + + .PARAMETER TaskSequenceVariables + One or more task sequence variable(s) to retrieve during task sequence execution. + If this parameter is not specified, all task sequence variable(s) will be stored into the variable 'TSVariableTable'. + Any task sequence variables that are new or have been updated will be saved back to the task sequence engine for futher usage. + + $TSVariable.MyCustomVariableName = "MyCustomVariableValue" + $TSVariable.Make = "MyDeviceModel" + + .PARAMETER LogDir + A valid folder path. If the folder does not exist, it will be created. This parameter can also be specified by the alias "LogPath". + + .PARAMETER ContinueOnError + Ignore failures. + + .EXAMPLE + Use this command to execute a VBSCript that will launch this powershell script automatically with the specified parameters. This is useful to avoid powershell execution complexities. + + cscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + wscript.exe /nologo "%FolderPathContainingScript%\%ScriptName%.vbs" /SwitchParameter /ScriptParameter:"%ScriptParameterValue%" /ScriptParameterArray:"%ScriptParameterValue1%,%ScriptParameterValue2%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NoProfile -NoLogo -File "%FolderPathContainingScript%\%ScriptName%.ps1" -SwitchParameter -ScriptParameter "%ScriptParameterValue%" + + .EXAMPLE + powershell.exe -ExecutionPolicy Bypass -NonInteractive -NoProfile -NoLogo -WindowStyle Hidden -Command "& '%FolderPathContainingScript%\%ScriptName%.ps1' -ScriptParameter1 '%ScriptParameter1Value%' -ScriptParameter2 %ScriptParameter2Value% -SwitchParameter" + + .NOTES + Any useful tidbits + + .LINK + Place any useful link here where your function or cmdlet can be referenced +#> + +[CmdletBinding(SupportsShouldProcess=$True)] + Param + ( + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [ValidateScript({[System.IO.Directory]::Exists($_) -eq $True})] + [Alias('DPRD')] + [System.IO.DirectoryInfo]$DriverPackageRootDirectory, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [ValidatePattern('^.+\.(xml)$')] + [ValidateScript({[System.IO.File]::Exists($_) -eq $True})] + [Alias('DPMDP')] + [System.IO.FileInfo]$DriverPackageMetadataPath, + + [Parameter(Mandatory=$False)] + [Switch]$DisableDownLeveling, + + [Parameter(Mandatory=$False)] + [Alias('BIMB', 'Buffer', 'BufferInMegabytes')] + [Int]$SegmentSize = 8192, + + [Parameter(Mandatory=$False)] + [Alias('RD')] + [Switch]$RandomDelay, + + [Parameter(Mandatory=$False)] + [Alias('S', 'StageContent', 'StageDriversLocally')] + [Switch]$Stage, + + [Parameter(Mandatory=$False)] + [Alias('SRD')] + [System.IO.DirectoryInfo]$StagingRootDirectory, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('TSVars', 'TSVs')] + [String[]]$TaskSequenceVariables, + + [Parameter(Mandatory=$False)] + [ValidateNotNullOrEmpty()] + [Alias('LogDir', 'LogPath')] + [System.IO.DirectoryInfo]$LogDirectory, + + [Parameter(Mandatory=$False)] + [Switch]$ContinueOnError + ) + +Function Get-AdministrativePrivilege + { + $Identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() + $Principal = New-Object System.Security.Principal.WindowsPrincipal($Identity) + Write-Output -InputObject ($Principal.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) + } + +If ((Get-AdministrativePrivilege) -eq $False) + { + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Path)" + + $ArgumentList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $ArgumentList.Add('-ExecutionPolicy Bypass') + $ArgumentList.Add('-NoProfile') + $ArgumentList.Add('-NoExit') + $ArgumentList.Add('-NoLogo') + $ArgumentList.Add("-File `"$($ScriptPath.FullName)`"") + + $Null = Start-Process -FilePath "$([System.Environment]::SystemDirectory)\WindowsPowershell\v1.0\powershell.exe" -WorkingDirectory "$([System.Environment]::SystemDirectory)" -ArgumentList ($ArgumentList.ToArray()) -WindowStyle Normal -Verb RunAs -PassThru + } +Else + { + #Determine the date and time we executed the function + $ScriptStartTime = (Get-Date) + + #Define Default Action Preferences + $Script:DebugPreference = 'SilentlyContinue' + $Script:ErrorActionPreference = 'Stop' + $Script:VerbosePreference = 'SilentlyContinue' + $Script:WarningPreference = 'Continue' + $Script:ConfirmPreference = 'None' + $Script:WhatIfPreference = $False + + #Load WMI Classes + $Baseboard = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Baseboard" -Property * + $Bios = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_Bios" -Property * + $ComputerSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_ComputerSystem" -Property * + $OperatingSystem = Get-WmiObject -Namespace "root\CIMv2" -Class "Win32_OperatingSystem" -Property * + $MSSystemInformation = Get-WmiObject -Namespace "root\WMI" -Class "MS_SystemInformation" -Property * + + #Retrieve property values + $OSArchitecture = $($OperatingSystem.OSArchitecture).Replace("-bit", "").Replace("32", "86").Insert(0,"x").ToUpper() + + #Define variable(s) + $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)} + [System.IO.FileInfo]$ScriptPath = "$($MyInvocation.MyCommand.Definition)" + [System.IO.DirectoryInfo]$ScriptDirectory = "$($ScriptPath.Directory.FullName)" + [System.IO.DirectoryInfo]$ContentDirectory = "$($ScriptDirectory.FullName)\Content" + [System.IO.DirectoryInfo]$FunctionsDirectory = "$($ScriptDirectory.FullName)\Functions" + [System.IO.DirectoryInfo]$ModulesDirectory = "$($ScriptDirectory.FullName)\Modules" + [System.IO.DirectoryInfo]$ToolsDirectory = "$($ScriptDirectory.FullName)\Tools" + [System.IO.DirectoryInfo]$ToolsDirectory_OSAll = "$($ToolsDirectory.FullName)\All" + [System.IO.DirectoryInfo]$ToolsDirectory_OSArchSpecific = "$($ToolsDirectory.FullName)\$($OSArchitecture)" + [System.IO.DirectoryInfo]$System32Directory = [System.Environment]::SystemDirectory + [System.IO.DirectoryInfo]$ProgramFilesDirectory = "$($Env:SystemDrive)\Program Files" + [System.IO.DirectoryInfo]$ProgramFilesx86Directory = "$($Env:SystemDrive)\Program Files (x86)" + [System.IO.FileInfo]$PowershellPath = "$($System32Directory.FullName)\WindowsPowershell\v1.0\powershell.exe" + [System.IO.DirectoryInfo]$System32Directory = "$([System.Environment]::SystemDirectory)" + $IsWindowsPE = Test-Path -Path 'HKLM:\SYSTEM\ControlSet001\Control\MiniNT' -ErrorAction SilentlyContinue + [System.Text.RegularExpressions.RegexOptions[]]$RegexOptions = [System.Text.RegularExpressions.RegexOptions]::IgnoreCase, [System.Text.RegularExpressions.RegexOptions]::Multiline + [ScriptBlock]$GetRandomGUID = {[System.GUID]::NewGUID().GUID.ToString().ToUpper()} + [String]$ParameterSetName = "$($PSCmdlet.ParameterSetName)" + $TextInfo = (Get-Culture).TextInfo + $Script:LASTEXITCODE = 0 + $TerminationCodes = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $TerminationCodes.Add('Success', @(0)) + $TerminationCodes.Add('Warning', @(5000..5999)) + $TerminationCodes.Add('Error', @(6000..6999)) + $Script:WarningCodeIndex = 0 + [ScriptBlock]$GetAvailableWarningCode = {$TerminationCodes.Warning[$Script:WarningCodeIndex]; $Script:WarningCodeIndex++} + $Script:ErrorCodeIndex = 0 + [ScriptBlock]$GetAvailableErrorCode = {$TerminationCodes.Error[$Script:ErrorCodeIndex]; $Script:ErrorCodeIndex++} + $LoggingDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $LoggingDetails.Add('LogMessage', $Null) + $LoggingDetails.Add('WarningMessage', $Null) + $LoggingDetails.Add('ErrorMessage', $Null) + $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] + $CommonParameterList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::CommonParameters) + $CommonParameterList.AddRange([System.Management.Automation.PSCmdlet]::OptionalCommonParameters) + + #Define the error handling definition + [ScriptBlock]$ErrorHandlingDefinition = { + If (($Null -ieq $Script:LASTEXITCODE) -or ($Script:LASTEXITCODE -eq 0)) + { + [Int]$Script:LASTEXITCODE = $GetAvailableErrorCode.InvokeReturnAsIs() + } + + $ErrorMessageList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $ErrorMessageList.Add('Message', $_.Exception.Message) + $ErrorMessageList.Add('Category', $_.Exception.ErrorRecord.FullyQualifiedErrorID) + $ErrorMessageList.Add('ExitCode', $Script:LASTEXITCODE) + $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-Verbose -Message ($LoggingDetails.ErrorMessage) -Verbose + } + + Switch (($ContinueOnError.IsPresent -eq $False) -or ($ContinueOnError -eq $False)) + { + {($_ -eq $True)} + { + Throw + } + } + } + + #Log task sequence variables if debug mode is enabled within the task sequence + Try + { + [System.__ComObject]$TSEnvironment = New-Object -ComObject "Microsoft.SMS.TSEnvironment" + + If ($Null -ine $TSEnvironment) + { + $IsRunningTaskSequence = $True + + [Boolean]$IsConfigurationManagerTaskSequence = [String]::IsNullOrEmpty($TSEnvironment.Value("_SMSTSPackageID")) -eq $False + + Switch ($IsConfigurationManagerTaskSequence) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A Microsoft Endpoint Configuration Manager (MECM) task sequence was detected." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + {($_ -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A Microsoft Deployment Toolkit (MDT) task sequence was detected." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + } + Catch + { + $IsRunningTaskSequence = $False + } + + #Determine default parameter value(s) + Switch ($True) + { + {([String]::IsNullOrEmpty($LogDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($LogDirectory) -eq $True)} + { + Switch ($IsRunningTaskSequence) + { + {($_ -eq $True)} + { + Switch ($IsConfigurationManagerTaskSequence) + { + {($_ -eq $True)} + { + [String]$_SMSTSLogPath = "$($TSEnvironment.Value('_SMSTSLogPath'))" + } + + {($_ -eq $False)} + { + [String]$_SMSTSLogPath = "$($TSEnvironment.Value('LogPath'))" + } + } + + Switch ([String]::IsNullOrEmpty($_SMSTSLogPath)) + { + {($_ -eq $True)} + { + [System.IO.DirectoryInfo]$TSLogDirectory = "$($Env:Windir)\Temp\SMSTSLog" + } + + {($_ -eq $False)} + { + Switch ($True) + { + {(Test-Path -Path ($_SMSTSLogPath) -PathType Container)} + { + [System.IO.DirectoryInfo]$TSLogDirectory = ($_SMSTSLogPath) + } + + {(Test-Path -Path ($_SMSTSLogPath) -PathType Leaf)} + { + [System.IO.DirectoryInfo]$TSLogDirectory = Split-Path -Path ($_SMSTSLogPath) -Parent + } + } + } + } + + [System.IO.DirectoryInfo]$LogDirectory = "$($TSLogDirectory.FullName)\$($ScriptPath.BaseName)" + } + + {($_ -eq $False)} + { + Switch ($IsWindowsPE) + { + {($_ -eq $True)} + { + [System.IO.FileInfo]$MDTBootImageDetectionPath = "$($Env:SystemDrive)\Deploy\Scripts\Litetouch.wsf" + + [Boolean]$MDTBootImageDetected = Test-Path -Path ($MDTBootImageDetectionPath.FullName) + + Switch ($MDTBootImageDetected) + { + {($_ -eq $True)} + { + [System.IO.DirectoryInfo]$LogDirectory = "$($Env:SystemDrive)\MININT\SMSOSD\OSDLOGS\$($ScriptPath.BaseName)" + } + + {($_ -eq $False)} + { + [System.IO.DirectoryInfo]$LogDirectory = "$($Env:Windir)\Temp\SMSTSLog" + } + } + } + + {($_ -eq $False)} + { + [System.IO.DirectoryInfo]$ApplicationDataRootDirectory = "$($Env:ProgramData)\Invoke-DriverPackageCreator" + + [System.IO.DirectoryInfo]$LogDirectory = "$($ApplicationDataRootDirectory.FullName)\Logs" + } + } + } + } + } + } + + #Start transcripting (Logging) + [System.IO.FileInfo]$ScriptLogPath = "$($LogDirectory.FullName)\$($ScriptPath.BaseName)_$($GetCurrentDateFileFormat.Invoke()).log" + If ($ScriptLogPath.Directory.Exists -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($ScriptLogPath.Directory.FullName)} + Start-Transcript -Path "$($ScriptLogPath.FullName)" -Force -WhatIf:$False + + #Log any useful information + [String]$CmdletName = $MyInvocation.MyCommand.Name + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Execution of script `"$($CmdletName)`" began on $($ScriptStartTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script Path = $($ScriptPath.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [String[]]$AvailableScriptParameters = (Get-Command -Name ($ScriptPath.FullName)).Parameters.GetEnumerator() | Where-Object {($_.Value.Name -inotin $CommonParameterList)} | ForEach-Object {"-$($_.Value.Name):$($_.Value.ParameterType.Name)"} + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Available Script 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 Script Parameter(s) = $($SuppliedScriptParameters -Join ', ')" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($True) + { + {([String]::IsNullOrEmpty($ParameterSetName) -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Parameter Set Name = $($ParameterSetName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Command Line: $((Get-WMIObject -Namespace 'Root\CIMv2' -Class 'Win32_Process' -Filter "ProcessID = $($PID)").CommandLine)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($PSBoundParameters.Count) command line parameter(s) were specified." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $OperatingSystemDetailsTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $OperatingSystemDetailsTable.ProductName = $OperatingSystem.Caption -ireplace '(Microsoft\s+)', '' + $OperatingSystemDetailsTable.Version = $OperatingSystem.Version + $OperatingSystemDetailsTable.Architecture = $OperatingSystem.OSArchitecture + + $OperatingSystemRegistryDetails = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = ''; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'UBR'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseVersion'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'ReleaseID'; Value = $Null})) + $OperatingSystemRegistryDetails.Add((New-Object -TypeName 'PSObject' -Property @{Alias = 'ReleaseID'; Path = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; ValueName = 'DisplayVersion'; Value = $Null})) + + ForEach ($OperatingSystemRegistryDetail In $OperatingSystemRegistryDetails) + { + $OperatingSystemRegistryDetail.Value = Try {(Get-Item -Path $OperatingSystemRegistryDetail.Path).GetValue($OperatingSystemRegistryDetail.ValueName)} Catch {} + + :NextOSDetail Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Value) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Value) -eq $False)) + { + {($_ -eq $True)} + { + Switch ($OperatingSystemRegistryDetail.ValueName) + { + {($_ -ieq 'UBR')} + { + $OperatingSystemDetailsTable.Version = $OperatingSystemDetailsTable.Version + '.' + $OperatingSystemRegistryDetail.Value + + Break NextOSDetail + } + } + + Switch (([String]::IsNullOrEmpty($OperatingSystemRegistryDetail.Alias) -eq $False) -and ([String]::IsNullOrWhiteSpace($OperatingSystemRegistryDetail.Alias) -eq $False)) + { + {($_ -eq $True)} + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.Alias) = $OperatingSystemRegistryDetail.Value + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + Default + { + $OperatingSystemDetailsTable.$($OperatingSystemRegistryDetail.ValueName) = $OperatingSystemRegistryDetail.Value + } + } + } + + ForEach ($OperatingSystemDetail In $OperatingSystemDetailsTable.GetEnumerator()) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($OperatingSystemDetail.Key): $($OperatingSystemDetail.Value)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Powershell Version: $($PSVersionTable.PSVersion.ToString())" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ExecutionPolicyList = Get-ExecutionPolicy -List + + For ($ExecutionPolicyListIndex = 0; $ExecutionPolicyListIndex -lt $ExecutionPolicyList.Count; $ExecutionPolicyListIndex++) + { + $ExecutionPolicy = $ExecutionPolicyList[$ExecutionPolicyListIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The powershell execution policy is currently set to `"$($ExecutionPolicy.ExecutionPolicy.ToString())`" for the `"$($ExecutionPolicy.Scope.ToString())`" scope." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + #Log hardware information + $MSSystemInformationMembers = $MSSystemInformation.PSObject.Properties | Where-Object {($_.MemberType -imatch '^NoteProperty$|^Property$') -and ($_.Name -imatch '^Base.*|Bios.*|System.*$') -and ($_.Name -inotmatch '^.*Major.*|.*Minor.*|.*Properties.*$')} | Sort-Object -Property @('Name') + + Switch ($MSSystemInformationMembers.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to display device information properties from the `"$($MSSystemInformation.__CLASS)`" WMI class located within the `"$($MSSystemInformation.__NAMESPACE)`" WMI namespace. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + ForEach ($MSSystemInformationMember In $MSSystemInformationMembers) + { + [String]$MSSystemInformationMemberName = ($MSSystemInformationMember.Name) + [String]$MSSystemInformationMemberValue = $MSSystemInformation.$($MSSystemInformationMemberName) + + Switch ([String]::IsNullOrEmpty($MSSystemInformationMemberValue)) + { + {($_ -eq $False)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($MSSystemInformationMemberName) = $($MSSystemInformationMemberValue)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The `"MSSystemInformation`" WMI class could not be found." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #region Log Cleanup + [Int]$MaximumLogHistory = 3 + + $LogList = Get-ChildItem -Path ($LogDirectory.FullName) -Filter "$($ScriptPath.BaseName)_*" -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $SortedLogList = $LogList | Sort-Object -Property @('LastWriteTime') -Descending | Select-Object -Skip ($MaximumLogHistory) + + Switch ($SortedLogList.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + + For ($SortedLogListIndex = 0; $SortedLogListIndex -lt $SortedLogList.Count; $SortedLogListIndex++) + { + Try + { + $Log = $SortedLogList[$SortedLogListIndex] + + $LogAge = New-TimeSpan -Start ($Log.LastWriteTime) -End (Get-Date) + + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to cleanup log file `"$($Log.FullName)`". Please Wait... [Last Modified: $($Log.LastWriteTime.ToString($DateTimeMessageFormat))] [Age: $($LogAge.Days.ToString()) day(s); $($LogAge.Hours.ToString()) hours(s); $($LogAge.Minutes.ToString()) minute(s); $($LogAge.Seconds.ToString()) second(s)]." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + + $Null = [System.IO.File]::Delete($Log.FullName) + } + Catch + { + + } + } + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There are $($SortedLogList.Count) log file(s) requiring cleanup." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + #endregion + + #region Import Dependency Modules + If (($ModulesDirectory.Exists -eq $True) -and ($ModulesDirectory.GetDirectories().Count -gt 0)) + { + $Modules = Get-Module -Name "$($ModulesDirectory.FullName)\*" -ListAvailable -ErrorAction Stop + + $ModuleGroups = $Modules | Group-Object -Property @('Name') + + ForEach ($ModuleGroup In $ModuleGroups) + { + $LatestModuleVersion = $ModuleGroup.Group | Sort-Object -Property @('Version') -Descending | Select-Object -First 1 + + If ($Null -ine $LatestModuleVersion) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to import dependency powershell module `"$($LatestModuleVersion.Name)`" [Version: $($LatestModuleVersion.Version.ToString())]. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Try {$Null = Import-Module -Name "$($LatestModuleVersion.Path)" -Global -DisableNameChecking -Force -Verbose:$False} Catch {} + } + } + } + #endregion + + #region Dot Source Dependency Scripts + #Dot source any additional script(s) from the functions directory. This will provide flexibility to add additional functions without adding complexity to the main script and to maintain function consistency. + Try + { + If ($FunctionsDirectory.Exists -eq $True) + { + $AdditionalFunctionsFilter = New-Object -TypeName 'System.Collections.Generic.List[String]' + $AdditionalFunctionsFilter.Add('*.ps1') + + $AdditionalFunctionsToImport = Get-ChildItem -Path "$($FunctionsDirectory.FullName)" -Include ($AdditionalFunctionsFilter) -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + $AdditionalFunctionsToImportCount = $AdditionalFunctionsToImport | Measure-Object | Select-Object -ExpandProperty Count + + If ($AdditionalFunctionsToImportCount -gt 0) + { + ForEach ($AdditionalFunctionToImport In $AdditionalFunctionsToImport) + { + Try + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to dot source the functions contained within the dependency script `"$($AdditionalFunctionToImport.Name)`". Please Wait... [Script Path: `"$($AdditionalFunctionToImport.FullName)`"]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + . "$($AdditionalFunctionToImport.FullName)" + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + } + } + } + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + #endregion + + #region Load any required libraries + [System.IO.DirectoryInfo]$LibariesDirectory = "$($FunctionsDirectory.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) -Verbose + + $Null = [System.Reflection.Assembly]::Load($LibraryBytes) + } + } + } + } + } + } + } + #endregion + + #Perform script action(s) + Try + { + #If necessary, create, get, and or set any task sequence variable(s). + Switch ($IsRunningTaskSequence) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A task sequence is currently running." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $TaskSequenceVariableRetrievalList = New-Object -TypeName 'System.Collections.Generic.List[String]' + + Switch ($TaskSequenceVariables.Count -gt 0) + { + {($_ -eq $True)} + { + ForEach ($TaskSequenceVariable In $TaskSequenceVariables) + { + $TaskSequenceVariableRetrievalList.Add($TaskSequenceVariable) + } + } + } + + $TSVariableTable = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to retrieve the task sequence variable list. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch ($TaskSequenceVariableRetrievalList.Count -gt 0) + { + {($_ -eq $True)} + { + $TSVariableList = $TSEnvironment.GetVariables() | Where-Object {($_ -iin $TaskSequenceVariableRetrievalList)} | Sort-Object + } + + Default + { + $TSVariableList = $TSEnvironment.GetVariables() | Sort-Object + } + } + + ForEach ($TSVariable In $TSVariableList) + { + $TSVariableName = $TSVariable + $TSVariableValue = $TSEnvironment.Value($TSVariableName) + + Switch ($True) + { + {($TSVariableName -inotmatch '(^_SMSTSTaskSequence$)|(^TaskSequence$)|(^.*Pass.*word.*$)')} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to retrieve the value of task sequence variable `"$($TSVariableName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) + } + + {($TSVariableTable.Contains($TSVariableName) -eq $False)} + { + $TSVariableTable.Add($TSVariableName, $TSVariableValue) + } + } + } + } + } + + #Set default parameter value(s) + Switch ($IsRunningTaskSequence) + { + {($_ -eq $True)} + { + Switch ($True) + { + {([String]::IsNullOrEmpty($DriverPackageRootDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($DriverPackageRootDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$DriverPackageRootDirectory = "$($TSVariableTable.DEPLOYROOT)\Out-Of-Box-Driver-Packages" + } + + {([String]::IsNullOrEmpty($DriverPackageMetadataPath) -eq $True) -or ([String]::IsNullOrWhiteSpace($DriverPackageMetadataPath) -eq $True)} + { + [System.IO.FileInfo]$DriverPackageMetadataPath = "$($DriverPackageRootDirectory.FullName)\Metadata\DriverPackageList.xml" + } + } + } + + Default + { + Switch ($True) + { + {([String]::IsNullOrEmpty($DriverPackageRootDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($DriverPackageRootDirectory) -eq $True)} + { + [System.IO.DirectoryInfo]$DriverPackageRootDirectory = "C:\ProgramData\Invoke-DriverPackageCreator\DriverPackages" + } + + {([String]::IsNullOrEmpty($DriverPackageMetadataPath) -eq $True) -or ([String]::IsNullOrWhiteSpace($DriverPackageMetadataPath) -eq $True)} + { + [System.IO.FileInfo]$DriverPackageMetadataPath = "$($DriverPackageRootDirectory.FullName)\Metadata\DriverPackageList.xml" + } + } + } + } + + #Determine which driver package(s) are applicable to the deployed operating system and product ID + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - A task sequence is currently running." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageDownloadList = New-Object -TypeName 'System.Collections.Generic.List[PSObject]' + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to read the contents of `"$($DriverPackageMetadataPath.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageMetadataContents = [System.IO.File]::ReadAllText($DriverPackageMetadataPath.FullName) + + $DriverPackageXMLDocument = New-Object -TypeName 'System.Xml.XmlDocument' + $DriverPackageXMLDocument.LoadXml($DriverPackageMetadataContents) + + $DriverPackageMetadata = $DriverPackageXMLDocument.Metadata + + $ManufacturerList = $DriverPackageMetadata.ManufacturerList.Manufacturer + + $ManufacturerDetails = $ManufacturerList | Where-Object {($_.Enabled -eq $True) -and ($MSSystemInformation.SystemManufacturer -imatch $_.EligibilityExpression)} + + $ManufacturerDetailsCount = ($ManufacturerDetails | Measure-Object).Count + + Switch ($ManufacturerDetailsCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The current system manufacturer of `"$($MSSystemInformation.SystemManufacturer)`" matches the manufacturer list for `"$($ManufacturerDetails.Name)`" [Eligibility Expression: $($ManufacturerDetails.EligibilityExpression)]." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $ProductIDExpression = [Scriptblock]::Create($ManufacturerDetails.ProductIDExpression) + + $ProductID = ($MSSystemInformation | Select-Object -Property ($ProductIDExpression.Invoke())).ProductID + + $ModelList = $ManufacturerDetails.ModelList.Model + + $ModelDetails = $ModelList | Where-Object {($_.Enabled -eq $True) -and ($_.ProductIDList.ProductID -icontains $ProductID)} + + $ModelDetailsCount = ($ModelDetails | Measure-Object).Count + + Switch ($ModelDetailsCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ModelDetailsCount) model(s) contain the product ID of `"$($ProductID)`"." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + ForEach ($Model In $ModelDetails) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The current product ID of `"$($ProductID)`" is contained within the product ID list for the `"$($Model.Name)`" device model." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Product ID List: $($Model.ProductIDList.ProductID -Join ', ')]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $OperatingSystemPropertyList = New-Object -TypeName 'System.Collections.Generic.List[Object]' + $OperatingSystemPropertyList.Add(@{Name = 'MinimumVersion'; Expression = {[Version]$_.MinimumVersion}}) + $OperatingSystemPropertyList.Add('*') + + $OperatingSystemList = $ModelDetails.OperatingSystemList.OperatingSystem | Select-Object -Property ($OperatingSystemPropertyList) -ExcludeProperty @('MinimumVersion') + + $InvokeRegistryHiveActionParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + + $FixedVolumeList = [System.IO.DriveInfo]::GetDrives() | Where-Object {($_.DriveType -iin @('Fixed')) -and ($_.IsReady -eq $True) -and ($_.Name.TrimEnd('\') -inotin @($Env:SystemDrive)) -and (([String]::IsNullOrEmpty($_.Name) -eq $False) -or ([String]::IsNullOrWhiteSpace($_.Name) -eq $False))} | Sort-Object -Property @('TotalSize') + + :FixedVolumeLoop ForEach ($FixedVolume In $FixedVolumeList) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to check fixed volume `"$($FixedVolume.Name.TrimEnd('\'))`" for a valid installation of Windows." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [System.IO.DirectoryInfo]$WindowsDirectory = "$($FixedVolume.Name.TrimEnd('\'))\Windows" + + Switch ([System.IO.Directory]::Exists($WindowsDirectory.FullName)) + { + {($_ -eq $True)} + { + $WindowsDirectoryItemList = Get-ChildItem -Path ($WindowsDirectory.FullName) -ErrorAction SilentlyContinue + + $WindowsDirectoryItemListCount = ($WindowsDirectoryItemList | Measure-Object).Count + + Switch (($WindowsDirectoryItemListCount -ge 2) -and ($WindowsDirectoryItemList | Where-Object {($_.Name -ieq 'explorer.exe')})) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Fixed volume `"$($FixedVolume.Name.TrimEnd('\'))`" contains a valid installation of Windows." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $WindowsImageDriveInfo = New-Object -TypeName 'System.IO.DriveInfo' -ArgumentList "$($FixedVolume.Name.TrimEnd('\'))" + + $InvokeRegistryHiveActionParameters.HivePath = "$($WindowsImageDriveInfo.Name.TrimEnd('\').Toupper())\Windows\System32\Config\SOFTWARE" + + Break FixedVolumeLoop + } + } + } + } + } + + #$SecondLargestVolume = ($FixedVolumeList | Sort-Object -Property 'TotalSize' -Descending)[1] + + #[System.IO.DirectoryInfo]$DriverPackageDownloadDirectory = "$($SecondLargestVolume.Name.TrimEnd('\'))\Downloads" + + [System.IO.DirectoryInfo]$DriverPackageDownloadDirectory = "$($WindowsImageDriveInfo.Name.TrimEnd('\').Toupper())\Downloads" + + $InvokeRegistryHiveActionParameters.KeyPath = New-Object -TypeName 'System.Collections.Generic.List[String]' + $InvokeRegistryHiveActionParameters.KeyPath.Add('Root\Microsoft\Windows NT\CurrentVersion') + $InvokeRegistryHiveActionParameters.ValueNameExpression = New-Object -TypeName 'System.Collections.Generic.List[Regex]' + $InvokeRegistryHiveActionParameters.ValueNameExpression.Add('.*') + $InvokeRegistryHiveActionParameters.ContinueOnError = $False + $InvokeRegistryHiveActionParameters.Verbose = $True + + $InvokeRegistryHiveActionResult = Invoke-RegistryHiveAction @InvokeRegistryHiveActionParameters + + $WindowsImageDetails = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $WindowsImageDetails.MajorVersionNumber = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'CurrentMajorVersionNumber')}).Value + $WindowsImageDetails.MinorVersionNumber = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'CurrentMinorVersionNumber')}).Value + $WindowsImageDetails.BuildNumber = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'CurrentBuildNumber')}).Value + $WindowsImageDetails.RevisionNumber = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'UBR')}).Value + $WindowsImageDetails.Version = New-Object -TypeName 'System.Version' -ArgumentList @($WindowsImageDetails.MajorVersionNumber, $WindowsImageDetails.MinorVersionNumber, $WindowsImageDetails.BuildNumber, $WindowsImageDetails.RevisionNumber) + $WindowsImageDetails.ReleaseNumber = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'ReleaseID')}).Value + $WindowsImageDetails.ReleaseID = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'DisplayVersion')}).Value + $WindowsImageDetails.BuildLabEX = ($InvokeRegistryHiveActionResult[0].ValueList | Where-Object {($_.Name -ieq 'BuildLabEX')}).Value + + ForEach ($WindowsImageDetail In $WindowsImageDetails.GetEnumerator()) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Deployed Operating System - $($WindowsImageDetail.Key): $($WindowsImageDetail.Value)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $OperatingSystemCriteria = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + + Switch ($WindowsImageDetails.BuildLabEX) + { + {($_ -imatch '.*amd64.*')} + { + $OperatingSystemCriteria.Architecture = 'X64' + } + + Default + { + $OperatingSystemCriteria.Architecture = 'X86' + } + } + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Deployed Operating System - Architecture: $($OperatingSystemCriteria.Architecture)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $OperatingSystemCriteria.Version = $WindowsImageDetails.Version + + $OperatingSystemDetails = $OperatingSystemList | Where-Object {($_.Enabled -eq $True) -and ($_.Architecture -ieq $OperatingSystemCriteria.Architecture) -and (($OperatingSystemCriteria.Version.Major -eq $_.MinimumVersion.Major) -and ($OperatingSystemCriteria.Version.Minor -eq $_.MinimumVersion.Minor))} + + $OperatingSystemDetailsCount = ($OperatingSystemDetails | Measure-Object).Count + + Switch ($OperatingSystemDetailsCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The deployed operating system image kernel version of $($WindowsImageDetails.MajorVersionNumber).$($WindowsImageDetails.MinorVersionNumber) matches $($OperatingSystemDetails.Count) supported operating system(s)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + For ($OperatingSystemDetailIndex = 0; $OperatingSystemDetailIndex -lt $OperatingSystemDetailsCount; $OperatingSystemDetailIndex++) + { + $OperatingSystemDetailNumber = $OperatingSystemDetailIndex + 1 + + $OperatingSystemDetail = $OperatingSystemDetails[$OperatingSystemDetailIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Supported Operating System #$($OperatingSystemDetailNumber.ToString('00')) - $($OperatingSystemDetail.Name) - [Minimum Version: $($OperatingSystemDetail.MinimumVersion)]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $DriverPackagePropertyList = New-Object -TypeName 'System.Collections.Generic.List[Object]' + $DriverPackagePropertyList.Add(@{Name = 'MinimumOSVersion'; Expression = {[Version]$_.MinimumOSVersion}}) + $DriverPackagePropertyList.Add('*') + + $DriverPackages = $OperatingSystemDetails.DriverPackageList.DriverPackage | Select-Object -Property ($DriverPackagePropertyList) -ExcludeProperty @('MinimumOSVersion') + + Switch ($DisableDownLeveling.IsPresent) + { + {($_ -eq $True)} + { + $DriverPackageList = $DriverPackages | Where-Object {($_.Enabled -eq $True) -and (($OperatingSystemCriteria.Version.Major -eq $_.MinimumOSVersion.Major) -and ($OperatingSystemCriteria.Version.Minor -eq $_.MinimumOSVersion.Minor) -and ($_.MinimumOSVersion.Build -eq $OperatingSystemCriteria.Version.Build))} + } + + Default + { + $DriverPackageList = $DriverPackages | Where-Object {($_.Enabled -eq $True) -and (($OperatingSystemCriteria.Version.Major -eq $_.MinimumOSVersion.Major) -and ($OperatingSystemCriteria.Version.Minor -eq $_.MinimumOSVersion.Minor) -and ($_.MinimumOSVersion.Build -le $OperatingSystemCriteria.Version.Build))} + } + } + + $DriverPackageListCount = ($DriverPackageList | Measure-Object).Count + + Switch ($DriverPackageListCount -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackageListCount) driver package(s) were found." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + For ($DriverPackageListIndex = 0; $DriverPackageListIndex -lt $DriverPackageListCount; $DriverPackageListIndex++) + { + $DriverPackageListItemNumber = $DriverPackageListIndex + 1 + + $DriverPackageListItem = $DriverPackageList[$DriverPackageListIndex] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Driver Package #$($DriverPackageListItemNumber.ToString('00')) - $(Split-Path -Path $DriverPackageListItem.FilePath -Leaf) [Minimum Operating System Version: $($DriverPackageListItem.MinimumOSVersion)] [Release Date: $($DriverPackageListItem.ReleaseDate)]" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + + $DriverPackage = $DriverPackageList | Sort-Object -Property {[DateTime]$_.ReleaseDate} -Descending | Select-Object -First 1 + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Selected the most recent driver package of `"$(Split-Path -Path $DriverPackage.FilePath -Leaf)`" [Release Date: $($DriverPackage.ReleaseDate)]." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageDownloadProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DriverPackageDownloadProperties.PackagePath = "$($DriverPackageRootDirectory.FullName)\$($DriverPackage.FilePath)" -As [System.IO.FileInfo] + $DriverPackageDownloadProperties.MetadataPath = "$($DriverPackageRootDirectory.FullName)\$($DriverPackage.MetadataPath)" -As [System.IO.FileInfo] + $DriverPackageDownloadProperties.DownloadPath = "$($DriverPackageDownloadDirectory.FullName)\DriverPackages\$($DriverPackageDownloadProperties.PackagePath.Name)" -As [System.IO.FileInfo] + $DriverPackageDownloadProperties.Details = $Null + + Switch ([System.IO.File]::Exists($DriverPackageDownloadProperties.PackagePath.FullName)) + { + {($_ -eq $True)} + { + Switch ([System.IO.File]::Exists($DriverPackageDownloadProperties.MetadataPath.FullName)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add driver package `"$($DriverPackageDownloadProperties.PackagePath.FullName)`" to the download list. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageDownloadProperties.Details = ConvertFrom-JSON -InputObject ([System.IO.File]::ReadAllText($DriverPackageDownloadProperties.MetadataPath.FullName)) + + $DriverPackageDownloadObject = New-Object -TypeName 'PSObject' -Property ($DriverPackageDownloadProperties) + + $DriverPackageDownloadList.Add($DriverPackageDownloadObject) + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The driver package metadata file `"$($DriverPackageProperties.MetadataPath.FullName)`" does not exist." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The driver package file `"$($DriverPackageProperties.PackagePath.FullName)`" does not exist." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackageListCount) driver package(s) were found." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($OperatingSystemDetails.Count) operating system(s) matching version `"$($OperatingSystemCriteria.Version.Major).$($OperatingSystemCriteria.Version.Minor)`" [Architecture: $($OperatingSystemCriteria.Architecture)] were found." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ModelDetailsCount) model(s) containing product ID `"$($ProductID)`" in their product ID list could be found." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($ManufacturerDetailsCount) manufacturers whose eligibility expression matches `"$($MSSystemInformation.SystemManufacturer)`" could be found." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #Process generic driver package(s) + [System.IO.DirectoryInfo]$GenericDriverPackageRootDirectory = "$($DriverPackageRootDirectory.FullName)\Generic" + + Switch ([System.IO.Directory]::Exists($GenericDriverPackageRootDirectory.FullName)) + { + {($_ -eq $True)} + { + $GenericDriverPackageMetadataFileList = Get-ChildItem -Path ($GenericDriverPackageRootDirectory.FullName) -Filter '*.json' -Recurse -Force | Where-Object {($_ -is [System.IO.FileInfo])} + + ForEach ($GenericDriverPackageMetadataFile In $GenericDriverPackageMetadataFileList) + { + $GenericDriverPackageMetadataFileContents = [System.IO.File]::ReadAllText($GenericDriverPackageMetadataFile.FullName, [System.Text.Encoding]::Default) + + $GenericDriverPackageDetails = Try {ConvertFrom-JSON -InputObject ($GenericDriverPackageMetadataFileContents)} Catch + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package metadata file `"$($GenericDriverPackageMetadataFile.FullName)`" contains invalid JSON. Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + + Continue + } + + Switch ($GenericDriverPackageDetails.Enabled) + { + {($_ -eq $True)} + { + Switch ((($MSSystemInformation.BaseBoardManufacturer -imatch $GenericDriverPackageDetails.Metadata.ManufacturerInclusionExpression) -or ($MSSystemInformation.SystemManufacturer -imatch $GenericDriverPackageDetails.Metadata.ManufacturerInclusionExpression)) -and (($MSSystemInformation.BaseBoardManufacturer -inotmatch $GenericDriverPackageDetails.Metadata.ManufacturerExclusionExpression) -or ($MSSystemInformation.SystemManufacturer -inotmatch $GenericDriverPackageDetails.Metadata.ManufacturerExclusionExpression))) + { + {($_ -eq $True)} + { + Switch ((($MSSystemInformation.BaseBoardProduct -imatch $GenericDriverPackageDetails.Metadata.ProductIDInclusionExpression) -or ($MSSystemInformation.SystemProductName -imatch $GenericDriverPackageDetails.Metadata.ProductIDInclusionExpression) -or ($MSSystemInformation.SystemSKU -imatch $GenericDriverPackageDetails.Metadata.ProductIDInclusionExpression) -or ($MSSystemInformation.SystemVersion -imatch $GenericDriverPackageDetails.Metadata.ProductIDInclusionExpression)) -and (($MSSystemInformation.BaseBoardProduct -inotmatch $GenericDriverPackageDetails.Metadata.ProductIDExclusionExpression) -and ($MSSystemInformation.SystemProductName -inotmatch $GenericDriverPackageDetails.Metadata.ProductIDExclusionExpression) -and ($MSSystemInformation.SystemSKU -inotmatch $GenericDriverPackageDetails.Metadata.ProductIDExclusionExpression) -and ($MSSystemInformation.SystemVersion -inotmatch $GenericDriverPackageDetails.Metadata.ProductIDExclusionExpression))) + { + {($_ -eq $True)} + { + $GenericDriverPackageOSVersion = $GenericDriverPackageDetails.Metadata.OSVersionMinimum -As [System.Version] + + Switch (($GenericDriverPackageOSVersion.Major -eq $OperatingSystemCriteria.Version.Major) -and ($GenericDriverPackageOSVersion.Minor -eq $OperatingSystemCriteria.Version.Minor) -and ($GenericDriverPackageOSVersion.Build -le $OperatingSystemCriteria.Version.Build)) + { + {($_ -eq $True)} + { + Switch ($OperatingSystemCriteria.Architecture -imatch $GenericDriverPackageDetails.Metadata.OSArchitectureExpression) + { + {($_ -eq $True)} + { + $GenericDriverPackageDownloadProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $GenericDriverPackageDownloadProperties.PackagePath = "$($GenericDriverPackageRootDirectory.FullName)\$($GenericDriverPackageDetails.Metadata.FilePath)" -As [System.IO.FileInfo] + $GenericDriverPackageDownloadProperties.MetadataPath = "$($GenericDriverPackageRootDirectory.FullName)\$($GenericDriverPackageDetails.Metadata.MetadataPath)" -As [System.IO.FileInfo] + $GenericDriverPackageDownloadProperties.DownloadPath = "$($DriverPackageDownloadDirectory.FullName)\DriverPackages\$($GenericDriverPackageDownloadProperties.PackagePath.Name)" -As [System.IO.FileInfo] + $GenericDriverPackageDownloadProperties.Details = $Null + + Switch ([System.IO.File]::Exists($GenericDriverPackageDownloadProperties.PackagePath.FullName)) + { + {($_ -eq $True)} + { + Switch ([System.IO.File]::Exists($GenericDriverPackageDownloadProperties.MetadataPath.FullName)) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to add generic driver package `"$($GenericDriverPackageDownloadProperties.PackagePath.FullName)`" to the download list. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $GenericDriverPackageDownloadProperties.Details = ConvertFrom-JSON -InputObject ([System.IO.File]::ReadAllText($GenericDriverPackageDownloadProperties.MetadataPath.FullName)) + + $GenericDriverPackageDownloadObject = New-Object -TypeName 'PSObject' -Property ($GenericDriverPackageDownloadProperties) + + $DriverPackageDownloadList.Add($GenericDriverPackageDownloadObject) + } + + Default + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the generic driver package metadata file `"$($GenericDriverPackageProperties.MetadataPath.FullName)`" does not exist." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the driver package file `"$($GenericDriverPackageProperties.PackagePath.FullName)`" does not exist." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the deployed operating system architecture of `"$($OperatingSystemCriteria.Architecture)`" does not match the oeprating system expression of `"$($GenericDriverPackageDetails.OSArchitectureExpression)`". Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the minimum required operating system version of `"$($GenericDriverPackageOSVersion.ToString())`" is not less than or equal to the deployed operating system version of `"$($OperatingSystemCriteria.Version.ToString())`". Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the device product ID either does not match `"$($GenericDriverPackageDetails.Metadata.ProductIDInclusionExpression)`" or matches `"$($GenericDriverPackageDetails.Metadata.ProductIDExclusionExpression)`". Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because the device manufacturer either does not match `"$($GenericDriverPackageDetails.Metadata.ManufacturerInclusionExpression)`" or matches `"$($GenericDriverPackageDetails.Metadata.ManufacturerExclusionExpression)`". Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - The generic driver package of `"$($GenericDriverPackageDetails.Metadata.Name)`" is excluded because it has been disabled. Skipping..." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + } + + #Download driver packages + Switch ($True) + { + {($IsRunningTaskSequence -eq $True) -and ($IsConfigurationManagerTaskSequence -eq $True)} + { + #Use an alternative way of downloading the driver package content + } + + Default + { + Switch ($DriverPackageDownloadList.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackageDownloadList.Count) driver package(s) need to be downloaded." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackagesToApply = New-Object -TypeName 'System.Collections.Generic.List[System.IO.FileInfo]' + + ForEach ($DriverPackageDownload In $DriverPackageDownloadList) + { + $CopyItemWithProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $CopyItemWithProgressParameters.Path = $DriverPackageDownload.PackagePath.FullName + $CopyItemWithProgressParameters.Destination = $DriverPackageDownload.DownloadPath.Directory.FullName + $CopyItemWithProgressParameters.Force = $True + $CopyItemWithProgressParameters.SegmentSize = $SegmentSize + $CopyItemWithProgressParameters.RandomDelay = $RandomDelay.IsPresent + $CopyItemWithProgressParameters.ContinueOnError = $False + $CopyItemWithProgressParameters.Verbose = $True + + $CopyItemWithProgressResult = Copy-ItemWithProgress @CopyItemWithProgressParameters + + $DriverPackagesToApply.Add($CopyItemWithProgressResult.Destination) + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackageDownloadList.Count) driver package(s) need to be downloaded." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + } + } + + #Apply the downloaded driver packages to the Windows installation + Switch ($DriverPackagesToApply.Count -gt 0) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackagesToApply.Count) driver package(s) need to be applied to the Windows installation contained on volume `"$($WindowsImageDriveInfo.Name.TrimEnd('\').Toupper())`"." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + [System.IO.DirectoryInfo]$WindowsImageMountRootDirectory = "$($WindowsImageDriveInfo.Name.TrimEnd('\'))\WIMMount" + + [System.IO.DirectoryInfo]$DISMLogDirectory = "$($LogDirectory.FullName)\DISM" + + If ([System.IO.Directory]::Exists($DISMLogDirectory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DISMLogDirectory.FullName)} + + $FileSystemObject = New-Object -ComObject 'Scripting.FileSystemObject' + + For ($DriverPackagesToApplyIndex = 0; $DriverPackagesToApplyIndex -lt $DriverPackagesToApply.Count; $DriverPackagesToApplyIndex++) + { + $DriverPackageToApply = $DriverPackagesToApply[$DriverPackagesToApplyIndex] + + $DriverPackageToApplyNumber = $DriverPackagesToApplyIndex + 1 + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process driver pack image #$($DriverPackageToApplyNumber.ToString('00')) - $($DriverPackageToApply.FullName)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageImageList = Get-WindowsImage -ImagePath "$($DriverPackageToApply.FullName)" + + ForEach ($DriverPackageImage In $DriverPackageImageList) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to process driver pack image index $($DriverPackageImage.ImageIndex). Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DISMCommandProperties = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DISMCommandProperties.MountDirectory = "$($WindowsImageMountRootDirectory.FullName)\Image$($DriverPackageToApplyNumber.ToString('00'))\Index$($DriverPackageImage.ImageIndex.ToString('00'))" -As [System.IO.DirectoryInfo] + $DISMCommandProperties.LogLevel = 3 + + $DISMCommandList = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $DISMCommandList.MountImage = "/Mount-Image /ImageFile:`"$($DriverPackageImage.ImagePath)`" /Index:$($DriverPackageImage.ImageIndex) /MountDir:`"$($DISMCommandProperties.MountDirectory.FullName)`" /ReadOnly" + $DISMCommandList.AddDrivers = "/Image:$($WindowsImageDriveInfo.Name.TrimEnd('\')) /Add-Driver /Driver:`"$($DISMCommandProperties.MountDirectory.FullName)`" /Recurse" + $DISMCommandList.UnmountImage = "/Unmount-Image /MountDir:`"$($DISMCommandProperties.MountDirectory.FullName)`" /Discard" + + If ([System.IO.Directory]::Exists($DISMCommandProperties.MountDirectory.FullName) -eq $False) {$Null = [System.IO.Directory]::CreateDirectory($DISMCommandProperties.MountDirectory.FullName)} + + $DISMCommandCounter = 1 + + ForEach ($DISMCommand In $DISMCommandList.GetEnumerator()) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to execute the command(s) associated with the `"$($DISMCommand.Key)`" key. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $StartDISMCommandProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $StartDISMCommandProgressParameters.Activity = "Attempting to `"$($DISMCommand.Key)`" from index $($DriverPackageImage.ImageIndex) within $($FileSystemObject.GetFile($DriverPackageImage.ImagePath).ShortName). Please Wait..." + $StartDISMCommandProgressParameters.Status = $StartDISMCommandProgressParameters.Activity + $StartDISMCommandProgressParameters.PercentComplete = (($DISMCommandCounter / $DISMCommandList.Count) * 100) -As [Int] + + Write-Progress @StartDISMCommandProgressParameters + + $StartProcessWithOutputParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $StartProcessWithOutputParameters.FilePath = "$($System32Directory.FullName)\dism.exe" + $StartProcessWithOutputParameters.ArgumentList = $DISMCommand.Value + ' ' + "/LogPath:`"$($DISMLogDirectory.FullName)\DISM_$($DISMCommand.Key)_Image$($DriverPackageToApplyNumber.ToString('00'))_Index$($DriverPackageImage.ImageIndex.ToString('00')).log`" /LogLevel:$($DISMCommandProperties.LogLevel)" + $StartProcessWithOutputParameters.AcceptableExitCodeList = New-Object -TypeName 'System.Collections.Generic.List[String]' + $StartProcessWithOutputParameters.AcceptableExitCodeList.Add(0) + $StartProcessWithOutputParameters.AcceptableExitCodeList.Add(2) + $StartProcessWithOutputParameters.AcceptableExitCodeList.Add(50) + $StartProcessWithOutputParameters.CreateNoWindow = $True + $StartProcessWithOutputParameters.LogOutput = $False + $StartProcessWithOutputParameters.Verbose = $True + + Switch ($DISMCommand.Key) + { + {($_ -iin @('UnmountImage'))} + { + Switch ($Stage.IsPresent) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to stage driver content contained within image index $($DriverPackageImage.ImageIndex) locally on this device. Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Switch (([String]::IsNullOrEmpty($StagingRootDirectory) -eq $True) -or ([String]::IsNullOrWhiteSpace($StagingRootDirectory) -eq $True)) + { + {($_ -eq $True)} + { + [System.IO.DirectoryInfo]$DriverPackageStagingRootDirectory = "$($WindowsImageDriveInfo.Name.TrimEnd('\'))\DriverCache" + } + + Default + { + [System.IO.DirectoryInfo]$DriverPackageStagingRootDirectory = $StagingRootDirectory.FullName + } + } + + $CopyItemWithProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $CopyItemWithProgressParameters.Path = $DISMCommandProperties.MountDirectory.FullName + $CopyItemWithProgressParameters.Destination = "$($DriverPackageStagingRootDirectory.FullName)\Pkg$($DriverPackageToApplyNumber.ToString('00'))\Index$($DriverPackageImage.ImageIndex.ToString('00'))" + $CopyItemWithProgressParameters.Recurse = $True + $CopyItemWithProgressParameters.Force = $True + $CopyItemWithProgressParameters.SegmentSize = $SegmentSize + $CopyItemWithProgressParameters.RandomDelay = $RandomDelay.IsPresent + $CopyItemWithProgressParameters.ContinueOnError = $False + $CopyItemWithProgressParameters.Verbose = $False + + $DriverPackageStagingContentList = Copy-ItemWithProgress @CopyItemWithProgressParameters + + $DriverPackageStagingRootDirectoryDetails = $DriverPackageStagingRootDirectory.FullName -As [System.IO.DirectoryInfo] + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to hide the driver staging directory of `"$($DriverPackageStagingRootDirectory.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $DriverPackageStagingRootDirectoryDetails.Attributes = [System.IO.FileAttributes]::Hidden + } + } + + $Null = Start-ProcessWithOutput @StartProcessWithOutputParameters + } + + Default + { + $Null = Start-ProcessWithOutput @StartProcessWithOutputParameters + } + } + + $FinishDISMCommandProgressParameters = New-Object -TypeName 'System.Collections.Specialized.OrderedDictionary' + $FinishDISMCommandProgressParameters.Activity = $StartDISMCommandProgressParameters.Activity + $FinishDISMCommandProgressParameters.Completed = $True + + Write-Progress @FinishDISMCommandProgressParameters + + $DISMCommandCounter++ + } + } + } + + If ([System.IO.Directory]::Exists($WindowsImageMountRootDirectory.FullName) -eq $True) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove directory `"$($WindowsImageMountRootDirectory.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Try {$Null = [System.IO.Directory]::Delete($WindowsImageMountRootDirectory.FullName, $True)} Catch {} + } + } + + Default + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - $($DriverPackagesToApply.Count) driver package(s) need to be applied to the Windows installation contained on volume `"$($WindowsImageDriveInfo.Name.TrimEnd('\').Toupper())`"." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #If necessary, create, get, and or set any task sequence variable(s). + Switch ($IsRunningTaskSequence) + { + {($_ -eq $True)} + { + ForEach ($TSVariable In $TSVariableTable.GetEnumerator()) + { + [String]$TSVariableName = "$($TSVariable.Key)" + [String]$TSVariableCurrentValue = $TSEnvironment.Value($TSVariableName) + [String]$TSVariableNewValue = "$($TSVariable.Value -Join ',')" + + Switch ($TSVariableCurrentValue -ine $TSVariableNewValue) + { + {($_ -eq $True)} + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to set the task sequence variable of `"$($TSVariableName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $Null = $TSEnvironment.Value($TSVariableName) = "$($TSVariableNewValue)" + } + } + } + + $Null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($TSEnvironment) + } + + {($_ -eq $False)} + { + $LoggingDetails.WarningMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - There is no task sequence running." + Write-Verbose -Message ($LoggingDetails.WarningMessage) -Verbose + } + } + + #Cleanup the driver package download directory + If ([System.IO.Directory]::Exists($DriverPackageDownloadDirectory.FullName) -eq $True) + { + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Attempting to remove directory `"$($DriverPackageDownloadDirectory.FullName)`". Please Wait..." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Try {$Null = [System.IO.Directory]::Delete($DriverPackageDownloadDirectory.FullName, $True)} Catch {} + } + + $Script:LASTEXITCODE = $TerminationCodes.Success[0] + } + Catch + { + $ErrorHandlingDefinition.Invoke() + } + Finally + { + Try + { + #Determine the date and time the function completed execution + $ScriptEndTime = (Get-Date) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution of `"$($CmdletName)`" ended on $($ScriptEndTime.ToString($DateTimeLogFormat))" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + #Log the total script execution time + $ScriptExecutionTimespan = New-TimeSpan -Start ($ScriptStartTime) -End ($ScriptEndTime) + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Script execution took $($ScriptExecutionTimespan.Hours.ToString()) hour(s), $($ScriptExecutionTimespan.Minutes.ToString()) minute(s), $($ScriptExecutionTimespan.Seconds.ToString()) second(s), and $($ScriptExecutionTimespan.Milliseconds.ToString()) millisecond(s)" + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + $LoggingDetails.LogMessage = "$($GetCurrentDateTimeMessageFormat.Invoke()) - Exiting script `"$($ScriptPath.FullName)`" with exit code $($Script:LASTEXITCODE)." + Write-Verbose -Message ($LoggingDetails.LogMessage) -Verbose + + Stop-Transcript + } + Catch + { + + } + } + } \ No newline at end of file diff --git a/Invoke-DriverPackageDownload.vbs b/Invoke-DriverPackageDownload.vbs new file mode 100644 index 0000000..2190820 --- /dev/null +++ b/Invoke-DriverPackageDownload.vbs @@ -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 \ No newline at end of file diff --git a/Tools/X64/7z.dll b/Tools/X64/7z.dll new file mode 100644 index 0000000..b028a37 Binary files /dev/null and b/Tools/X64/7z.dll differ diff --git a/Tools/X64/7z.exe b/Tools/X64/7z.exe new file mode 100644 index 0000000..6a34fc6 Binary files /dev/null and b/Tools/X64/7z.exe differ diff --git a/Tools/X86/7z.dll b/Tools/X86/7z.dll new file mode 100644 index 0000000..02d0974 Binary files /dev/null and b/Tools/X86/7z.dll differ diff --git a/Tools/X86/7z.exe b/Tools/X86/7z.exe new file mode 100644 index 0000000..d5d5801 Binary files /dev/null and b/Tools/X86/7z.exe differ