Silent Scripted PNP Driver Installation

Occasionally, you may find the need to push a new driver to computers.  Perhaps a driver is causing BSOD issues or whatever the reason.  Since DotNet does not have a direct way to do this, you are usually left with depending on the driver publisher to include an silent installation method.  In reality this rarely happens.  You definitely don’t want to run around and manually install the drivers, and tools like Configuration Manager don’t have support for post OS deployment of drivers.

Recently, I stumbled into the Devcon tool available from the Windows Driver kit.  With this, we can script the installation.  What we need is the Hardware ID of the device and the INF of the driver we want to install.

First we’ll use the following function to get the properties of the INF file.  Since INF files are written by multiple vendors it can be tricky to get this to work on all INFs.  I’ve tested multiple vendors successfully, but if you have issues, it is likely caused by extra whitespace etc… add/removed from the INF file.  In this case, you’ll want to tweak the regex in the function below:


Function Get-InfDriverInfo{
Param(
[ValidateScript({
if(Test-path $_){
Get-item $_ | Where{$_.Extension -match 'inf'}
}
})]
[string]$Path
)

$infContent = Get-Content $Path

#Get the version number
$Versionmatches = [regex]::matches($infContent,"(?<=DriverVer)(?:\s*=\s*\d{1,2}\/\d{1,2}\/\d{4},\s*)(\d{1,4}\.\d{1,4}\.\d{1,4}\.\d{1,4})")
if($Versionmatches.groups.count -gt 1){
$versionnumber = $Versionmatches.groups[1] | select -ExpandProperty value
Try{$versionnumber = [version]$versionnumber}Catch [exception]{$versionnumber = $null}
}

#Get Driver name and pnpIDs
$PNPIDGroups = [regex]::matches($infContent,"(?:,\s+)((?:\w|\\|&)+?VEN_\w+&DEV_\w+)")
$pnpidcount = $pnpidgroups.count
if($pnpidcount -gt 1){
$pnpIDs = ForEach($pnpGroup in $pnpidgroups){
#ignore errors
Try{$pnpgroup.groups[1] | Select -ExpandProperty value}Catch [exception]{}
}
}else{
write-error "Unable to find PNPIDs in INF"
}

[pscustomobject]@{
"Path" = $Path
"Version" = $VersionNumber;
"ValidPNPids" = $pnpids
}
}

 

Next, we’ll search WMI for PNP devices that match the valid IDs returned by the previous function and install the driver for that device (Note that this requires Devcon.exe to be in the same directory as your script):


Function Install-Driver{
Param(
$DeviceNameFilter,
[ValidateScript({
if(Test-path $_){
Get-item $_ | Where{$_.Extension -match 'inf'}
}
})]
[string]$INFPath
)
#Find Devcon.exe
$devcon = "${PSScriptroot}\devcon.exe"
if(!(Test-path $devcon)){
write-error "Unable to find devcon.exe in the script location"
Return
}

#Get INF info
$infInfo = Get-InfDriverInfo -Path $INFPath
$pnpIDs = $infInfo.ValidPnpIDs
$Version = $infInfo.Version
if(!($Version)){
write-error "Unable to get version information from inf file"
Return
}

#Get Device
$WMIDevice = gwmi -query "select * from win32_pnpentity Where Name like '$DeviceNameFilter'"
$deviceHWIDs = $WMIDevice | Where{$_.CompatibleID | Where{$_ -in $PNPIDs}} | Select-object -expandproperty HardwareID
if(!($deviceHWIDs)){
write-error "No valid devices found for inf file"
Return
}

#Install the device
if($deviceHWIDs.count -ge 2){
$deviceHWID = $deviceHWIDs[0]
}else{
$deviceHWID = $deviceHWIDs
}
$Args = "update `"$INFPath`" `"$deviceHWID`""
$objReturn = Start-Process -FilePath $DevCon -ArgumentList $Args -wait -PassThru
$exitcode = $objReturn.ExitCode
Return $exitcode
}

Usage is quite simple:


Install-Driver -DeviceNameFilter '%' -INFPath $inf

Please note however that if you are using these in production scripts, you will want to add fail safe options such as not allowing driver downgrade etc… as well as logging.  Now you can simply deploy this as a standard program/application and deploy!

 

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s