During a session at Microsoft Ignite that I attended this week, I saw the speakers had a nifty little PowerShell one-liner that would get all drivers in a WIM file and output it to a GridView. This got me thinking that it could be used in ConfigMgr for validating that the correct drivers have in fact been injected into a given Boot Image. I’m aware of that this can easily be shown by choosing Properting on the Boot Image under the Drivers tab, but as a experienced ConfigMgr administrator, I’ve been around long enough to know that drivers and Boot Images are not always friends. Instead I went for a method that would actually query for path where the Boot.<PackageID>.wim is located by only specifying the name of the Boot Image, and mount that Boot Image using the PowerShell Dism module. Once the WIM file is mounted to a location, it’s simply the matter of listing all of the drivers inside the mount folder using Get-WindowsDriver.

So in scenarios when you have to troubleshoot drivers in Boot Images, this script below will come in handy as it outputs the drivers that are actually present in the Boot Image, and just not what’s being shown to you in the ConfigMgr console.

Script

Save the code below as Get-CMBootImageDrivers.ps1.

<#
.SYNOPSIS
    List all drivers that has been added to a specific Boot Image in ConfigMgr 2012 
.DESCRIPTION 
    This script will list all the drivers added to a Boot Image in ConfigMgr 2012. It's also possible to list 
    Microsoft standard drivers by specifying the All parameter. 
.PARAMETER SiteServer 
    Site server name with SMS Provider installed 
.PARAMETER BootImageName 
    Specify the Boot Image name as a string or an array of strings 
.PARAMETER MountPath 
    Default path to where the script will temporarly mount the Boot Image 
.PARAMETER All 
    When specified all drivers will be listed, including default Microsoft drivers 
.PARAMETER ShowProgress 
    Show a progressbar displaying the current operation 
.EXAMPLE 
    .\Get-CMBootImageDrivers.ps1 -SiteServer CM01 -BootImageName "Boot Image (x64)" -MounthPath C:\Temp\MountFolder 
    List all drivers in a Boot Image named 'Boot Image (x64)' on a Primary Site server called CM01:
.NOTES 
    Script name: Get-CMBootImageDrivers.ps1 
    Author:      Nickolaj Andersen 
    Contact:     @NickolajA 
    DateCreated: 2015-05-06 
#>
[CmdletBinding(SupportsShouldProcess=$true)]
param(
    [parameter(Mandatory=$true, HelpMessage="Site server where the SMS Provider is installed")]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({Test-Connection -ComputerName $_ -Count 1 -Quiet})]
    [string]$SiteServer,
    [parameter(Mandatory=$true, HelpMessage="Specify the Boot Image name as a string or an array of strings")]
    [ValidateNotNullOrEmpty()]
    [string[]]$BootImageName,
    [parameter(Mandatory=$false, HelpMessage="Default path to where the script will temporarly mount the Boot Image")]
    [ValidateNotNullOrEmpty()]
    [ValidatePattern("^[A-Za-z]{1}:\\\w+")]
    [string]$MountPath = "C:\MountFolder",
    [parameter(Mandatory=$false, HelpMessage="When specified all drivers will be listed, including default Microsoft drivers")]
    [switch]$All,
    [parameter(Mandatory=$false, HelpMessage="Show a progressbar displaying the current operation")]
    [switch]$ShowProgress
)
Begin {
    # Determine SiteCode from WMI
    try {
        Write-Verbose "Determining SiteCode for Site Server: '$($SiteServer)'"
        $SiteCodeObjects = Get-WmiObject -Namespace "root\SMS" -Class SMS_ProviderLocation -ComputerName $SiteServer -ErrorAction Stop
        foreach ($SiteCodeObject in $SiteCodeObjects) {
            if ($SiteCodeObject.ProviderForLocalSite -eq $true) {
                $SiteCode = $SiteCodeObject.SiteCode
                Write-Debug "SiteCode: $($SiteCode)"
            }
        }
    }
    catch [System.Exception] {
        Write-Warning -Message "Unable to determine SiteCode" ; break
    }
    # Determine if we need to load the Dism PowerShell module
    if (-not(Get-Module -Name Dism)) {
        try {
            Import-Module Dism -ErrorAction Stop -Verbose:$false
        }
        catch [System.Exception] {
            Write-Warning -Message "Unable to load the Dism PowerShell module" ; break
        }
    }
    # Determine if temporary mount folder is accessible, if not create it
    if (-not(Test-Path -Path $MountPath -PathType Container -ErrorAction SilentlyContinue -Verbose:$false)) {
        New-Item -Path $MountPath -ItemType Directory -Force -Verbose:$false | Out-Null
    }
}
Process {
    if ($PSBoundParameters["ShowProgress"]) {
        $ProgressCount = 0
    }
    # Enumerate trough all specified boot image names
    foreach ($BootImageItem in $BootImageName) {
        try {
            Write-Verbose -Message "Querying for boot image: $($BootImageItem)"
            $BootImage = Get-WmiObject -Namespace "root\SMS\site_$($SiteCode)" -Class SMS_BootImagePackage -ComputerName $SiteServer -Filter "Name like '$($BootImageItem)'" -ErrorAction Stop
            if ($BootImage -ne $null) {
                $BootImagePath = $BootImage.PkgSourcePath
                Write-Verbose -Message "Located boot image wim file: $($BootImagePath)"
                # Mount Boot Image to temporary mount folder
                if ($PSCmdlet.ShouldProcess($BootImagePath, "Mount")) {
                    Mount-WindowsImage -ImagePath $BootImagePath -Path $MountPath -Index 1 -ErrorAction Stop -Verbose:$false | Out-Null
                }
                # Get all drivers in the mounted Boot Image
                $WindowsDriverArguments = @{
                    Path = $MountPath
                    ErrorAction = "Stop"
                    Verbose = $false
                }
                if ($PSBoundParameters["All"]) {
                    $WindowsDriverArguments.Add("All", $true)
                }
                if ($PSCmdlet.ShouldProcess($MountPath, "ListDrivers")) {
                    $Drivers = Get-WindowsDriver @WindowsDriverArguments
                    if ($Drivers -ne $null) {
                        $DriverCount = ($Drivers | Measure-Object).Count
                        foreach ($Driver in $Drivers) {
                            if ($PSBoundParameters["ShowProgress"]) {
                                $ProgressCount++
                                Write-Progress -Activity "Enumerating drivers in '$($BootImage.Name)'" -Id 1 -Status "Processing $($ProgressCount) / $($DriverCount)" -PercentComplete (($ProgressCount / $DriverCount) * 100)
                            }
                            $PSObject = [PSCustomObject]@{
                                Driver = $Driver.Driver
                                Version = $Driver.Version
                                Manufacturer = $Driver.ProviderName
                                ClassName = $Driver.ClassName
                                Date = $Driver.Date
                                BootImageName = $BootImage.Name
                            }
                            Write-Output $PSObject
                        }
                        if ($PSBoundParameters["ShowProgress"]) {
                            Write-Progress -Activity "Enumerating drivers in '$($BootImage.Name)'" -Id 1 -Completed
                        }
                    }
                    else {
                        Write-Warning -Message "No drivers was found"
                    }
                }
            }
            else {
                Write-Warning -Message "Unable to locate a boot image called '$($BootImageName)'"
            }
        }
        catch [System.UnauthorizedAccessException] {
            Write-Warning -Message "Access denied" ; break
        }
        catch [System.Exception] {
            Write-Warning -Message $_.Exception.Message ; break
        }
        # Dismount the boot image
        if ($PSCmdlet.ShouldProcess($BootImagePath, "Dismount")) {
            Dismount-WindowsImage -Path $MountPath -Discard -ErrorAction Stop -Verbose:$false | Out-Null
        }
    }
}
End {
    # Clean up mount folder
    try {
        Remove-Item -Path $MountPath -Force -ErrorAction Stop -Verbose:$false
    }
    catch [System.UnauthorizedAccessException] {
        Write-Warning -Message "Access denied"
    }
    catch [System.Exception] {
        Write-Warning -Message $_.Exception.Message
    }
}

Documentation

This script has a set of parameters that is required for it to function properly. Below you’ll find a description of those and if they’re required or not:

Parameter Name Required Value Description
SiteServer Yes string This should reflect the Primary Site server where the SMS Provider is installed. If the SMS Provider is installed on a remote Site server, specify that remote server instead.
BootImageName Yes string array The name of the Boot Image to list drivers for. Could be an array of strings.
MountPath Yes string Path the a temporary location where the Boot Image WIM file will be mounted in order to list all drivers.
All No switch Includes Microsoft drivers present in the Boot Image.
ShowProgress No switch Show a progressbar displaying the current operation

By default the script will only list all non-Microsoft drivers that has been injected to the Boot Image. If you’d like to list all drivers available in the Boot Image, include the All switch in your command. In addition to the parameters listed above, the script also supports the advanced function parameter switches like Verbose, WhatIf etc.

Using the script

Download the script above and save it as Get-CMBootImageDrivers.ps1 in e.g. C:\Scripts on your Primary Site server.

1. Open an elevated PowerShell console and browse to C:\Scripts.
2. Run the following command:

.\Get-CMBootImageDrivers.ps1 -SiteServer CAS01 -BootImageName "Boot Image (x64)" -Verbose

144_1

As shown in the above image, the script found 3 drivers that had been injected into the Boot Image that I specified.

I hope that this helps you with your Boot Image and drivers troubleshooting. And as always, if you have any questions, please leave a comment.

(1216)

Nickolaj Andersen

Principal Consultant and Enterprise Mobility MVP since 2016. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows devices and deployments including automation. Currently working for TrueSec as a Principal Consultant. Awarded as PowerShell Hero in 2015 by the community for his script and tools contributions. Creator of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService to name a few. Frequent speaker at conferences and user groups.

comments
  • Matt
    Posted at 14:37 June 9, 2016
    Matt
    Reply
    Author

    Great script. Thank you!

  • Johannes
    Posted at 15:51 September 6, 2016
    Johannes
    Reply
    Author

    When you Google something you need and end up on your page!

    \o/

    #mvpftw

  • alan
    Posted at 15:59 February 8, 2017
    alan
    Reply
    Author

    I would add to the script the Original File Name if you can.
    SCCM will tend to lean towards the original file name.

    Published Name : oem0.inf
    Original File Name : e1d6232.inf

  • Leave a Reply