Post-deployment Driver Installation using PowerShell scripts.
-
Hello,
I just wanted to share how I’ve been handling driver installation to have 1 image work for over 20 different models. I’ve done this for both Windows 7 and Windows 10 with (mostly) great results.
I’d argue that the most important step is to make sure your image contains all of the NIC drivers for all the models that you need to support. Drivers are going to be hosted on a network share. No NIC drivers means no Internet.
Go ahead and download all of the NIC drivers for all of your models or find driver packs online. Once downloaded extract the files to a common folder. In the root of the folder create a PowerShell script with the following line. Run the script. (You may need to adjust your Execution Policy to run scripts. Google it.)
Get-ChildItem -Path $PSScriptRoot -Recurse | Where-Object -Property Extension -EQ ".inf" | ForEach { PnPUtil.exe -a $PSItem.FullName }
This will do a recursive search for all files that have the .inf extension and use pnputil to add them to Windows driver store. During sysprep Windows will detect your hardware and pull the correct driver from the driver store.
Besides the NIC drivers, there are only 3 files that I add to the image. My unattend.xml, SetupComplete.cmd, and SetupComplete.ps1. The rest are pulled from a network share.
I assume you already have a working answer file, I’m not going to go into that or how to use sysprep.
SetupComplete.cmd - Located in C:\Windows\Setup\Scripts\ - You’ll need to create the Scripts folder.
@ECHO OFF ECHO Initializing Driver Installation Script START /wait PowerShell.exe -Command "& '%~dpn0.ps1'" ECHO Removing configuration files. CD %dp0 DEL SetupComplete.ps1 CD %windir%\System32\Sysprep DEL unattend.xml
This will launch a PowerShell script with the same name as the SetupComplete.cmd file, stored in the same location. SetupComplete.ps1.
After SetupComplete.ps1 is finished running it will get deleted. The unattend.xml file also gets deleted.SetupComplete.ps1 - Located in C:\Windows\Setup\Scripts\
# Credentials used to access network drive. $Username = "Domain\Username" $Password = "Password" | ConvertTo-SecureString -AsPlainText -Force $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username, $Password $RootPath = "$env:HOMEDRIVE\Drivers\" # Function to determine if 10.10.0.1 is reachable, used to verify # that NIC drivers are working. Waits up to 30 seconds. Function Wait-ForConnection { $wait = $true $count = 30 Write-Host "Establishing network connection." -NoNewline while($wait) { $connection = Test-Connection 10.10.0.1 -ErrorAction SilentlyContinue if ($connection) { Write-Host Write-Host "Connection established!" -ForegroundColor Green $wait = $false } else { Write-Host " ." -NoNewline $count = ($count -1) Start-Sleep -Seconds 1 } if ($count -le 0) { $wait = $false Write-Host "Unable to establish a network connection." -ForegroundColor Red } } } Wait-ForConnection Write-Host Write-Host "Mounting network drive." New-PSDrive -Name "Deploy" -PSProvider FileSystem -Root \\ShareName\Windows10 -Credential $Credential | Out-Null $Deploy = Test-Path -Path Deploy:\ -ErrorAction SilentlyContinue if ($Deploy) { Write-Host "Successfully mounted network drive." -ForegroundColor Green Write-Host if (!(Test-Path -Path $RootPath)) { New-Item -Path $RootPath -ItemType Directory | Out-Null } $Controller = "Deploy:\Controller.ps1" Copy-Item -Path $Controller -Destination "$env:HOMEDRIVE\Drivers\Controller.ps1" -Force $Controller = "$env:HOMEDRIVE\Drivers\Controller.ps1" Write-Host "Running script " -NoNewline Write-Host "Controller.ps1 " -NoNewline -ForegroundColor Yellow Invoke-Expression -Command $Controller # Do other stuff, etc... Write-Host Write-Host "Unmounting network drive." Remove-PSDrive -Name "Deploy" } else { Write-Host "Failed to mount network drive." -ForegroundColor Red } Start-Sleep -Seconds 5 Restart-Computer
First off, this will check to see if you can reach 10.10.0.1 (Change this to something relevant to you).
If you can reach it then your NIC is probably working.
Second, it will mount a network drive using supplied credentials. The account only needs Read access to the share and it’s contents.
Third, it will copy another PowerShell script that I’ve titled Controller from the network share to a local directory. It’ll then run that script.
Last, after Controller.ps1 has finished doing it’s thing we unmount the network drive and restart the computer.Controller.ps1 - Stored in the root of the network share you mounted in the last step.
Note: This is a shortened version. I removed somethings for the purpose of this guide.$Model = (Get-WmiObject -Class Win32_ComputerSystem).Model Write-Host "Model identified as: " -NoNewline -ForegroundColor DarkYellow Write-Host $Model -ForegroundColor Yellow Write-Host $file = "" switch ($Model) { "OptiPlex 7040" { $file = "dell7040.ps1"; break; } "Precision 3510" { $file = "dell3510.ps1"; break; } "Precision T1700" { $file = "dell1700.ps1"; break; } "Precision Tower 7810" { $file = "dell7810.ps1"; break; } "HP Compaq Pro 6300 SFF" { $file = "hp6300.ps1"; break; } "VirtualBox" { $file = "virtualbox.ps1"; break; } default { # There were multiple numbers for these models. If ($model -like '*6710b*') { $file = "hp6710.ps1"; } ElseIf ($model -like '*6730b*') { $file = "hp6730.ps1"; } } } $filePath = $RootPath + "Model.ps1" Copy-Item -Path "Deploy:\ModelScripts\$file" -Destination $filePath -Force Invoke-Expression -Command $filePath if ((Get-WmiObject -Class Win32_ComputerSystem).Manufacturer -match "Dell") { # May do some Dell BIOS settings here. } Write-Host Write-Host "Downloading FOG Client from FOGServer." -ForegroundColor Yellow $url = "http://<FOGSERVER>/fog/client/download.php?newclient" $outfile = "$RootPath + FOGService.msi" (New-Object System.Net.WebClient).DownloadFile($url, $outfile) Write-Host "Installing FOG Client." -ForegroundColor Cyan $ArgumentList = "/i $outfile /quiet USETRAY=""0"" WEBADDRESS=""<FOGSERVER>"" WEBROOT=""/fog"" /norestart" Start-Process -FilePath MSIEXEC.exe -ArgumentList $ArgumentList -Wait Write-Host "Creating a scheduled task to start the FOGClient after reboot." $TaskPath = "$env:windir\Setup\StartFOG.ps1 " Copy-Item -Path "Deploy:\StartFOG.ps1" -Destination $TaskPath $Action = New-ScheduledTaskAction -Execute PowerShell.exe -Argument "-Command ""& $TaskPath """ $Trigger = New-ScheduledTaskTrigger -AtStartup Register-ScheduledTask -Action $Action -Trigger $Trigger -TaskName "StartFOG" -User "NT AUTHORITY\SYSTEM" -RunLevel Highest -Force Write-Host "Deleting downloaded driver files." Remove-Item -Path $RootPath -Recurse -Force
Here we use a quick WMI query to get the model number of the PC and use a switch to pick the model specific driver installation script. You’ll want to run the query on each machine beforehand to find out exactly how it’s stored.
The beauty of this is that it’s flexible. You can easily add support for new models as long as the NIC drivers are on the image.
hp6300.ps1 - Located in Deploy:\ModelScripts\ - You’ll make similiar script for each model. This is a very simple one.
Write-Host "$Model Driver Installation" -ForegroundColor DarkGreen $chipset = $RootPath + "chipset.exe" $audio = $RootPath + "0008-64bit_Win7_Win8_Win81_Win10_R281\Setup.exe" Write-Host "Downloading Intel Chipset Drivers." Copy-Item -Path "Deploy:\Drivers\chipset\Intel\SetupChipset.exe" -Destination $chipset -Force Write-Host "Downloading Realtek HD Audio Drivers." Copy-Item -Path "Deploy:\Drivers\audio\Realtek\0008-64bit_Win7_Win8_Win81_Win10_R281\" -Destination $RootPath -Recurse -Force Write-Host Write-Host "Installing Intel Chipset Support Drivers." Start-Process -FilePath $chipset -ArgumentList "/s /norestart" -Wait Write-Host "Installing Realtek HD Audio Driver." Start-Process -FilePath $audio -ArgumentList "/s" -Wait Start-Sleep 5
Doesn’t really get much simplier than this one. Most drivers Windows 10 already picked up. The process is basically the same for every driver, as long as they are properly signed.
This should be enough information to get you started, if you want to do it this way. I like doing it this way because it gives me a high level of control over everything. I can specify exactly what version gets installed onto each model. You could even query the serial number of the PC and install device specify software, if you really wanted to get down to that level of detail (FOG snapins would obviously be easier though)
-
@Avaryan Awesome tutorial, thank you for giving back.