Here again is my take on automatic updating. We have a guy that handles patching via a 3rd party tool (Kaspersky). I don't like him touching my domain controllers so I used a number of scripts I found to distill down to the script below.
Kaspersky uses a network agent to keep the settings uniform and that causes issues when you want to use the Microsoft update site. This script is designed to stop this agent temporarily and allow normal patching to occur.
It requires the NuGet and PSWindowsUpdate modules but will auto-load them if needed. It also requires PowerShell v5.
To simplify the entire process I decided that the script should run twice, once Saturday night to apply patches, then again Sunday morning to verify what was done. The verification sends and html coded report to whoever you chose. It also creates a .CVS log file and only keeps the last 10 to avoid filling the disk.
So far this seems to work quite well. It will FORCE a reboot to be sure the patches get in right so understand that before you run it.
This script uses no external config file so you need to edit the internal config lines. It sets up scheduled tasks to re-run itself on days noted in the config file. If it sees the tasks don't exist it will create them. It sets them up to run using the LOCAL system context.
I added the ability to deploy the script to other systems listed in the config file. That helps get all instances to the same version.
Please test this thoroughly before running on any production systems. My setup will undoubtedly be different from yours and this was built to pause Kaspersky before it will run.
Command line options for console output and status only emails are documented within the script.
Note that I use encrypted credentials stored in the config file. The example below has bogus data.
(12-01-17: Updated to v2.5 below to fix a minor bug...)
param(
[bool]$Console = $False, #--[ Set to true to enable local console
result display. Defaults to false ]--
[bool]$Debug = $False, #--[ Set to true to only send results to
debug email address. Default to false ]--
[bool]$Manual = $False, #--[ Use to run update off-schedule ]--
[bool]$Status = $False, #--[ If set to true checks for results
after the reboot and emails, then goes idle. ]--
[bool]$Deploy = $False #--[ If set to true will copy this
script to the other members of the peer group ]--
)
<#======================================================================================
File Name : UnattendedUpdate.ps1
Original Author : Kenneth C. Mazie (kcmjr
AT kcmjr.com)
:
Description : Will scan the Windows
Update site and install all missing updates.
:
Operation : Requires PowerShell
v5. Requires NuGet and PSWindowsUpdate
: modules. Will auto-install them if needed. Reboots system if Powershell
: determines it is
required. Creates appropriate LOCAL
scheduled
: tasks automatically on
first run. Schedules are set to randomize run
: within a 90 minute
window. Update routine is governed by
the week days
: noted in the config
fileRequires a config file in XML format located
: in the same folder as the
script. See example at bottom.
:
Arguments : Normal operation is with
no command line options.
: -Console $true (Will enable local console output)
: -Debug $true (Not used)
: -Manual $true (forces a manual run bypassing the
schedule)
: -Status $true (forces a status email to be sent)
: -Deploy $true (forces the script and config file to be
copied to the identical
: location on the other peer
servers listed in the config file)
:
Warnings : Uses local SYSTEM user
context for tasks. Install LOCALLY, not
remotely.
: Adjust the task
schedule(s) to conform to your maintenance window.
:
Legal : Public Domain. Modify and
redistribute freely. No rights reserved.
: SCRIPT PROVIDED "AS
IS" WITHOUT WARRANTIES OR GUARANTEES OF
: ANY KIND. USE AT YOUR OWN
RISK. NO TECHNICAL SUPPORT PROVIDED.
:
Credits : Code snippets and/or
ideas came from many sources including but
: not limited to the following:
: https://www.powershellgallery.com/packages/PSWindowsUpdate/1.5.2.2
:
Last Update by : Kenneth C. Mazie (kcmjr
AT kcmjr.com)
#>
$Script:ScriptVer = "2.5"
<#
Version History : v1.0 - 12-29-16 -
Original
Change History : v2.0 - 01-13-17 - Added
forced status option for Sundays.
: v2.1 - 01-17-17 - Added
regkey delete.
: v2.2 - 04-06-17 - Fixed
rowdata to start clean at each loop
: v2.3 - 09-28-17 - Turned
off extra email after patching.
: Moved config file out. Added script replication option.
: Added run day from config file option.
: v2.4 - 10-19-17 - Added
randomizer for reboot. Added check for no data.
: v2.5 - 12-01-17 - Fixed
runday detection
:
=======================================================================================#>
#Requires
-version 5.0
clear-host
if ($Console){$Script:Console = $true}
if ($Debug){$Script:Debug = $true}
if ($Manual){$Script:Manual = $true}
if ($Status){$Script:Status = $true}
if ($Deploy){
$Script:Deploy = $true
$Script:Console = $true
}
$ErrorActionPreference = "SilentlyContinue"
$Now = Get-Date -Format "MM-dd-yyyy_HHmm"
$Script:ThisComputer = $Env:Computername
$Script:MessageBody = "Starting
Local Patch Processing...<br>"
#--------------------------------------
$Today = (get-date).dayofweek
$Script:Attach = $false
$Script:ResultLog = "$PSScriptRoot\Results-$Now.csv"
$Script:KillKaspersky = $true #--[ Used to stop the Kaspersky agent
during updates. Set to $false if not
applicable ]--
$Script:ScriptName = $MyInvocation.MyCommand.Name
$Script:ScriptFullPath = $PSScriptRoot+"\"+$MyInvocation.MyCommand.Name
$ConfigFile = $Script:ScriptFullPath.Split(".")[0]+".xml"
$Script:UserContext = [Security.Principal.WindowsIdentity]::GetCurrent()
#==[ Functions
]================================================================
function LoadConfig {
#--[ Read and
load configuration file ]-------------------------------------
if (!(Test-Path $ConfigFile)){ #--[ Error out
if configuration file doesn't exist ]--
$Script:HTMLData = "MISSING
CONFIG FILE. Script aborted."
if ($Script:Log){Add-content -Path "$PSScriptRoot\debug.txt" -Value "MISSING
CONFIG FILE. Script aborted."}
write-host "CONFIGURATION
FILE $ConfigFile NOT FOUND - EXITING" -ForegroundColor Red
break
}else{
[xml]$Script:Configuration = Get-Content $ConfigFile #--[ Read & Load XML ]--
$Script:PeerList = $Script:Configuration.Settings.General.PeerList
$Script:RunDays = $Script:Configuration.Settings.General.RunDays
$Script:RunTime = $Script:Configuration.Settings.General.RunTime
$Script:DebugEmail = $Script:Configuration.Settings.Email.Debug
$Script:eMailRecipient = $Script:Configuration.Settings.Email.To
$Script:eMailFrom = $ThisComputer+'_'+$Script:Configuration.Settings.Email.From
$Script:eMailHTML = $Script:Configuration.Settings.Email.HTML
$Script:eMailSubject = $ThisComputer+' '+($Script:Configuration.Settings.Email.Subject)
$Script:SmtpServer = $Script:Configuration.Settings.Email.SmtpServer
$Script:UserName = $Script:Configuration.Settings.Credentials.Username
$Script:EncryptedPW = $Script:Configuration.Settings.Credentials.Password
$Script:Base64String = $Script:Configuration.Settings.Credentials.Key
$Script:ReportName = $ThisComputer+' '+($Script:Configuration.Settings.General.ReportName)
}
$ByteArray = [System.Convert]::FromBase64String($Script:Base64String);
$Script:Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Script:UserName, ($Script:EncryptedPW | ConvertTo-SecureString -Key $ByteArray)
$Script:Password = $Script:Credential.GetNetworkCredential().Password
}
function SendEmail {
$SMTP = new-object System.Net.Mail.SmtpClient($Script:SmtpServer)
$Email = New-Object System.Net.Mail.MailMessage
$Email.Body = $Script:MessageBody
$Email.IsBodyHtml = $Script:eMailHTML
$Email.To.Add($Script:eMailRecipient)
if ($Script:Attach){
$Attachment = New-Object System.Net.Mail.Attachment –ArgumentList $Script:ResultLog, 'Application/Octet'
$Email.Attachments.Add($Attachment)
}
$Email.From = $Script:eMailFrom
$Email.Subject = $Script:eMailSubject
$SMTP.Send($Email)
$Email.Dispose()
$SMTP.Dispose()
}
function GetResults {
$Script:ResultOut = ""
$Script:ResultLine = ""
$RowFlag = $false
#--[ Add header to html output file ]--
# $Script:HTMLData = @()
$Script:MessageBody += '<tr><th>KB
Number</th><th>Status</th><th>Date /
Time</th><th>Results</th><th>Messages</th></tr>'
#--[ HTML row Settings
]----------------------------------------------------
$BGColor = "#dfdfdf"
#--[ Grey default cell background ]--
$BGColorRed = "#ff0000"
#--[ Red background for alerts ]--
$BGColorOra = "#ff9900" #--[ Orange
background for alerts ]--
$BGColorYel = "#ffd900"
#--[ Yellow background for alerts ]--
$FGColor = "#000000"
#--[ Black default cell foreground ]--
#--[ Only keep 10 of the last runtime
logs ]------------------------------------
Get-ChildItem -Path $PSScriptRoot | Where-Object {(-not $_.PsIsContainer) -and ($_.Name -like "*results*.csv")} | Sort-Object -Descending -Property LastTimeWrite | Select-Object -Skip 10 | Remove-Item
#--[ Scan Eventlogs for Event 19 &
20 ]--
$Script:LogDump = Get-WinEvent -FilterHashtable @{LogName = "System";ID=19,20}
foreach ($Script:LogItem in $Script:LogDump ){
if ($Script:LogItem.ProviderName -eq 'Microsoft-Windows-WindowsUpdateClient'){
$RowFlag = $true
$RowData = '<tr>'
#--[ Start table row ]--
$Script:LogItemStat = $Script:LogItem.message.split(":")[0]
if ($Script:LogItem.Message -like "*(KB*"){
$Script:LogItemKB = ($Script:LogItem.message.split("(")[1]).split(")")[0]
if (!($Script:LogItemKB -like "KB*")){
$Script:LogItemKB = ($Script:LogItem.message.split("(")[2]).split(")")[0]
}
}else{
$Script:LogItemKB = $Script:LogItem.message.split(":")[2] #"---------"
}
if ($Script:Console){write-host $Script:LogItemKB" " -ForegroundColor Red -NoNewline }
$RowData += '<td bgcolor=' + $BGColor + '><font
color=' + $FGColor + '>' + $Script:LogItemKB + '</td>'
if ($Script:Console){write-host $Script:LogItem.LevelDisplayName" " -ForegroundColor yellow -NoNewline }
if ($Script:LogItem.LevelDisplayName -like "Error"){
$RowData += '<td
bgcolor=' + $BGColor + '><font color=#800000>' + $Script:LogItem.LevelDisplayName + '</td>'
$RowData += '<td
bgcolor=' + $BGColor + '><font color=#800000>' + $Script:LogItemStat + '</td>'
}else{
$RowData += '<td
bgcolor=' + $BGColor + '><font color=' + $FGColor + '>' + $Script:LogItem.LevelDisplayName + '</td>'
$RowData += '<td
bgcolor=' + $BGColor + '><font color=' + $FGColor + '>' + $Script:LogItemStat + '</td>'
}
if ($Script:Console){write-host $Script:LogItem.TimeCreated" " -ForegroundColor magenta -NoNewline }
$RowData += '<td
bgcolor=' + $BGColor + '><font color=' + $FGColor + '>' + $Script:LogItem.TimeCreated + '</td>'
if ($Script:Console){write-host $Script:LogItemStat" " -ForegroundColor Cyan -NoNewline }
#$RowData += '<td bgcolor=' +
$BGColor + '><font color=#800000>' + $Script:LogItemStat +
'</td>' #--[ Note: Added to error
detection above ]--
if ($Script:Console){write-host $Script:LogItem.Message" " -ForegroundColor green}
$RowData += '<td bgcolor=' + $BGColor + '><font
color=' + $FGColor + '>' + $Script:LogItem.message + '</td>'
$Script:ResultLine = $Script:LogItemKB+","+$Script:LogItem.LevelDisplayName+","+$Script:LogItem.TimeCreated+","+$Script:LogItemStat+","+$Script:LogItem.Message
$Script:ResultOut = $Script:ResultOut+$Script:ResultLine+"`n"
$RowData += '</tr>'
}
$Script:MessageBody += $RowData
}
If ($RowFlag){
$Script:Attach = $true
}Else{
$Script:MessageBody += '<td
colspan=5 bgcolor=' + $BGColor + '><font color=' + $FGColor + '><center>No New Data
to Report </center></td>'
}
Add-Content -value $Script:ResultOut -path $Script:ResultLog
$Script:MessageBody += '</table><br>'
$Script:MessageBody += "<font
size=3 face='times new roman'>- Done.
Emailing results...<br>"
if ($Script:Console){Write-Host "`n-
Done. Emailing results...`n" -ForegroundColor Magenta }
SendEmail
}
function ServiceMgr ($Svc, $SvcStatus) {
$Script:MessageBody = $Script:MessageBody + "-
Processing :$Svc<br>"
if ($Script:Console){Write-Host "-
Processing :$Svc" -ForegroundColor Magenta }
$Count = 0
#--[ State prior to start/stop process
]--
$Script:SvcState = (Get-Service -Name $Svc).Status
$Script:MessageBody = $Script:MessageBody + "-- $Svc
Initial Status: $Script:SvcState<br>"
if ($Script:Console){Write-Host "-- $Svc
Initial Status: $Script:SvcState" -ForegroundColor Magenta }
$Script:MessageBody = $Script:MessageBody + "---
Pausing while attemtping to set service state to: $SvcStatus...<br>"
if ($Script:Console){Write-Host "---
Pausing while attemtping to set service state to: $SvcStatus..." -ForegroundColor Magenta }
while ((Get-Service -Name $Svc).Status -ne $SvcStatus){
Get-Service -Name $Svc | Set-Service -Status $SvcStatus
sleep -Milliseconds 500
$Count ++
if ($Count -ge 20){
if ($Script:Console){Write-Host "-- There
was an error setting the "$Svc" service to the "$SvcStatus"
state..." -ForegroundColor Magenta }
$Script:MessageBody = $Script:MessageBody + '-- There was
an error setting the "$Svc" service to the "$SvcStatus"
state...<br>'
break
}
}
#--[ State after start/stop process ]--
$Script:SvcState = (Get-Service -Name $Svc).Status
$Script:MessageBody = $Script:MessageBody + "-- $Svc
Final Status: $Script:SvcState<br>"
if ($Script:Console){Write-Host "-- $Svc
Final Status: $Script:SvcState" -ForegroundColor Magenta }
Sleep -Seconds 1
if ($Script:SvcState -eq "stopped"){
RegKill
}
}
function RegKill {
if ($Script:Console){Write-Host "--
Removing Windows Update registry key..." -ForegroundColor Magenta }
try{
Remove-ItemProperty -Name 'WUServer' -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Force:$true -Confirm:$false
Remove-ItemProperty -Name 'WUStatusServer' -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Force:$true -Confirm:$false
Remove-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -Force:$true -Confirm:$false -Recurse:$true
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + "- Failed
to remove Windows Update Key(s).
$ErrorMessage<br>"
if ($Script:Console){Write-Host "- Failed
to remove Windows Update Key(s).
$ErrorMessage" -ForegroundColor Red }
}
if (Test-Path -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'){
if ($Script:Console){Write-Host "-- FAILED
to remove Windows Update registry key..." -ForegroundColor Magenta }
$Script:MessageBody = $Script:MessageBody + "- Failed
to remove Windows Update Key(s).<br>"
}else{
if ($Script:Console){Write-Host "--
Verified removal of Windows Update registry key..." -ForegroundColor Magenta }
$Script:MessageBody = $Script:MessageBody + "-
Verified removal of Windows Update registry key(s).<br>"
}
}
function PatchIt {
if ($Script:KillKaspersky){
ServiceMgr "klnagent" "stopped" #--[ Stops Kaspersky agent prior to
running update. Comment out if not
applicable. ]--
}else{
$SvcState = "stopped"
}
if ($SvcState -eq "stopped"){
#--[ NuGet is required to pull the
module from the MS repository ]--
if (!(Get-PackageProvider NuGet)){
if (!(Get-ChildItem -Path "C:\Program
Files\PackageManagement\ProviderAssemblies\nuget" -Filter "Microsoft.PackageManagement.NuGetProvider.dll" -Recurse)){
try{
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -ErrorAction SilentlyContinue -Confirm:$false -Force:$true
$Script:MessageBody = $Script:MessageBody + "- NuGet
provider is being installed.<br>"
if ($Script:Console){Write-Host "- NuGet
provider is being installed." -ForegroundColor Magenta }
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + "- NuGet
module install FAILED.
$ErrorMessage<br>"
if ($Script:Console){Write-Host "- NuGet
module install FAILED.
$ErrorMessage" -ForegroundColor Red }
}
}
}
#--[ Install the update module if it's
not already loaded ]--
if (!(Get-Module PSWindowsUpdate)){
try{
$Script:MessageBody = $Script:MessageBody + '-
"PSWindowsUpdate" module is being installed.<br>'
if ($Script:Console){Write-Host '-
"PSWindowsUpdate" module is being installed...' -ForegroundColor Magenta }
Install-Module PSWindowsUpdate -ErrorAction Stop -Confirm:$false -Force:$true
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + '-
"PSWindowsUpdate" module install FAILED. $ErrorMessage<br>'
if ($Script:Console){Write-Host '-
"PSWindowsUpdate" module install FAILED. $ErrorMessage' -ForegroundColor Red }
}
}
#--[ Register to use the Microsoft
Update Service, as opposed to just the default Windows Update Service. ]--
if (!((Get-WUServiceManager).ServiceID -contains "7971f918-a847-4430-9279-4a52d1efe18d")){
try{
$Script:MessageBody = $Script:MessageBody + "- Service
Manager ID is being registered.<br>"
if ($Script:Console){Write-Host "- Service
Manager ID is being registered." -ForegroundColor Magenta }
Add-WUServiceManager -ServiceID '7971f918-a847-4430-9279-4a52d1efe18d' -Confirm:$false -ErrorAction Stop
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + "- Service
Manager ID registration FAILED.
$ErrorMessage<br>"
if ($Script:Console){Write-Host "- Service
Manager ID registration FAILED.
$ErrorMessage" -ForegroundColor Red }
}
}
#--[ Run the update in unattended mode,
check for all updates on the MS update site, accept all EULAs, reboot if needed
]--
$Script:MessageBody = $Script:MessageBody + "-
Checking for and installing new updates.<br>- Because a reboot is
required to register new patches, results are emailed following every
reboot. The system will reboot
shortly."
if ($Script:Console){Write-Host "-
Checking for and installing new updates.
`nBecause a reboot is required to register new patches, results are emailed
following every reboot. The system will
reboot shortly." -ForegroundColor Magenta }
$Script:Attach = $false
#SendEmail #--[ Disabled so that the emails only go out
are after reboot or on svc error ]--
Get-WUInstall –MicrosoftUpdate –AcceptAll -Confirm:$false –AutoReboot:$true
}else{
$Script:MessageBody = $Script:MessageBody + "-- $Svc
Failed to stop -- ABORTING --"
if ($Script:Console){Write-Host "`n-- $Svc
Failed to stop -- ABORTING --`n" -ForegroundColor Red }
SendEmail
}
#--[ Things to do if no reboot occurs
after running update. Comment items out
if not applicable. ]--
Sleep -Seconds 60 #--[ Wait to see if no reboot has
occurred ]--
#if ($Script:KillKaspersky){
#
ServiceMgr "klnagent" "running"
#}else{
#
$SvcState = "running"
#}
#--[ Rerun the script with status
option. Not needed if forcing a restart
]--
#$ScriptBlock =
[Scriptblock]::Create((Get-Content $Script:ScriptFullPath -Raw))
#Invoke-Command -NoNewScope
-ArgumentList '-status $true' -ScriptBlock $ScriptBlock -Verbose #--[ Rerun this script and send results if no
reboot occurred ]--
#--[ Force a reboot with random delay if
none occurred. Scheduled task already
randomizes so this is optional ]--
#$RndArray = @(5,10,15,20,25,30)
#$Rnd = (new-object System.Random)
#$RndDelay = $Array[ $Rnd.Next(
$Array.Count ) ]
$RndDelay = 1
Restart-Computer -Confirm $false -Delay $RndDelay
}
function ScheduledTask ($TaskName){
if (!(Get-ScheduledTask | Where-Object {$_.TaskName -like $TaskName })){
if ($Script:Console){Write-Host '- Creating new
scheduled task "'$TaskName'"...' -ForegroundColor Cyan }
$Script:MessageBody = $Script:MessageBody + '- Creating new
scheduled task "'+$TaskName+'"...<br>'
$Principal = New-ScheduledTaskPrincipal -UserID "NT
AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Highest
if ($TaskName -eq "UpdateStatus"){ #--[ Creates status task with 5 minute
delay that runs at startup ]--
$Argument = '-WindowStyle Hidden
–Noninteractive -noprofile -nologo -executionpolicy Bypass -Command
"&{'+$Script:ScriptFullPath+' -status $true}"'
$Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $Argument
$Trigger = New-ScheduledTaskTrigger -AtStartup -RandomDelay (New-TimeSpan -Minutes 5)
$Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Settings (New-ScheduledTaskSettingsSet)
try{
$Create = Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Principal $principal
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + '- Scheduled
Task "'+$TaskName+'" failed to be created...
$ErrorMessage<br>'
if ($Script:Console){Write-Host '- Scheduled
Task '"$TaskName"' failed to be created... $ErrorMessage' -ForegroundColor Red }
}
}else{
#--[ Creates patch task with 90 minute random delay ]--
$Argument = '-WindowStyle Hidden
–Noninteractive -noprofile -nologo -executionpolicy Bypass -Command
"&{'+$Script:ScriptFullPath+'}"'
$Action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument $Argument
$Trigger = New-ScheduledTaskTrigger -Daily -RandomDelay (New-TimeSpan -Minutes 90) -At $Script:RunTime
$Task = New-ScheduledTask -Action $Action -Trigger $Trigger -Settings (New-ScheduledTaskSettingsSet)
try{
$Create = Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Principal $principal -ErrorAction Stop
}catch{
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
$Script:MessageBody = $Script:MessageBody + '- Scheduled
Task "'+$TaskName+'" failed to be created...
$ErrorMessage<br>'
if ($Script:Console){Write-Host '- Scheduled
Task "'$TaskName'" failed to be created...
$ErrorMessage' -ForegroundColor Red }
}
}
}else{
if ($Script:Console){Write-Host '- Task "'$TaskName'" already
exists...' -ForegroundColor Cyan }
$Script:MessageBody = $Script:MessageBody + '- Task "'+$TaskName+'" already
exists...<br>'
}
}
function DeployScript{ #--[ Copies this script to all other
systems noted in the config files. ]--
if ($Script:Console){Write-Host '--[ Deploying
Updated Script to Peer Hosts ]------------' -ForegroundColor Yellow}
foreach ($Target in $Script:PeerList.Split(",")){
if ($Script:Console){Write-Host `n'--[ Deploying
to'($Target.ToUpper())'
]-------------------------' -ForegroundColor Cyan}
if ($Target -ne $ThisComputer){
if (Test-Path "\\$Target\c$\scripts\unattendedupdate.ps1"){
if ($Script:Console){Write-Host " -- Existing files found..." -ForegroundColor green
try{
Get-ChildItem -Path "\\$Target\c$\scripts\" | where{$_.Name -match "unattendedupdate.*"} | Remove-Item -Force:$true -Confirm:$false
}catch{
if ($Script:Console){Write-Host " -- File delete on $Target FAILED..." -ForegroundColor Red}
if ($Script:Console){Write-Host " -- Error Message = "$_.Exception.Message}
if ($Script:Console){Write-Host " -- Error Item = "$_.Exception.ItemName}
break
}
}
if (!(Test-Path "\\$Target\c$\scripts\unattendedupdate.ps1")){
if ($Script:Console){Write-Host " -- Deletion validated. Files no longer detected..." -ForegroundColor green}
}else{
if ($Script:Console){Write-Host " -- Deletion FAILED..." -ForegroundColor red}
}
try{
Copy-Item -Path $Script:ScriptFullPath -Destination "\\$Target\c$\scripts\" -Force -Confirm:$false
Copy-Item -Path ($PSScriptRoot+'\'+$Script:ScriptName.split('.')[0]+'.xml') -Destination "\\$Target\c$\scripts\" -Force -Confirm:$false
}catch{
if ($Script:Console){Write-Host " -- File copy to $Target FAILED..." -ForegroundColor Red}
if ($Script:Console){Write-Host " -- Error Message = "$_.Exception.Message}
if ($Script:Console){Write-Host " -- Error Item = "$_.Exception.ItemName}
break
}
if (Test-Path "\\$Target\c$\scripts\unattendedupdate.ps1"){
if ($Script:Console){Write-Host " -- Verified PS1 copy to $Target..." -ForegroundColor Green}
}
if (Test-Path "\\$Target\c$\scripts\unattendedupdate.xml"){
if ($Script:Console){Write-Host " -- Verified XML copy to $Target..." -ForegroundColor Green}
}
}
}else{
if ($Script:Console){Write-Host ' -- Bypassing local system'($Target.ToUpper()) -ForegroundColor Magenta}
}
}
if ($Script:Console){Write-Host `n"---
COMPLETED ---" -ForegroundColor Red }
break
}
#==[ End of
Functions / Start of Main Process
]===============================================
#--[ Load the
external config file ]--
LoadConfig
if ($Script:Deploy){DeployScript}
#--[ Add header
to html output file ]--
$Script:MessageBody = @()
$Script:MessageBody += '
<style Type="text/css">
table.myTable { border:5px solid black;border-collapse:collapse; }
table.myTable td { border:2px solid black;padding:5px}
table.myTable th { border:2px solid black;padding:5px;background: #949494 }
table.bottomBorder { border-collapse:collapse; }
table.bottomBorder td, table.bottomBorder th { border-bottom:1px dotted black;padding:5px; }
tr.noBorder td {border: 0; }
</style>'
$Script:MessageBody +=
'<table class="myTable">
<tr class="noBorder"><td colspan=5><center><h1>- ' +
$Script:eMailSubject + ' -</h1></td></tr>
<tr class="noBorder"><td colspan=5><center>The following report displays all recently installed patches on the system.</center></td></tr>
<tr class="noBorder"><td colspan=5></tr>
<tr class="noBorder"><td colspan=5>- script executed by: ' + $Script:UserContext.Name + '</tr>
<tr class="noBorder"><td colspan=5>- script version : ' +
$Script:ScriptVer + '</tr><br>
'
ScheduledTask "UpdateStatus"
ScheduledTask "UnattendedUpdate"
if ($Status){
if ($Script:Console){Write-Host "-
Collecting results..." -ForegroundColor Cyan }
$Script:MessageBody += '<tr
class="noBorder"><td colspan=5>- Collecting
results....</tr><br>'
GetResults
}elseif (($Script:Manual) -or ($Script:RunDays -Match $Today)){ #--[ Update routine is governed by the
week days noted in the config file ]--
if(Test-Path $PSScriptRoot\Results.csv){Remove-Item -path $PSScriptRoot\Results.csv -confirm:$false }
if ($Script:Console){Write-Host "- Running
update routine..." -ForegroundColor Cyan }
$Script:MessageBody = $Script:MessageBody + "- Running
update routine....<br>"
PatchIt
}else{
if ($Script:Console){Write-Host "`n--
Nothing scheduled for today --`n" -ForegroundColor Cyan }
}
if ($Script:Console){Write-Host "---
COMPLETED ---" -ForegroundColor Red }
<#
--[ Sample XML
config file. Should use the same name as
this script and be in the same folder. ]--
<!-- Settings
& configuration file -->
<Settings>
<General>
<ReportName>Patch
Processing</ReportName>
<PeerList>server1,server2,server3,server4</PeerList>
<RunDays>Monday,Wednesday,Friday</RunDays>
<RunTime>1am</RunTime>
</General>
<Email>
<from>UnattendedUpdate@domain.com</from>
<To>you@domain.com</To>
<Debug>you@domain.com</Debug>
<Subject>Automated Patch
Processing</Subject>
<HTML>$true</HTML>
<SmtpServer>10.10.10.1</SmtpServer>
</Email>
<Credentials>
<UserName>domain\serviceaccount</UserName>
<Password>76492d1ws656ertg116743a5345MgB8AHIAegB2AHUTGYT6ghjYAZQAxAGIATgBaAHwAYwAzADQANgA0AGEAMAAwADQAZgB7812q87hH087hnA2AGQAZAAQBmAGQAOAA0ADAHwAYwAzADQANgAEANgBiADAANwBkADEANAA4AGQAZgA3ADIAYQAwADYAZAA3AGUAZgBkAGYAZAA=</Password>
<Key>kdhCh7HCvLO+E6/AWnHbuTeJ7I78hnsdXN0IObie8mE=</Key>
</Credentials>
</Settings>
#>
No comments:
Post a Comment