Install-PrinterDriversRemotely.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
<#PSScriptInfo
 
.VERSION 1.0
 
.GUID ede5f6c5-50d3-42a9-b958-daff4b31972d
 
.AUTHOR Juan Granados
 
.COPYRIGHT 2021 Juan Granados
 
.TAGS Install Printer Drivers Remote Remotely PrintNightmare PrinterExport
 
.LICENSEURI https://raw.githubusercontent.com/juangranados/powershell-scripts/main/LICENSE
 
.PROJECTURI https://github.com/juangranados/powershell-scripts/tree/main/Install%20Print%20Drivers%20Remotely
 
.RELEASENOTES
    Initial release
#>


<#
.SYNOPSIS
    Install printer drivers export file (*.printerExport) in a list of computers.
.DESCRIPTION
    Install printer drivers export file (*.printerExport) in a list of computers (plain computer list, OU or CSV file) using PSExec.
    To generate a printer export file with all print drivers, run PrintbrmUI.exe from the computer or server you want to export them.
    Be carefully because this tool exports all printers and ports too. I recommend install all drivers in a test computer without printers and export them using PrintbrmUI.exe.
    Alternatively, you can export all from print server, import in a test computer, delete printers and ports and export again to obtain a printerExport file with only drivers.
    If PSExec is not found on computer, script asks to the user for download it and extract in system folder.
    I recommend use PSExec latest version because v2.2 does not launch printbrm.exe properly.
.PARAMETER printerExportFile
    Path to the printerExport file
    To generate a printer export file with all print drivers, run PrintbrmUI.exe from the computer or server you want to export them.
    Be carefully because this tool exports all printers and ports too. I recommend install all drivers in a test computer without printers and export them using PrintbrmUI.exe.
    Alternatively, you can export all from print server, import in a test computer, delete printers and ports and export again to obtain a printerExport file with only drivers.
.PARAMETER ComputerList
    List of computers in install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
    Example: SRV-RDSH-001,SRV-RDSH-002,SRV-RDSH-003 (Without quotation marks)
.PARAMETER OU
    OU containing computers in which install printer drivers.
    RSAT for AD module for PowerShell must be installed in order to query AD.
     - Install on Windows 10: Get-WindowsCapability -Online |? {$_.Name -like "*RSAT.ActiveDirectory*" -and $_.State -eq "NotPresent"} | Add-WindowsCapability -Online
     - Install on server: Install-WindowsFeature RSAT-AD-PowerShell
    Restart console after installation.
    If you run script from a Domain Controller, AD module for PowerShell is already enabled.
    You can only use one source of target computers: ComputerList, OU or CSV.
    Example: 'OU=Test,OU=Computers,DC=CONTOSO,DC=COM'
.PARAMETER CSV
    CSV file containing computers in which install printer drivers. You can only use one source of target computers: ComputerList, OU or CSV.
    Example: 'C:\Scripts\Computers.csv'
    CSV Format:
        Name
        Computer001
        Computer002
        Computer003
.PARAMETER LogPath
    Path where save log file.
    Default: My Documents
    Example: C:\Logs
.PARAMETER Credential
    Script will ask for an account to perform remote installation.
.EXAMPLE
    Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -OU "OU=RDS,OU=Datacenter,DC=CONTOSO,DC=COM"
.EXAMPLE
    Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -ComputerList SRVRSH-001,SRVRSH-002,SRVRSH-003 -Credential -LogPath C:\Temp\Logs
.EXAMPLE
    Install-PrinterExportRemoteComputers.ps1 -printerExportFile "\\MV-SRV-PR01\Drivers\print_drivers.printerExport" -CSV "C:\scripts\computers.csv"
.LINK
    https://github.com/juangranados/powershell-scripts/tree/main/Install%20Print%20Drivers%20Remotely
.NOTES
    Author: Juan Granados
#>


Param(
        [Parameter(Mandatory=$true,Position=0)] 
        [ValidateNotNullOrEmpty()]
        [string]$printerExportFile,
        [Parameter(Mandatory=$false,Position=1)] 
        [ValidateNotNullOrEmpty()]
        [string]$LocalPath="C:\temp",
        [Parameter(Mandatory=$false,Position=2)] 
        [ValidateNotNullOrEmpty()]
        [string[]]$ComputerList,
        [Parameter(Mandatory=$false,Position=3)] 
        [ValidateNotNullOrEmpty()]
        [string]$OU,
        [Parameter(Mandatory=$false,Position=4)] 
        [ValidateNotNullOrEmpty()]
        [string]$CSV,
        [Parameter(Mandatory=$false,Position=5)] 
        [ValidateNotNullOrEmpty()]
        [string]$LogPath=[Environment]::GetFolderPath("MyDocuments"),
        [Parameter(Position=6)] 
        [switch]$Credential
    )

#Requires -RunAsAdministrator

#Functions

Add-Type -AssemblyName System.IO.Compression.FileSystem
Import-Module BitsTransfer

function Unzip
{
    param([string]$zipfile, [string]$outpath)
    
    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}

$ErrorActionPreference = "Stop"

#Initialice log

$LogPath += "\InstallPrinterExportRemoteComputers_" + $(get-date -Format "yyyy-mm-dd_hh-mm-ss") + ".txt"
Start-Transcript $LogPath
Write-Host "Start remote installation on $(get-date -Format "yyyy-mm-dd hh:mm:ss")"

#Initial validations.

If (!(Test-Path $printerExportFile)){
    Write-Host "Error accessing $($printerExportFile). Script can not continue"
    Stop-Transcript
    Exit 1
}
if (-not($printerExportFile -Like "*.printerExport")) {
    Write-Host "File $($printerExportFile) does not has .printerExport extension. Script can not continue"
    Stop-Transcript
    Exit 1
}

if (!(Get-Command "psexec.exe" -ErrorAction SilentlyContinue)){ 
    Write-Host "Error. Microsoft Psexec not found on system. Download it from https://download.sysinternals.com/files/PSTools.zip and extract all in C:\Windows\System32" -ForegroundColor Yellow
    $Answer=Read-Host "Do you want to download and install PSTools (y/n)?"
    if (($Answer -eq "y") -or ($Answer -eq "Y")){
        Write-Host "Downloading PSTools"
        If (Test-Path "$($env:temp)\PSTools.zip"){
            Remove-Item "$($env:temp)\PSTools.zip" -Force
        }
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        (New-Object System.Net.WebClient).DownloadFile("https://download.sysinternals.com/files/PSTools.zip", "$($env:temp)\PSTools.zip")
        if (Test-Path "$($env:temp)\PSTools.zip"){
            Write-Host "Unzipping PSTools"
            If (Test-Path "$($env:temp)\PSTools"){
                Remove-Item "$($env:temp)\PSTools" -Force -Recurse
            }
            Unzip "$($env:temp)\PSTools.zip" "$($env:temp)\PSTools" 
            Copy-Item "$($env:temp)\PSTools\*.exe" "$($env:SystemRoot)\System32" -Force
            if (Test-Path "$($env:SystemRoot)\System32\psexec.exe"){
                Write-Host "PSTools installed" -ForegroundColor Green 
            }
            else{
                Write-Host "Error unzipping PSTools" -ForegroundColor Red
                Remove-Item "$($env:temp)\PSTools.zip" -Force
                Stop-Transcript
                Exit 1
            }
        }
        else{
            Write-Host "Error downloading PSTools" -ForegroundColor Red
            Stop-Transcript
            Exit 1
        }
    }
    else{
        Stop-Transcript
        Exit 1
    }
}

If ($OU){
    if (!(Get-Command "Get-ADComputer" -ErrorAction SilentlyContinue)){ 
        Write-Host "Error. Get-ADComputer not found on system. You have to install the PowerShell Active Directory module order to query Active Directory." -ForegroundColor Red
        Write-Host 'Windows 10: Get-WindowsCapability -Online |? {$_.Name -like "*RSAT.ActiveDirectory*" -and $_.State -eq "NotPresent"} | Add-WindowsCapability -Online'
        Write-Host "Server: Install-WindowsFeature RSAT-AD-PowerShell"
        Write-Host "Restart console after installation"
        Stop-Transcript
        Exit 1
    }
    try{
        $ComputerList = Get-ADComputer -Filter * -SearchBase "$OU" | Select-Object -Expand name
    }catch{
        Write-Host "Error querying AD: $($_.Exception.Message)" -ForegroundColor Red
        Stop-Transcript
        Exit 1
    }
}
ElseIf ($CSV){
    try{
        $ComputerList = Get-Content $CSV | where {$_ -notmatch 'Name'} | Foreach-Object {$_ -replace '"', ''}
    }catch{
        Write-Host "Error getting CSV content: $($_.Exception.Message)" -ForegroundColor Red
        Stop-Transcript
        Exit 1
    }
}
ElseIf(!$ComputerList){
    Write-Host "You have to set a list of computers, OU or CSV." -ForegroundColor Red
    Stop-Transcript
    Exit 1
}
If ($Credential){
    $Cred = Get-Credential
}
$usingCredential = $false
If(!$Cred -or !$Credential){
    Write-Host "No credential specified. Using logon account"
}
Else{
    $usingCredential = $true;
    Write-Host "Using user $($Cred.UserName)"
    $UserName = $Cred.UserName
    $Password = $Cred.GetNetworkCredential().Password
}
ForEach ($Computer in $ComputerList) {
    Write-Host "Processing computer $Computer"
    $Destination = "\\$Computer\C$\Temp\printer_drivers.printerExport"
    try {
    if (-not(Test-Path "\\$Computer\C$\Temp\")) {
        mkdir "\\$Computer\C$\Temp\"
    }
    Start-BitsTransfer -Source $printerExportFile -Destination $Destination -Description "Copy $driverFile to $Computer" -DisplayName "Copying"
    } catch {
        Write-Host "Error copying file: $($_.Exception.Message)"
        continue
    }
    Write-Host "Launching installation using PSExec in $Computer. This may take a while, please be patient..."
    try {
        if ($usingCredential) {
            psexec.exe /accepteula -h -i "\\$Computer" -u $UserName -p $Password C:\Windows\System32\spool\tools\Printbrm.exe -F C:\Temp\printer_drivers.printerExport -R
        }
        else {
            psexec /accepteula -h "\\$Computer" C:\Windows\System32\spool\tools\Printbrm.exe -F C:\Temp\printer_drivers.printerExport -R
        }
    } catch {
        Write-Host "PSExec return an error. Check console output above"
    }

    try {
        Write-Host "Removing remote file"
        Remove-Item $Destination -Force;
    } catch {
        Write-Host "Error removing remote file: $($_.Exception.Message)" -ForegroundColor Red
    }
}     
Stop-Transcript