Installing Updates and patches on remote computers by powershell

In this post, we will use PowerShell to install updates and patches on remote computers using PSExec utility. Although this script is not as efficient as a full fledged SCCM or WUSA setup, but it can come handy in scenarios where the task of patching has to be done manually.

There are certain things we need to ensure before proceeding with this method. Firstly, PSExec service must not be in disabled or marked for deletion state in target remote server. Secondly, the account launching the script should have appropriate administrative privileges. Thirdly, execution of scripts must not be disabled in remote computers via group policy.

Step 1> Proceeding with this method, first create a runme file with name, say runme.ps1 as follows:

<#------------Creating log file------------#>
$CSVFile = "C:\patches\servers_status.csv"
<#-----------------------------------------#>

<#-----------Execute PSEXEC----------------#>
$machines = get-content "C:\patches\servers.txt"
ForEach ($machine In $machines) 
{
 if (Test-Connection -ComputerName $machine -Count 2 -ErrorAction SilentlyContinue)
  {
   Write-Host "$machine is up" -ForegroundColor Green & C:\Windows\System32\psexec.exe \\$machine -u COMPANY\admin -p ###### -h -accepteula cmd.exe /c "\\SERVER1\patches$\script.bat" 
   Restart-Computer $machine -Force
   Write-Host "$machine rebooted" -ForegroundColor Red
   $info = "$machine rebooted"
   Add-content -value $info -Path $CSVFile
  }
else
  {
   Write-Host "$machine is down" -ForegroundColor Red
   $info = "$machine is down"
   Add-content -value $info -Path $CSVFile
  }
}
<#----------------------------------------#>

Step 2> Above script will call out to following batch file “Script.bat” over network.

@Echo Off
Net Use T: /delete /yes
Net Use T: \\SERVER1\patches$
CMD /C powershell.exe -ExecutionPolicy Bypass -Command T:\install_patches.ps1
Net Use T: /delete /yes
Exit

Step 3> Above script will map the folder with script as a network drive on remote computer and then call out to following install_patches.ps1 file which will then perform the installation of update packages. Note that now we won’t use network path of folders, instead we will write complete local path. As the script runs locally on the remote computer, this script will offset the delay caused due to network latency.

<#--------Create folder "patches"-----------#>
if (-Not (Test-Path C:\Users\admin\desktop\patches))
{
    md -path C:\Users\admin\desktop\patches
}
Write-Host "CCM folder created, starting copy of install files"
#------------------------------------
Copy installation files from source to 
target server's desktop #>
Copy-Item -path "\\SERVER2\Packages\*" -destination "C:\Users\admin\desktop\patches" -Recurse
#----------------------------------#
<#ensure 100% copy by matching source 
and destination folder size#>
Do 
{
 start-sleep 2
 write-host "copy of patches is in progress"
 $FolderList1 = Get-ChildItem "\\SERVER2\Packages\"
 Foreach($Folder1 in $FolderList1)
  {
   $FolderSize1 = (Get-ChildItem "\\SERVER2\Packages\$Folder1" -Recurse | 
   Measure-Object -property length -sum).Sum/1mb
   write-host "source folder size: $FolderSize1" 
  }
 $FolderList2 = Get-ChildItem "C:\Users\admin\desktop\patches"
 Foreach($Folder2 in $FolderList2)
  {
   $FolderSize2 = (Get-ChildItem`
   "C:\Users\admin\desktop\CCM\$Folder2" -Recurse | 
   Measure-Object -property length -sum).Sum/1mb
   write-host "destination folder size: $FolderSize2"  
  }
} 
until ($FolderSize1 -eq $FolderSize2)
write-host "installation files copied successfully"
#--------------------------------------
Start installation of patches and create log file #>
write-host "installation of patches started"
$dir = (Get-Item -Path "C:\Users\admin\Desktop\patches\*" -Verbose).FullName
Foreach($item in (ls $dir *.cab -Name))
{
 echo $item
 $item = $dir + "\" + $item
 dism /online /Add-Package /PackagePath:$item  /NoRestart | Out-Null
}
Foreach($item in (ls $dir *.msu -Name))
{
 echo $item
 $item = $dir + "\" + $item
 wusa $item /quiet /NoRestart | Out-Null
}
$machinename = hostname
write-host "$machinename patching completed"
$outinfo = $machinename + "," + "patching completed"
Add-content -value $outinfo -path "T:\servers_status.csv"

After successful reboot of all servers, we can run the following script to get the list of hotfixes installed on each remote computer in the past one day and output the same to corresponding server name’s csv file, as follows:

$machines = get-content "C:\patches\servers.txt"
ForEach ($machine In $machines)
{
 get-hotfix -computername $machine | 
 Select HotfixID, Description, InstalledOn |
 Where-Object {$_.installedon -gt (get-date).adddays(-1)}|
 Sort-Object -property installedon -Descending |
 export-csv "C:\patches\$machinename hotfixes.csv" -NoTypeInformation
}
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