Files
ADDAP/Invoke-ConfigurationGenerator.ps1
T
freedbygrace 3fcf06990c Initial Upload
2022-12-01 11:40:57 -05:00

913 lines
60 KiB
PowerShell

#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 = "<Model $($XMLAttributes -Join ' ') />"
Write-Output -InputObject ($XMLNode)
$Null = $XMLNode | Set-Clipboard -Verbose
### End Code Snippet ###
<ModelList>
### Copy and paste the generated text from the clipboard in between the 'ModelList' section for the desired manufacturer ###
</ModelList>
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
{
}
}
}