Public/Dell-DCU-Functions.ps1

<#Gary Blok - @gwblok - GARYTOWN.COM
 
 
DISCLAIMER: THIS IS NOT AN OFFICIAL DELL SCRIPT. I DO NOT WORK FOR DELL.
USE AT YOUR OWN RISK. I TAKE NO RESPONSIBILITY FOR ANYTHING THIS SCRIPT DOES. TEST IN A LAB FIRST.
ALL INFORMATION IS PUBLICLY AVAILABLE ON THE INTERNET. I JUST CONSOLIDATED IT INTO ONE SCRIPT.
 
 
#https://www.dell.com/support/manuals/en-us/command-update/dcu_rg/dell-command-%7C-update-cli-commands?guid=guid-92619086-5f7c-4a05-bce2-0d560c15e8ed&lang=en-us
 
# Summary of Functions:
 
# 1. Get-DellSupportedModels:
# - Retrieves a list of supported Dell models from the Dell Command Update XML catalog.
# - Returns an array of objects containing system ID, model name, URL, and date.
 
# 2. Get-DCUVersion:
# - Retrieves the version of Dell Command Update (DCU) installed on the system.
# - Returns the DCU version as a string or $false if DCU is not installed.
 
# 3. Get-DCUInstallDetails:
# - Retrieves installation details of Dell Command Update (DCU) from the registry.
# - Returns an object containing version, app type (Universal or Classic), and DCU path.
 
# 4. Get-DCUExitInfo:
# - Retrieves information about Dell Command Update (DCU) exit codes.
# - Takes an optional parameter for the DCU exit code to get specific information.
# - Returns an array of objects containing exit code, description, and resolution.
 
# 6. Set-DCUSettings:
# - Configures settings for Dell Command Update (DCU) using the dcu-cli.exe utility.
# - Supports settings like advancedDriverRestore, autoSuspendBitLocker, installationDeferral, systemRestartDeferral, scheduleAction, and scheduleAuto.
# - Writes logs for each configuration change.
 
# 7. Get-DCUSettings:
# - Lists the Settings that are already set by looking at Registry
# - has parameter -Reset to clear all settings
 
# 8. Invoke-DCU:
# - Invokes Dell Command Update (DCU) actions like scanning for updates or applying updates.
# - Supports parameters for updateSeverity, updateType, updateDeviceCategory, autoSuspendBitLocker, reboot, forceupdate, scan, and applyUpdates.
# - Builds the argument list based on the selected parameters and executes the DCU action.
 
# 9. Get-DCUUpdateList:
# - Retrieves a list of available updates from Dell Command Update (DCU) for the system.
# - Supports filtering by updateType, and updateDeviceCategory.
# - Returns an array of objects containing update details like severity, type, category, name, and release date.
 
# 10. Get-DellDeviceDetails:
# - Retrieves details of the Dell device like model, systemID.
# - Supports filtering by systemID and model name
 
# Change Log
 
24.9.9.1 - Modified logic in Get-DellDeviceDetails to allow it to work on non-dell devices when you provide a SKU or Model Name
25.2.11.1 - Changed the Logic on Get-DellBIOSUpdates -Check, some folks reported that it wasn't working with the -Latest switch.
25.4.30.1 - Added Get-DCUSettings Function
25.4.30.2 - Added Logic for Set-DCUSettings | if $scheduleAction -ne 'DownloadInstallAndNotify', then you can't set deferals... because there is nothing to defer. Breaks logic.
26.7.1.1 - Added Logic for Set-DCUSettings for delayDays | https://github.com/gwblok/garytown/issues/43
 
# 5. Set-DCUSettings:
    # Pull down Dell XML CAB used in Dell Command Update ,extract and Load
    if (!(Test-Path $DellCabExtractPath)){$null = New-Item -Path $DellCabExtractPath -ItemType Directory -Force}
    Write-Verbose "Downloading Dell Cab"
    Invoke-WebRequest -Uri "https://downloads.dell.com/catalog/CatalogIndexPC.cab" -OutFile $CabPathIndex -UseBasicParsing -Proxy $ProxyServer
# 6. Get-DCUSettings:
    Start-Sleep -Seconds 1
    if (test-path $DellCabExtractPath){Remove-Item -Path $DellCabExtractPath -Force -Recurse}
    $null = New-Item -Path $DellCabExtractPath -ItemType Directory
# 7. Invoke-DCU:
    $null = expand $CabPathIndex $DellCabExtractPath\CatalogIndexPC.xml
     
    Write-Verbose "Loading Dell Catalog XML.... can take awhile"
    [xml]$XMLIndex = Get-Content "$DellCabExtractPath\CatalogIndexPC.xml"
# 8. Get-DCUUpdateList:
        $SPInventory = New-Object -TypeName PSObject
        $SPInventory | Add-Member -MemberType NoteProperty -Name "SystemID" -Value "$($SupportedModel.SupportedSystems.Brand.Model.systemID)" -Force
        $SPInventory | Add-Member -MemberType NoteProperty -Name "Model" -Value "$($SupportedModel.SupportedSystems.Brand.Model.Display.'#cdata-section')" -Force
# 9. Get-DellDeviceDetails:
    $DellCabExtractPath = "$env:ProgramData\EMPS\DellCabDownloads\DellCabExtract"
     
    # Pull down Dell XML CAB used in Dell Command Update ,extract and Load
# 10. Get-DellDeviceDriverPack:
    Write-Verbose "Downloading Dell Cab"
    Invoke-WebRequest -Uri "https://downloads.dell.com/catalog/DriverPackCatalog.cab" -OutFile $CabPathIndex -UseBasicParsing -Proxy $ProxyServer
# 11. Get-DellBIOSUpdates:
    Start-Sleep -Seconds 1
    if (test-path $DellCabExtractPath){Remove-Item -Path $DellCabExtractPath -Force -Recurse}
    $null = New-Item -Path $DellCabExtractPath -ItemType Directory
    Write-Verbose "Expanding the Cab File..."
                }
                 
                if (!(Test-Path $TargetFilePathName)){
                    Invoke-WebRequest -Uri $TargetLink -OutFile $TargetFilePathName -UseBasicParsing -Verbose
                }
                #Confirm Download
                if (Test-Path $TargetFilePathName){
                    $LogFileName = ($TargetFilePathName.replace(".exe",".log")).Replace(".EXE",".log")
                    $Arguments = "/s /l=$LogFileName"
                    Write-Output "Starting DCU Install"
                    write-output "Log file = $LogFileName"
                    $Process = Start-Process "$TargetFilePathName" $Arguments -Wait -PassThru
                    write-output "Update Complete with Exitcode: $($Process.ExitCode)"
                    If($Process -ne $null -and $Process.ExitCode -eq '2'){
                        Write-Verbose "Reboot Required"
                    }
                }
                else{
                    Write-Verbose " FAILED TO DOWNLOAD DCU"
                }
            }
            else{
                Write-Output "Installed DCU: $CurrentVersion, Skipping Install"
 
            }
        }
        else{
            if ($Latest){
                Return $CommandUpdateAppsLatest
            }
            else{
                return $CommandUpdateApps
            }
        }
    }
    else{
        return "No DCU Found"
    }
 
}
 
 
function Set-DCUSettings {
    [CmdletBinding()]
    param (
    [ValidateSet('Enable','Disable')]
    [string]$advancedDriverRestore,
    [ValidateSet('Enable','Disable')]
    [string]$autoSuspendBitLocker = 'Enable',
    [ValidateSet('Enable','Disable')]
    [string]$installationDeferral,
    [ValidateRange(0,99)]
    [int]$deferralInstallInterval = 3,
    [ValidateRange(0,9)]
    [int]$deferralInstallCount = 5,
 
    [ValidateSet('Enable','Disable')]
    [string]$systemRestartDeferral,
    [ValidateRange(0,99)]
    [int]$deferralRestartInterval = 3,
    [ValidateRange(0,9)]
    [int]$deferralRestartCount = 5,
 
    #[ValidateSet('Enable','Disable')]
    #[string]$reboot = 'Disable',
    [ValidateSet('NotifyAvailableUpdates','DownloadAndNotify','DownloadInstallAndNotify')]
    [string]$scheduleAction = 'DownloadInstallAndNotify',
    [switch]$scheduleAuto,
    [string]$CustomCatalogPath, #Path to a custom catalog file for Offline DCU or just to lock in a specific catalog
    [ValidateRange(1,45)]
    [int]$ExcludeUpdatesFromLastNDays #Excludes updates released within the last N days from being applied
    )
     
    $DCUPath = (Get-DCUInstallDetails).DCUPath
    Write-Verbose "DCU Path: $DCUPath"
    $LogPath = "$env:SystemDrive\Users\Dell\EMPS\Logs"
    Write-Verbose "Log Path: $LogPath"
    $DateTimeStamp = Get-Date -Format "yyyyMMdd-HHmmss"
    #$ArgList = "$ActionVar $updateSeverityVar $updateTypeVar $updateDeviceCategoryVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-$Action.log`""
 
    if ($advancedDriverRestore){
        $advancedDriverRestoreVar = "-advancedDriverRestore=$advancedDriverRestore -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-advancedDriverRestore.log`""
        $ArgList = "/configure $advancedDriverRestoreVar"
        Write-Verbose $ArgList
        $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
    if ($autoSuspendBitLocker){
        $autoSuspendBitLockerVar = "-autoSuspendBitLocker=$autoSuspendBitLocker -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-autoSuspendBitLocker.log`""
        $ArgList = "/configure $autoSuspendBitLockerVar"
        Write-Verbose $ArgList
        $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
    if ($scheduleAction){
        $scheduleActionVar = "-scheduleAction=$scheduleAction"
        $ArgList = "/configure $scheduleActionVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-scheduleAction.log`""
        Write-Verbose $ArgList
        $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
    if ($scheduleAuto){
        $scheduleAutoVar = "-scheduleAuto"
        $ArgList = "/configure $scheduleAutoVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-scheduleAuto.log`""
        Write-Verbose $ArgList
        $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
    #Installation Deferral
    if ($installationDeferral){
        if ($scheduleAction -ne 'DownloadInstallAndNotify'){
            Write-Verbose "Schedule Action must be set to DownloadInstallAndNotify to use Installation Deferral"
             
        }
        else {
            if ($installationDeferral -eq 'Enable'){
                $installationDeferralVar = "-installationDeferral=$installationDeferral"
                if ($deferralInstallInterval){
                    [string]$deferralInstallIntervalVar = "-deferralInstallInterval=$deferralInstallInterval"
                }
                else {
                    [string]$deferralInstallIntervalVar = "-deferralInstallInterval=5"
                }
                if ($deferralInstallCount){
                    [string]$deferralInstallCountVar = "-deferralInstallCount=$deferralInstallCount"
                }
                else {
                    [string]$deferralInstallCountVar = "-deferralInstallCount=5"
                }
                $ArgList = "/configure $installationDeferralVar $deferralInstallIntervalVar $deferralInstallCountVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-installationDeferral.log`""
                Write-Verbose $ArgList
                $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
                if ($DCUConfig.ExitCode -ne 0){
                    $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
                    Write-Verbose "Exit: $($DCUConfig.ExitCode)"
                    Write-Verbose "Description: $($ExitInfo.Description)"
                    Write-Verbose "Resolution: $($ExitInfo.Resolution)"
                }
                 
            }
            else {
                [string]$installationDeferralVar = "-installationDeferral=$installationDeferral"
                $ArgList = "/configure $installationDeferralVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-installationDeferral.log`""
                Write-Verbose $ArgList
                $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
                if ($DCUConfig.ExitCode -ne 0){
                    $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
                    Write-Verbose "Exit: $($DCUConfig.ExitCode)"
                    Write-Verbose "Description: $($ExitInfo.Description)"
                    Write-Verbose "Resolution: $($ExitInfo.Resolution)"
                }
            }
        }
    }
    #System Reboot Deferral
    if ($systemRestartDeferral){
        if ($scheduleAction -ne 'DownloadInstallAndNotify'){
            Write-Verbose "Schedule Action must be set to DownloadInstallAndNotify to use Installation Deferral"
             
        }
        else {
            if ($systemRestartDeferral -eq 'Enable'){
                $systemRestartDeferralVar = "-systemRestartDeferral=$systemRestartDeferral"
                if ($deferralRestartInterval){
                    [string]$deferralRestartIntervalVar = "-deferralRestartInterval=$deferralRestartInterval"
                }
                else {
                    [string]$deferralRestartIntervalVar = "-deferralRestartInterval=5"
                }
                if ($deferralRestartCount){
                    [string]$deferralRestartCountVar = "-deferralRestartCount=$deferralRestartCount"
                }
                else {
                    [string]$deferralRestartCountVar = "-deferralRestartCount=5"
                }
                $ArgList = "/configure $systemRestartDeferralVar $deferralRestartIntervalVar $deferralRestartCountVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-RestartDeferral.log`""
                Write-Verbose $ArgList
                $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
                if ($DCUConfig.ExitCode -ne 0){
                    $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
                    Write-Verbose "Exit: $($DCUConfig.ExitCode)"
                    Write-Verbose "Description: $($ExitInfo.Description)"
                    Write-Verbose "Resolution: $($ExitInfo.Resolution)"
                }
                 
            }
            else {
                [string]$systemRestartDeferralVar = "-systemRestartDeferral=$systemRestartDeferral"
                $ArgList = "/configure $systemRestartDeferralVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-RestartDeferral.log`""
                Write-Verbose $ArgList
                $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
                if ($DCUConfig.ExitCode -ne 0){
                    $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
                    Write-Verbose "Exit: $($DCUConfig.ExitCode)"
                    Write-Verbose "Description: $($ExitInfo.Description)"
                    Write-Verbose "Resolution: $($ExitInfo.Resolution)"
                }
            }
        }
    }
    if ($CustomCatalogPath){
        $CustomCatalogPathVar = "-catalogLocation=`"$CustomCatalogPath`" -allowXML=enable"
        $ArgList = "/configure $CustomCatalogPathVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-CustomCatalogPath.log`""
        Write-Verbose $ArgList
        $DCUConfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
    if ($ExcludeUpdatesFromLastNDays){
        $ExcludeUpdatesFromLastNDaysVar = "-delaydays=$ExcludeUpdatesFromLastNDays -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-Configure-ExcludeUpdatesFromLastNDays.log`""
        $ArgList = "/configure $ExcludeUpdatesFromLastNDaysVar"
        Write-Verbose $ArgList
        $DCUCOnfig = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
        if ($DCUConfig.ExitCode -ne 0){
            $ExitInfo = Get-DCUExitInfo -DCUExit $DCUConfig.ExitCode
            Write-Verbose "Exit: $($DCUConfig.ExitCode)"
            Write-Verbose "Description: $($ExitInfo.Description)"
            Write-Verbose "Resolution: $($ExitInfo.Resolution)"
        }
    }
}
function Get-DCUSettings {
    [CmdletBinding()]
    param (
        [switch]$ResetSettings
    )
    write-host "Note: Info is in Registry here:" -ForegroundColor Cyan
    write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Dell\UpdateService\Clients\CommandUpdate\Preferences\Settings" -ForegroundColor Yellow
    $RegPath = "HKLM:\SOFTWARE\Dell\UpdateService\Clients\CommandUpdate\Preferences\Settings"
    if (!(Test-Path $RegPath)){
        Write-Output "No DCU Settings Found"
        return
    }
 
    $Keys = Get-ChildItem -Path $RegPath
    Write-output $Keys
    if ($ResetSettings){
        $Items = Get-ChildItem -Path $RegPath | Get-ItemProperty
        foreach ($Item in $Items){
            $ItemName = $Item.PSChildName
            Write-Output "Deleting $ItemName"
            Remove-Item -Path $Item.PSPath -Force
        }
    }
}
function Invoke-DCU {
    [CmdletBinding()]
     
    param (
    [ValidateSet('security','critical','recommended','optional')]
    [String[]]$updateSeverity,
    [ValidateSet('bios','firmware','driver','application','others')]
    [String[]]$updateType,
    [ValidateSet('audio','video','network','chipset','storage','input','others')]
    [String[]]$updateDeviceCategory,
    [ValidateSet('Enable','Disable')]
    [string]$autoSuspendBitLocker = 'Enable',
    [ValidateSet('Enable','Disable')]
    [string]$reboot = 'Disable',
    [ValidateSet('Enable','Disable')]
    [string]$forceupdate = 'Disable',
    [switch]$scan,
    [switch]$applyUpdates
    )
    $DCUPath = (Get-DCUInstallDetails).DCUPath
    $LogPath = "$env:SystemDrive\Users\Dell\EMPS\Logs"
    #Build Argument Strings for each parameter
    if ($updateSeverity){
        [String]$updateSeverity = $($updateSeverity -join ",").ToString()
        $updateSeverityVar = "-updateSeverity=$updateSeverity"
    }
    if ($updateType){
        [String]$updateType = $($updateType -join ",").ToString()
        $updateTypeVar = "-updateType=$updateType"
    }
    if ($updateDeviceCategory){
        [String]$updateDeviceCategory = $($updateDeviceCategory -join ",").ToString()
        $updateDeviceCategoryVar = "-updateDeviceCategory=$updateDeviceCategory"
    }
 
    #Pick Action, Scan or ApplyUpdates if both are selected, ApplyUpdates will be the action, if neither are selected, Scan will be the action
    $DateTimeStamp = Get-Date -Format "yyyyMMdd-HHmmss"
    if ($scan){
        $ActionVar = "/scan -report=$LogPath"
        $Action = "Scan"
    }
    if ($applyUpdates){
        $ActionVar = "/applyUpdates"
        $Action = "ApplyUpdates"
    }
    else {
        $ActionVar = "/scan -report=$LogPath"
        $Action = "Scan"
    }
     
 
    #Create Arugment List for Dell Command Update CLI
    $ArgList = "$ActionVar $updateSeverityVar $updateTypeVar $updateDeviceCategoryVar -outputlog=`"$LogPath\DCU-CLI-$($DateTimeStamp)-$Action.log`""
    Write-Verbose $ArgList
    $DCUApply = Start-Process -FilePath "$DCUPath\dcu-cli.exe" -ArgumentList $ArgList -NoNewWindow -PassThru -Wait
    if ($DCUApply.ExitCode -ne 0){
        $ExitInfo = Get-DCUExitInfo -DCUExit $DCUApply.ExitCode
        Write-Verbose "Exit: $($DCUApply.ExitCode)"
        Write-Verbose "Description: $($ExitInfo.Description)"
        Write-Verbose "Resolution: $($ExitInfo.Resolution)"
    }
}
 
function Get-DCUUpdateList {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$False)]
        [ValidateLength(4,4)]
        [string]$SystemSKUNumber,
        [ValidateSet('bios','firmware','driver','application')]
        [String[]]$updateType,
        [ValidateSet('audio','video','network','chipset','storage','BIOS','Application')]
        [String[]]$updateDeviceCategory,
        [switch]$RAWXML,
        [switch]$Latest,
        [switch]$TLDR
    )
 
     
    $temproot = "$env:windir\temp"
    #$SystemSKUNumber = (Get-CimInstance -ClassName Win32_ComputerSystem).SystemSKUNumber
    $Manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
    $CabPathIndexModel = "$temproot\DellCabDownloads\CatalogIndexModel.cab"
    $DellCabExtractPath = "$temproot\DellCabDownloads\DellCabExtract"
    if (!(Test-Path $DellCabExtractPath)){$null = New-Item -Path $DellCabExtractPath -ItemType Directory -Force}
     
     
    if (!($SystemSKUNumber)) {
        if ($Manufacturer -notmatch "Dell"){return "This Function is only for Dell Systems"}
        $SystemSKUNumber = (Get-CimInstance -ClassName Win32_ComputerSystem).SystemSKUNumber
    }
    $DellSKU = Get-DellSupportedModels | Where-Object {$_.systemID -match $SystemSKUNumber} | Select-Object -First 1
    if (!($DellSKU)){
        return "System SKU not found"
    }
    if (Test-Path $CabPathIndexModel){Remove-Item -Path $CabPathIndexModel -Force}
     
 
    Invoke-WebRequest -Uri "http://downloads.dell.com/$($DellSKU.URL)" -OutFile $CabPathIndexModel -UseBasicParsing
    if (Test-Path $CabPathIndexModel){
        $null = expand $CabPathIndexModel $DellCabExtractPath\CatalogIndexPCModel.xml
        [xml]$XMLIndexCAB = Get-Content "$DellCabExtractPath\CatalogIndexPCModel.xml"
         
        #DCUAppsAvailable = $XMLIndexCAB.Manifest.SoftwareComponent | Where-Object {$_.ComponentType.value -eq "APAC"}
        #$AppNames = $DCUAppsAvailable.name.display.'#cdata-section' | Select-Object -Unique
        $BaseURL = "https://$($XMLIndexCAB.Manifest.baseLocation)"
        $Components = $XMLIndexCAB.Manifest.SoftwareComponent
        if ($RAWXML){
            return $Components
        }
        $ComponentsObject = @()
        foreach ($Component in $Components){
            $Item = New-Object -TypeName PSObject
            $Item | Add-Member -MemberType NoteProperty -Name "PackageID" -Value "$($Component.packageID)" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "Category" -Value "$($Component.Category.Display.'#cdata-section')" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "Type" -Value "$($component.ComponentType.Display.'#cdata-section')" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "Name" -Value "$($Component.Name.Display.'#cdata-section')" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "ReleaseDate" -Value $([DateTime]($Component.releaseDate)) -Force
            $Item | Add-Member -MemberType NoteProperty -Name "DellVersion" -Value "$($Component.dellVersion)" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "VendorVersion" -Value "$($Component.vendorVersion)" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "PackageType" -Value "$($Component.packageType)" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "Path" -Value "$BaseURL/$($Component.path)" -Force
            $Item | Add-Member -MemberType NoteProperty -Name "Description" -Value "$($component.Description.Display.'#cdata-section')" -Force
            $ComponentsObject += $Item
        }
        if ($updateType){
            $ComponentsObject = $ComponentsObject | Where-Object {$_.Type -in $updateType}
        }
        if ($updateDeviceCategory){
            $ComponentsObject = $ComponentsObject | Where-Object {$_.Category -in $updateDeviceCategory}
        }
        if ($TLDR) {
            $ComponentsObject = $ComponentsObject | Select-Object -Property Name,ReleaseDate,DellVersion,Path
        }
        if ($Latest){
            $ComponentsObject = $ComponentsObject | Sort-Object -Property ReleaseDate -Descending
            $hash = @{}
            foreach ($ComponentObject in $ComponentsObject) {
                if (-not $hash.ContainsKey($ComponentObject.Name)) {
                    $hash[$ComponentObject.Name] = $ComponentObject
                }
            }
            $ComponentsObject = $hash.Values
        }
        return $ComponentsObject
    }
}
 
#Future Enahncements, see if I can add a column for the OS's that Dell Supports on the specific hardware model - This assumes they releases driver packs for the OS supported.
function Get-DellDeviceDetails {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$False)]
        [ValidateLength(4,4)]
        [string]$SystemSKUNumber,
        [string]$ModelLike
    )
     
    $Manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
     
     
    if ((!($SystemSKUNumber)) -and (!($ModelLike))) {
        if ($Manufacturer -notmatch "Dell"){return "This Function is only for Dell Systems, or please provide a SKU"}
        $SystemSKUNumber = (Get-CimInstance -ClassName Win32_ComputerSystem).SystemSKUNumber
    }
    <#
    if (!($ModelLike)){
        $DellSKU = Get-DellSupportedModels | Where-Object {$_.systemID -match $SystemSKUNumber} | Select-Object -First 1
    }
    else {
        $DellSKU = Get-DellSupportedModels | Where-Object { $_.Model -match $ModelLike}
    }
     
    return $DellSKU | Select-Object -Property SystemID,Model
    #>

    $MoreData = Get-DellDriverPackXML
    if (!($ModelLike)){
        $DrillDown = $MoreData.DriverPackManifest.DriverPackage.SupportedSystems.brand.model | Where-Object {$_.systemid -eq $SystemSKUNumber} | Select-Object -First 1
        $RDSDate = [DATETIME]"$($DrillDown.rtsDate)"
        $DeviceOutput = New-Object -TypeName PSObject
        $DeviceOutput | Add-Member -MemberType NoteProperty -Name "SystemID" -Value "$($DrillDown.systemID)" -Force
        $DeviceOutput | Add-Member -MemberType NoteProperty -Name "Model" -Value "$($DrillDown.name)"  -Force
        $DeviceOutput | Add-Member -MemberType NoteProperty -Name "RTSDate" -Value $([DATETIME]$RDSDate) -Force
        return $DeviceOutput        
    }
    else{
        $DrillDown = $MoreData.DriverPackManifest.DriverPackage.SupportedSystems.brand.model | Where-Object {$_.name -match $ModelLike}
        if ($DrillDown.count -gt 1){
            $SystemIDs = $DrillDown.systemID | Select-Object -Unique
            $DeviceOutputObject = @()
            foreach ($SystemID in $SystemIDs){
                $DrillDown = $MoreData.DriverPackManifest.DriverPackage.SupportedSystems.brand.model | Where-Object {$_.systemid -eq $SystemID}| Select-Object -First 1
                $RDSDate = [DATETIME]"$($DrillDown.rtsDate)"
                $DeviceOutput = New-Object -TypeName PSObject
                $DeviceOutput | Add-Member -MemberType NoteProperty -Name "SystemID" -Value "$($DrillDown.systemID)" -Force
                $DeviceOutput | Add-Member -MemberType NoteProperty -Name "Model" -Value "$($DrillDown.name)"  -Force
                $DeviceOutput | Add-Member -MemberType NoteProperty -Name "RTSDate" -Value $([DATETIME]$RDSDate) -Force
                $DeviceOutputObject += $DeviceOutput 
            }
            return $DeviceOutputObject | Sort-Object -Property RTSDate
        }
    }
}

#Add feature to download the driver pack, and have it default to the OS of the device it's running on, or allow the user to specify the OS they want to download the driver pack for.
function Get-DellDeviceDriverPack {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$False)]
        [ValidateLength(4,4)]    
        [string]$SystemSKUNumber,
        [ValidateSet('Windows10','Windows11')]
        [string]$OSVer
    )
    
    $Manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
    
    
    if (!($SystemSKUNumber)) {
        if ($Manufacturer -notmatch "Dell"){return "This Function is only for Dell Systems, or please provide a SKU"}
        $SystemSKUNumber = (Get-CimInstance -ClassName Win32_ComputerSystem).SystemSKUNumber
    }

    $MoreData = Get-DellDriverPackXML
    $DriverPacks = $MoreData.DriverPackManifest.DriverPackage | Where-Object {$_.SupportedSystems.brand.model.systemid -eq $SystemSKUNumber}
    $DeviceDetails = $MoreData.DriverPackManifest.DriverPackage.SupportedSystems.brand.model | Where-Object {$_.systemid -eq $SystemSKUNumber} | Select-Object -First 1
    $DriverPacksOBject = @()
    foreach ($DriverPack in $DriverPacks){
        $URL = "http://$($MoreData.DriverPackManifest.baseLocation)/$($DriverPack.path)"
        $FileName = $DriverPack.path -split "/" | Select-Object -Last 1
        $DeviceDriverPack = New-Object -TypeName PSObject
        $MetaDataVersion = $MoreData.DriverPackManifest.version
        $SizeinMB = [Math]::Round($DriverPack.size/1MB,2)
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "SystemID" -Value "$($DeviceDetails.systemID)" -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "Model" -Value "$($DeviceDetails.name)"  -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "MetaDataVersion" -Value "$MetaDataVersion"  -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "FileName" -Value "$FileName"  -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "ReleaseID" -Value "$($DriverPack.releaseID)"  -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "URL" -Value "$URL"  -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $([DATETIME]$DriverPack.dateTime) -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "hashMD5" -Value $($DriverPack.hashMD5) -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "SizeinMB" -Value $SizeinMB -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "OSSupported" -Value $($DriverPack.SupportedOperatingSystems.OperatingSystem.osCode) -Force
        $DeviceDriverPack | Add-Member -MemberType NoteProperty -Name "OsArch" -Value $($DriverPack.SupportedOperatingSystems.OperatingSystem.osArch) -Force
        $DriverPacksOBject += $DeviceDriverPack 
    }
    
    if ($OSVer){
        $DriverPacksOBject = $DriverPacksOBject | Where-Object {$_.OSSupported -match $OSVer}
    }
    
    return $DriverPacksOBject 

}




#Function to get a list of BIOS updates for a SKU, install, or download
#Similar to the Get-HPBIOSUpdate function in HPCMSL
Function Get-DellBIOSUpdates {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$False)]
        [ValidateLength(4,4)]    
        [string]$SystemSKUNumber,
        [switch]$Latest,
        [switch]$Check, #This will find the latest BIOS update and compare it to the current BIOS version
        [switch]$Flash,
        [string]$Password,
        [string]$DownloadPath

    )
    $Manufacturer = (Get-CimInstance -ClassName Win32_ComputerSystem).Manufacturer
    if (!($SystemSKUNumber)) {
        if ($Manufacturer -notmatch "Dell"){return "This Function is only for Dell Systems, or please provide a SKU"}
        $SystemSKUNumber = (Get-CimInstance -ClassName Win32_ComputerSystem).SystemSKUNumber
    }
    
    if ($Check){
        if ($Manufacturer -notmatch "Dell"){return "This Function is only for Dell Systems"}
        else{
            [Version]$CurrentBIOSVersion = (Get-CimInstance -ClassName Win32_BIOS).SMBIOSBIOSVersion
            $LatestBIOS = Get-DCUUpdateList -SystemSKUNumber $SystemSKUNumber -updateType BIOS -Latest | Sort-Object -Property ReleaseDate -Descending
            if ($LatestBIOS.count -gt 1){
                $LatestBIOS = $LatestBIOS | Select-Object -First 1
            }
            [version]$LatestVersion = ($LatestBIOS).DellVersion

            if ($CurrentBIOSVersion -lt $LatestVersion){
                Write-Verbose "Current BIOS Version: $CurrentBIOSVersion"
                Write-Verbose "Latest BIOS Version: $LatestVersion"
                Write-Verbose "New BIOS Update Available"
                return $false
            }
            else {
                Write-Verbose "Current BIOS Version: $CurrentBIOSVersion"
                Write-Verbose "Latest BIOS Version: $LatestVersion"
                Write-Verbose "No New BIOS Update Available"
                return $true
            }
        }
    }
    if ($Flash){
        #Test for Bitlocker
        $BitlockerStatus = Get-BitLockerVolume -MountPoint $env:SystemDrive
        if ($BitlockerStatus -ne $null){
            if ($BitlockerStatus.ProtectionStatus -eq "On"){
                Write-Host "Bitlocker is On, Please Suspend Bitlocker before Flashing BIOS"
                return
            }
        }
        #https://www.dell.com/support/kbdoc/en-us/000136752/command-line-switches-for-dell-bios-updates
        $Updates = Get-DCUUpdateList -SystemSKUNumber $SystemSKUNumber -updateType BIOS -Latest
        $Update = $Updates | Select-Object -First 1
        $UpdatePath = $Update.Path
        $UpdateFileName = $UpdatePath -split "/" | Select-Object -Last 1
        $UpdateLocalPath = "$env:windir\temp\$UpdateFileName"
        Start-BitsTransfer -DisplayName $UpdateFileName -Source $UpdatePath -Destination $UpdateLocalPath -Description "Downloading $UpdateFileName" -RetryInterval 60 #-CustomHeaders "User-Agent:Bob"
        if (Test-Path -Path $UpdateLocalPath){
            Write-Host "Installing $UpdateFileName, logfile: $($UpdateLocalPath).log"
            if ($Password){
                $BIOSArgs = "/s /l=$UpdateLocalPath.log /p=$Password"
            }
            else {
                $BIOSArgs = "/s /l=$UpdateLocalPath.log"
            }
            $InstallUpdate = Start-Process -FilePath $UpdateLocalPath -ArgumentList $BIOSArgs -Wait -PassThru
            Write-Host "Exit Code: $($InstallUpdate.ExitCode)"
            if ($InstallUpdate.ExitCode -ne 0){
                $ExitInfo = Get-DUPExitInfo -DUPExit $InstallUpdate.ExitCode
                Write-Host "Exit: $($InstallUpdate.ExitCode)"
                Write-Host "Code Name: $($ExitInfo.DisplayName)"
                Write-Host "Description: $($ExitInfo.Description)"
            }
            return
        }
        else {
            Write-Host "File Not Found: $UpdateFileName"
            return
        }
    }
    if ($DownloadPath){
        [void][System.IO.Directory]::CreateDirectory($DownloadPath)
        $Updates = Get-DCUUpdateList -SystemSKUNumber $SystemSKUNumber -updateType BIOS -Latest
        $Update = $Updates | Select-Object -First 1
        $UpdatePath = $Update.Path
        $UpdateFileName = $UpdatePath -split "/" | Select-Object -Last 1
        $UpdateLocalPath = "$DownloadPath\$UpdateFileName"
        Start-BitsTransfer -DisplayName $UpdateFileName -Source $UpdatePath -Destination $UpdateLocalPath -Description "Downloading $UpdateFileName" -RetryInterval 60 #-CustomHeaders "User-Agent:Bob"
        return $UpdateLocalPath
    }
    if ($Latest){
        $Updates = Get-DCUUpdateList -SystemSKUNumber $SystemSKUNumber -updateType BIOS -Latest
    }
    else {
        $Updates = Get-DCUUpdateList -SystemSKUNumber $SystemSKUNumber -updateType BIOS
    }
    return $Updates |Select-Object -Property "PackageID","Name","ReleaseDate","DellVersion" | Sort-Object -Property ReleaseDate -Descending
}

Function Invoke-DellIntuneAppPublishScript {

    write-host Write-Host -ForegroundColor Green "[+] Function: Invoke-PublishDellIntuneApp"
    $Description = "
    This Functions when invoked will do the below tasks
        1. show the UI to user to select required application
        2. Download the application that is posted for admin portal production to customer system
        3. Extract the contents and read the CreateAPPConfig.json file
        4. create win32_Lob App in intune
        5. Get APP file version
        6. Upload and commit intunewin file to Azure Storage Blob
        7. Update the file version in the Intune application
        "

    Write-Output $Description

    function Invoke-PublishDellIntuneApp{
        iex (irm https://raw.githubusercontent.com/dell/Endpoint-Management-Script-Library/refs/heads/main/Intune%20Scripts/EnterpriseAppDeployment/Dell_Intune_App_Publish_1.0.ps1 -help)
    }
}

<# Placeholders for future functions
 
#Function to get a specific Update with options to download, install or extract
#Similar to the Get-Softpaq function in HPCMSL
Function Get-DCUUpdate {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$False)]
        [String[]]$UpdateID,
        [ValidateSet('install','silentinstall')]
        [String]$Action,
        [String]$DownloadPath,
        [String]$ExtractPath,
        [switch]$Overwrite,
    )
 
}
 
 
 
 
#>