I recommend using this function with Get-HotFix, or Get-WmiObject -class Win32_product for a list of installed patches/software to determine if installation is needed and/or was successful. You can also come in after the push and look at the log files on the servers in an automated fashion to see how things went.
This script does require PSRemoting to be enabled on target servers, which is true by default on all Windows Server 2012 R2 installs, but needs to be turned on either by GPO or by hand on previous versions. There's a built in workaround if you pull down PSExec which will work on older servers without PSRemoting.
This file contains hidden or 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
#Requires -Version 3.0 | |
Function Push-Patch{ | |
<# | |
.SYNOPSIS | |
Deploys KB hotfix patch, MSI setup file, or an install-shield based program to remote servers | |
.DESCRIPTION | |
There's no "smart" installer type detection, it relies on the file naming convention. | |
Only works with single file installers. Automatically creates install log file in destination temp. | |
Requires all destination servers have PSRemoting enabled, or PSExec is installed locally and specified. | |
(https://technet.microsoft.com/en-us/sysinternals/bb897553.aspx) | |
.PARAMETER ComputerName | |
Specifies one or more server names to install to. | |
.PARAMETER PatchPath | |
Full local or UNC path to the file to be pushed out. | |
.PARAMETER NoRestart | |
Prevents installer from automatically rebooting servers. | |
.PARAMETER ForceRestart | |
Forces servers to restart even if it's not required. | |
.PARAMETER DestinationTemp | |
Temporary directory used on target servers for staging patch file. | |
.PARAMETER UsePSExec | |
Used as a last resort; it's better to have PSRemoting enabled on your target servers as PSexec is much slower (10-100X). | |
.EXAMPLE | |
Get-Content .\serverlist.txt | Push-Patch -PatchPath "\\server1\share\KB999999.msi" -NoRestart | |
Pushes the KB hotfix to all the servers on the serverlist.txt and remotely runs them using MSIEXEC and will prevent automatic restarts | |
.EXAMPLE | |
Push-Patch -ComputerName ServerA,ServerB -PatchPath "c:\softwarerepo\AdobeReader\Fullsetup.exe" -ForceRestart | |
Pushes the adobe reader setup to two servers and runs it and forces a reboot after installation is completed. | |
.LINK | |
http://www.bryanvine.com/2015/08/powershell-script-push-patch-remote.html | |
.LINK | |
Invoke-Command | |
.NOTES | |
Author: Bryan Vine | |
Last updated: 8/10/2015 | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$true,valuefrompipeline=$true,Position=0)] | |
[alias("CN","MachineName","ServerName","Server")][ValidateNotNullOrEmpty()][string[]] | |
$ComputerName, | |
[Parameter(Mandatory=$true,Position=1)][ValidateNotNullOrEmpty()] | |
[ValidateScript({Test-Path $_})][string] | |
$PatchPath, | |
[switch]$NoRestart, | |
[switch]$ForceRestart, | |
[String]$DestinationTemp = "C:\TEMP", | |
[switch]$UsePSExec | |
) | |
BEGIN{ | |
if($NoRestart){$NoRestartstr="/norestart"} | |
if($ForceRestart){$ForceRestartstr="/forcerestart"} | |
$file = dir $PatchPath | select -ExpandProperty Name | |
$logfile = "$destinationTemp\$file-install.log" | |
$AllComputers = @() | |
#building wrapper for remote exection | |
$runcommand = @() | |
if($file -like "*.exe"){ | |
if($ForceRestart){$ForceRestartstr="/forcerestart /forceappsclose"} | |
$runcommand += "$file /quiet /log:$logfile $NoRestartstr $ForceRestartstr" | |
} | |
if($file -like "*.msu"){ | |
if($ForceRestart){$ForceRestartstr="/forcerestart"} | |
$runcommand += "wusa $file /quiet /log:$logfile $NoRestartstr $ForceRestartstr" | |
} | |
if($file -like "*.msi"){ | |
if($ForceRestart){$ForceRestartstr="/forcerestart"} | |
$runcommand += "msiexec /i $file /qn /quiet /log $logfile $NoRestartstr $ForceRestartstr" | |
} | |
} | |
PROCESS{ | |
#Stage files | |
$ComputerName | %{ | |
if(test-path "\\$_\c$"){ | |
if(!(test-path "\\$_\$($DestinationTemp.replace(":","$"))")){ | |
New-Item -Path "\\$_\$($DestinationTemp.replace(":","$"))" -ItemType directory -Force | |
} | |
Write-Verbose "Copying Patch to $_" | |
Copy-Item -Path $PatchPath -Destination "\\$_\$($DestinationTemp.replace(":","$"))" | |
#writing remote wrapper. | |
"$($DestinationTemp.Substring(0,2)) & cd $DestinationTemp & $runcommand" | | |
out-file -Encoding ascii -FilePath "\\$_\$($DestinationTemp.replace(":","$"))\runpatch.cmd" | |
$AllComputers += $_ | |
} | |
else{ | |
Write-Warning "Unable to access $_" | |
} | |
} | |
} | |
END{ | |
#Remotely executing patch | |
if($UsePSExec){ | |
$serverlist = "$env:TEMP\serverlist.txt" | |
$AllComputers | Set-Content $serverlist | |
psexec `@$serverlist -accepteula -e -s -h -d -n 20 "$DestinationTemp\runpatch.cmd" | |
Remove-Item $serverlist | |
} | |
else{ | |
Invoke-Command -ComputerName $AllComputers -ThrottleLimit 500 -AsJob -ScriptBlock ([scriptblock]::Create("$DestinationTemp\runpatch.cmd")) | | |
Receive-Job -AutoRemoveJob -Wait | |
} | |
#cleanup | |
$AllComputers | %{ | |
Remove-Item "\\$_\$($DestinationTemp.replace(":","$"))\runpatch.cmd" | |
Remove-Item "\\$_\$($DestinationTemp.replace(":","$"))\$file" | |
} | |
} | |
} | |
Wonderful script Bryan.
ReplyDeleteThanks a lot for this.
However I am getting the following error, could you please advice?
Windows update could not be installed because of error 2147942405 "Access is denied."
Make sure the account you are running as is also a local administrator on the remote server. Also look at the log file created in the default temp location on the remote server, it should have more details. You might need to tweak the install parameters for certain patches.
ReplyDeleteThanks Bryan for the quick response.
ReplyDeleteYes, the account is a local admin on the target machine.
Regarding the generated log file, unable to read it using notepad as it is showing as follows.
ø ø ø ! À¬ t D ßf
“ €% ‡Í c ÕÐ Zb (
@ t z r e s . d l l , - 2 6 2
@ t z r e s . d l l , - 2 6 1 Äÿÿÿ ` êAÆÒÐ 6¾& žiÈb ÕÐ W U S A 5 9 5 6 C : \ T E M P \ W i n d o w s 8 . 1 - K B 2 9 8 8 9 4 8 - x 6 4 . m s u - i n s t a l l . l o g ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ