Last active
February 19, 2020 09:11
-
-
Save rileyz/464175e3bb96f1b67dfc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Reusable script for staging many device driver packages to the Drivers Store. | |
.DESCRIPTION | |
Intended Use | |
This script was produced to stage device drivers for an App-V package. Because device drivers | |
or drivers in general can't be virtualised, a method is required to stage the drivers to the | |
operating system, what we call in App-V colloquial terms, outside of the bubble. | |
Example | |
Action this script using the App-V DeploymentConfig, using the child elements AddPackage and/or | |
RemovePackage. | |
The driver package can either be located on the VFS or added as script collateral in the form | |
of a zip archive. | |
The script supports the importing and removal of certificates from the Trusted Publishers | |
certificate store. This is workaround to enable silent staging of Authenticode-Signed | |
Non-WHQL-Class driver packages. To utilise this feature, store your certificate/s at the | |
base directory of the drivers and prefix the certificate with 'TrustedPublisher'. | |
eg. TrustedPublisher-ContosoToasterHardware.cer | |
The mandatory parameters are as follows. | |
-DriverSource Token, UNC or relative path to driver source. | |
-LogName Name of the log file. | |
The optional parameters are as follows. | |
-HideARP Will not add entry to Programs and Features. | |
-Remove Will remove the driver package from the Driver Store. | |
* To stage a driver package to the Driver Store, where the driver package is located on the VFS. | |
Manage-Drivers.ps1 -DriverSource "..\Root\VFS\ProgramFilesX86\DriverFolder" -LogName "Example.log" | |
* To stage a driver package to the Driver Store, where the driver package is located on the VFS | |
in a form of a zip archive. | |
Manage-Drivers.ps1 -DriverSource "[{ProgramFilesX86}]DriverFolder\DriverArchive.zip" -LogName "Example.log" | |
* To stage a driver package to the Driver Store, where the driver package is script collateral in | |
the form of a zip archive. | |
Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log" | |
* To stage a driver package to the Driver Store and not add the 'Program and Features' entry. | |
Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log" -HideARP | |
* To remove a driver package from the Driver Store, where the driver package is located on | |
the VFS. | |
Manage-Drivers.ps1 -DriverSource "..\Root\VFS\ProgramFilesX86\DriverFolder" -LogName "Example.log" -Remove | |
* To remove a driver package to the Driver Store, where the driver package is located on the VFS | |
in a form of a zip archive. | |
Manage-Drivers.ps1 -DriverSource "[{ProgramFilesX86}]DriverFolder\DriverArchive.zip" -LogName "Example.log" -Remove | |
* To remove a driver package from the the Driver Store, where the driver package is script | |
collateral in the form of a zip archive. | |
Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log" -Remove | |
About | |
I needed a script to stage driver packages into the Driver Store from App-V, after much | |
research I couldn't find anything of great help on the interwebs. The only information that | |
came close to resolving my issue was Nicke Kallen's blog "App-V 5 and Drivers" at | |
www.applepie.se. This was close to the solution, my desire was to action it without the | |
use of Windows Installer packages but rather the free utility DPIsnt. This allows for lower | |
administration overhead whist enabling anyone without access to packaging software, such | |
as Adminstudio, to stage drivers in combination with App-V. | |
A thank you to Anton Burgess for the tip on driver certificates. | |
Known Defects/Bugs | |
* Single quotes when used to pass arguments will cause the script to defect. Please use double | |
quotes to pass the arguments. | |
* A backslash at the end of the DriverSource folder path will cause the script to fatally error | |
and not run, no log being created. | |
* Error checking and trapping has been provided but could be improved. | |
* If the script Log contains no "END OF LOGGING" line, increase the timeout in the | |
DeploymentConfig to allow more time for the script to complete. | |
* Resolved! ScriptRunner.exe released in App-V 5.1 is a 32-bit process, this causes PowerShell to be | |
launched as a 32-bit proccess, the knock on effect is the architecture detection will be | |
incorrect. Workaround, do not use ScriptRunner.exe to execute this script. | |
Code Snippet Credits | |
* http://www.howtogeek.com/tips/how-to-extract-zip-files-using-powershell | |
* http://stackoverflow.com/questions/7834656/create-log-file-in-powershell | |
* http://www.networkworld.com/article/2346838/microsoft-subnet/how-to-read-certificates-and-crls-using-powershell.html | |
* http://blogs.technet.com/b/heyscriptingguy/archive/2014/08/31/powertip-using-powershell-to-determine-if-path-is-to-file-or-folder.aspx | |
Version History | |
1.2 16/07/2016 | |
Improved architecture detection method when the parent process is 32-bit (ScriptRunner.exe) on | |
a 64-bit operating system. Grammar and spelling corrections. | |
1.1 12/05/2016 | |
Bugfixes. | |
1.0 25/01/2015 | |
Initial release. | |
Copyright & Intellectual Property | |
Feel to copy, modify and redistribute, but please pay credit where it is due. | |
Feed back is welcome, please contact me on LinkedIn. | |
.LINK | |
Author:.......http://www.linkedin.com/in/rileylim | |
Source Code:..https://gist.github.com/rileyz/464175e3bb96f1b67dfc | |
Article:......http://www.itninja.com/blog/view/app-v-5-and-drivers | |
.EXAMPLE | |
Staging a driver package where it is located on the VFS. | |
<AddPackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "..\Root\VFS\ProgramFilesX86\DriverFolder" -LogName "Example.log"</Arguments> | |
... | |
.EXAMPLE | |
Staging a driver package where it is located on the VFS in a form of a zip archive. | |
<AddPackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "[{ProgramFilesX86}]DriverFolder\DriverArchive.zip" -LogName "Example.log"</Arguments> | |
.EXAMPLE | |
Staging a driver package where it is script collateral in the form of a zip archive. | |
<AddPackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log"</Arguments> | |
... | |
.EXAMPLE | |
Staging a driver package and not add the 'Program and Features' entry. | |
<AddPackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log" -HideARP</Arguments> | |
... | |
.EXAMPLE | |
Remove a driver package where it is located on the VFS. | |
<RemovePackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "..\Root\VFS\ProgramFilesX86\DriverFolder" -LogName "Example.log" -Remove</Arguments> | |
... | |
.EXAMPLE | |
Remove a driver package where it is located on the VFS in a form of a zip archive. | |
<AddPackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "[{ProgramFilesX86}]DriverFolder\DriverArchive.zip" -LogName "Example.log" -Remove</Arguments> | |
.EXAMPLE | |
Remove a driver package where it is script collateral in the form of a zip archive. | |
<RemovePackage> | |
<Path>powershell.exe</Path> | |
<Arguments>-ExecutionPolicy ByPass -File Manage-Drivers.ps1 -DriverSource "DriverArchive.zip" -LogName "Example.log" -Remove</Arguments> | |
... | |
#> | |
Param ([Parameter(Mandatory=$True)][String]$DriverSource, | |
[Parameter(Mandatory=$True)][String]$LogName, | |
[Switch] $HideARP, | |
[Switch] $Remove) | |
# Function List ################################################################################### | |
Function LogWrite {Param ([String] $LogLine, | |
[Switch] $EndOfLog) | |
If ($EndOfLog -eq $True) {Add-content $LogFile -value 'INFO: END OF LOGGING' | |
Add-content $LogFile -value ''} | |
Else {Add-content $LogFile -value $LogLine}} | |
Function WindowsTempCleanup {If ($IsDriverSourceAFolder -eq $False) {Remove-Item $WindowsTempFolder -Force -Recurse | |
LogWrite "EVENT: Removed temporary working folder $WindowsTempFolder"}} | |
#<<< End Of Function List >>> | |
# Setting up housekeeping ######################################################################### | |
$LogFile = "$env:systemroot\Temp\$LogName" | |
$LogDate = Get-Date -Format 'dd MMMM, yyyy, HH:mm.' | |
$DPInst32 = '\DPInst-x86.exe' #Please ensure the DPInst executable has the same name as | |
$DPInst64 = '\DPInst-amd64.exe' #in the DriverSource. | |
$WithThisErrorCode = 0 #Graceful exit: 0. Fatal error: 1, this will halt Add-AppVPackage | |
#if using the standard xml attributes. | |
#eg <Wait RollbackOnError="true" Timeout="30"/>. | |
#<<< End of Setting up housekeeping >>> | |
# Start of script work ############################################################################ | |
LogWrite 'INFO: ****************************************' | |
LogWrite 'INFO: Manage Drivers Log' | |
LogWrite 'INFO: Author: http://www.linkedin.com/in/rileylim' | |
LogWrite 'INFO: Source Script: https://gist.github.com/rileyz/464175e3bb96f1b67dfc' | |
LogWrite "INFO: $LogDate" | |
LogWrite 'INFO: Running with the following parameters:' | |
LogWrite "INFO: DriverSource: $DriverSource" | |
LogWrite "INFO: Hide ARP: $HideARP | Remove drivers: $Remove" | |
LogWrite 'INFO: ****************************************' | |
If (((Get-WmiObject Win32_OperatingSystem).OSArchitecture).Contains('32') -eq $True) {$Architecture32Query++} | |
Else{$Architecture64Query++} | |
If ((Test-Path $ENV:WinDir\SysWOW64) -eq $True) {$Architecture64Query++} | |
Else{$Architecture32Query++} | |
Switch ([Environment]::Is64BitOperatingSystem) {True {$Architecture64Query++} | |
False {$Architecture32Query++} | |
Default {LogWrite 'INFO: Bitness check via a .Net 4 property was not run because it is not installed/enabled.'}} | |
If ($Architecture32Query -gt $Architecture64Query) {$Architecture = '32-bit' | |
LogWrite "INFO: Bitness check passed at $Architecture32Query/3 for 32-bit."} | |
Else{$Architecture = '64-bit' | |
LogWrite "INFO: Bitness check passed at $Architecture64Query/3 for 64-bit."} | |
LogWrite "INFO: The operating system architecture is $Architecture." | |
Try {$IsDriverSourceAFolder = (Get-Item $DriverSource -ErrorAction Stop) -is [System.IO.DirectoryInfo]} | |
Catch {LogWrite "ERROR: The DriverSource is not a valid folder or file!" | |
LogWrite 'INFO: Please ensure your relative path reference is correct from the VFS Scripts folder,' | |
LogWrite 'INFO: or use a App-V token path.' | |
LogWrite -EndOfLog | |
Exit $WithThisErrorCode} | |
If ($IsDriverSourceAFolder -eq $False) {$WindowsTempFolder = "$env:systemroot\Temp\" + [guid]::NewGuid() | |
$DriverZipPath = Resolve-Path -Path $DriverSource | |
$DriverSource = $WindowsTempFolder | |
LogWrite "INFO: Full path to zip file is $DriverZipPath" | |
$Shell = New-Object -ComObject shell.application | |
$Zip = $Shell.NameSpace("$DriverZipPath") | |
New-Item -ItemType Directory -Force -Path $WindowsTempFolder | |
LogWrite "EVENT: Created temporary working folder $WindowsTempFolder" | |
Foreach ($Item in $Zip.items()){$Shell.Namespace("$WindowsTempFolder").copyhere($Item)} ##not working | |
LogWrite 'EVENT: Unpacking of zip file is complete.'} | |
If ($Architecture -eq '32-bit') {$DPInstForThisArchitecture = $DriverSource + $DPInst32} | |
Else {$DPInstForThisArchitecture = $DriverSource + $DPInst64} | |
If ((Test-Path $DPInstForThisArchitecture ) -eq $False) {LogWrite 'ERROR: Can not find DPIsnt executable!' | |
LogWrite 'INFO: Please ensure the executable has the same file name as in the scripts housekeeping section.' | |
WindowsTempCleanup | |
LogWrite -EndOfLog | |
Exit $WithThisErrorCode} | |
LogWrite "EVENT: Driver Package Installer (DPInst) is valid and set to $DPInstForThisArchitecture" | |
If ((Test-Path "$DriverSource\*.inf") -eq $False) {LogWrite 'ERROR: Can not find any Setup Information (.inf) files types!' | |
LogWrite 'INFO: Please ensure the driver Setup Information (.inf) files are at the root of the DriverSource.' | |
WindowsTempCleanup | |
LogWrite -EndOfLog | |
Exit $WithThisErrorCode} | |
LogWrite "EVENT: Setup Information (.inf) file types have been found, we have something to process." | |
If ($Remove -eq $False) {LogWrite 'INFO: Starting driver injection...' | |
If ($HideARP -eq $True) {$SwitchHideARP = '/SA' | |
LogWrite 'EVENT: Hide Programs and Features (Add/Remove Programs) entries has been enabled.' | |
LogWrite 'INFO: By default the script allows DPInst to add entries to Programs and Features.'} | |
Get-ChildItem -Name "$DriverSource\TrustedPublisher*.cer" | ForEach-Object {$Certificate = Resolve-Path -Path "$DriverSource\$_" | |
LogWrite "EVENT: Found and processing certificate: $Certificate" | |
&Certutil.exe -addstore -f "TrustedPublisher" "$Certificate" | Out-Null | |
LogWrite "EVENT: CertUtil exit code was $LASTEXITCODE"} | |
If ((Test-Path "$DriverSource\dpinst.xml") -eq $True) {LogWrite 'INFO: Found dpinst.xml, be aware this file may affect your desired settings.'} | |
LogWrite 'EVENT: Starting DPInst to process drivers.' | |
Start-Process -FilePath "$DPInstForThisArchitecture" -ArgumentList "/Q /SE /F $SwitchHideARP" -Wait | |
LogWrite 'INFO: Driver injection complete, please check DPInst log for more details.' | |
WindowsTempCleanup | |
LogWrite -EndOfLog} | |
Else {LogWrite 'INFO: Starting driver removal...' | |
Get-ChildItem -Name "$DriverSource\*.inf" | ForEach-Object {$INF = Resolve-Path -Path "$DriverSource\$_" | |
LogWrite "EVENT: Processing Driver Store removal of $INF" | |
$Args = "/U " + '"' + $INF + '"' + " /Q" | |
Start-Process -FilePath "$DPInstForThisArchitecture" -ArgumentList "$Args" -Wait} | |
LogWrite 'INFO: Driver removal complete, please check DPInst log for more details.' | |
Get-ChildItem -Name "$DriverSource\*.cer" | ForEach-Object {$Certificate = Resolve-Path -Path "$DriverSource\$_" | |
$Shell = New-Object System.Security.Cryptography.X509Certificates.X509Certificate | |
$Shell.Import("$Certificate") | |
$CertificateSerialNumber = $Shell.GetSerialNumberString() | |
LogWrite "EVENT: Found and processing certificate removal: $Certificate" | |
LogWrite "EVENT: Serial number: $CertificateSerialNumber" | |
&Certutil.exe -delstore "TrustedPublisher" "$CertificateSerialNumber" | Out-Null | |
LogWrite "EVENT: CertUtil exit code was $LASTEXITCODE"} | |
WindowsTempCleanup | |
LogWrite -EndOfLog} | |
#<<< End of script work >>> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment