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
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.
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!
Hi Dave,
Are you using a matching or newer version of Windows 10 ADK than the boot image?
Regards,
Nickolaj
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
When you Google something you need and end up on your page!
\o/
#mvpftw
Great script. Thank you!