MSEndpointMgr

List Drivers in a Boot Image in ConfigMgr 2012 with PowerShell

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 NameRequiredValueDescription
SiteServerYesstringThis 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.
BootImageNameYesstring arrayThe name of the Boot Image to list drivers for. Could be an array of strings.
MountPathYesstringPath the a temporary location where the Boot Image WIM file will be mounted in order to list all drivers.
AllNoswitchIncludes Microsoft drivers present in the Boot Image.
ShowProgressNoswitchShow 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.

Nickolaj Andersen

Chief Technical Architect 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. 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 such as Microsoft Ignite, NIC Conference and IT/Dev Connections including nordic user groups.

5 comments

  • Hi Nickolaj,
    Having some trouble using your script – The output I get is:
    PS C:\scripts\Get-CMBootImageDrivers> .\Get-CMBootImageDrivers.ps1 -SiteServer -BootImageName -MountPath “D:\TMP2″ -Verbose
    VERBOSE: Determining SiteCode for Site Server: ”
    VERBOSE: Querying for boot image:
    VERBOSE: Located boot image wim file: .wim
    VERBOSE: Performing the operation “Mount” on target “\.wim”.
    VERBOSE: Performing the operation “ListDrivers” on target “D:\TMP2”.
    WARNING: An error occurred. No operation was performed.
    Verify that DISM is installed properly in the image, and then try the operation again.
    WARNING: The process cannot access the file ‘D:\TMP2’ because it is being used by another process.
    PS C:\scripts\Get-CMBootImageDrivers>
    I’ve tried this on pretty much all of my Primary Site Servers, as well as my CAS, and they all give the exact same result.
    Any ideas or feedback would be really appreciated, thank you!

  • 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

Sponsors