Script to perform windows updates log it and then reports.
*** The built in scheduling isn't complete its from a prior version and placed into the script flow but its not tested.
################################################################################
#
# 7/25/16 Ver 3.0a
# needs a cleanup but finding time. works solid.
#for < V3 powershell compatibility
clear
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
###########################################
########## BEGIN CONFIGURATION ############
###########################################
####> General
#Mail relay server dns name or IP
$Mailrelay = "mailrelay.domain.com"
#Email Recipients
$EmailRecpt = @("test@test.com")
#Who will the mail be sent as?
$mailfrom = "WindowsUpdates@test.com"
#Custom School Title for non hosted schools
# Leave as "" for "Hosting: "
# Leave a space at the end to format the text right
$MailCustomTitle = "Client: "
#scriptpath location script is stored ( end it with a "\" default is ".\" which is local directory )
$ScriptPath = "C:\Program Files (x86)\scripts\updates\"
#Logpath ( end it with a "\" default is ".\" which is local directory )
$LogPath = "C:\Program Files (x86)\scripts\updates\logs\"
#Logs Archive Directory
$logsarchive = "C:\Program Files (x86)\scripts\updates\archive\"
#Log FILENAME stamp (must be unique each run )
$stamp = get-date -uFormat "%m_%d_%Y__%H_%M_%S"
####> Schedules
#SKIPSCHEDULES MODE -- !!!!! THIS IGNORES ANY SCHEDULES !!!! ITS A RUN ON DEMAND TRIGGER
$SKIPSCHEDULES = $true
#Local Schedule Setting, If Null and no remote file exist the script will just run.
#Example: $LocalSched = @(MILITARY TIME,MONTH, DAY)
#Example: $LocalSched = @("22:00" , "4", "14")
#$LocalSched = $NULL
$LocalSched = @("23:35", "3", "18")
# Turn on Nth Day Scheduling (Overides LocalSched Above if Nth Day set to TRUE)?
$NthDaySchedules = $true
#NTH DAY Schedule: Only install updates at 22:35 1st Sunday of the month (example)
#Time Military, Nth Day Valid 1 - 4, Days of week: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
$LocalSchedNthDay = @("20:16", "1", "Fri")
#Remote Schedule: Set to true to use remote schedule file, set to $false to use local ones
$useremotesched = $false
#Remote Schedule: InputFile Storage location ( Local Schedule Configuration Overrides Remote )
$RemoteSchedFile = "C:\Users\Administrator\Desktop\WS_Schedule.csv"
####> Install Options
#Are we going to actuall install the updates or simulate?
$Install = $true
#FORCE attempt to install from the internet
$InstallFromInternet = $true
#Install optional updates?
$Installoptional = $true
#Install Hidden Updates?
$InstallHidden = $true
#Accept all EULA presented?
$AcceptEULA = $true
#A list of bad updates to not install, comma delimited, leave zzzzzz's if nothing. default: @("zzzzzzz","Dynamic","CRM","SQL","Exchange")
$BADUPDATES = @("zzzzzzz","Dynamic","CRM","SQL","Exchange","KB2901983","SHAREPOINT")
#Reboot the host when done with a install?
$Reboot = $true
#POST SERVICES - registers a script to run after reboot and check / compare service states / run updates again etc
$PostCheck = $true
#Time to wait for system to spin up in seconds
$waittime = 300
#Post restart services? (attempts to restart services missing from the comparison)
$PostServicesStart = $true
#Run updates a second time if available? This will check for them again and if available install.
$PostSecondRestart = $true
###########################################
########## END CONFIGURATION ##############
###########################################
Write-host "Configuration Loaded.."
###########################################
########## BEGIN FUNCTIONS ##############
###########################################
Function DoErrorCheck ($MyErrors, $MyErrTitle, $MyErrDescript,$ErrLogPath){
$TErrstamp = mytime
$HadError = $false
if ($MyErrors.Count -gt 0) {
log_entry $MyErrTitle $MyErrDescript $ErrLogPath
log_entry $MyErrTitle ("Error Details:`r`n" + $error[0] + "`r`nERR Category: " + $error[0].CategoryInfo + "`r`nERR Exception: " + $error[0].Exception + "`r`nERR FullyQualifiedErrorId: " + $error[0].FullyQualifiedErrorId + "`r`nERR InvocationInfo: " + $Error[0].InvocationInfo + "`r`nERR PipelineIterationInfo: " + $Error[0].PipelineIterationInfo + "`r`nERR ScriptStackTrace: " + $Error[0].ScriptStackTrace + "`r`nERR TargetObject: " + $Error[0].TargetObject + "`r`nERR PSMessageDetails: " + $Error[0].PSMessageDetails + "`r`n") $ErrLogPath
do_mail ($MailCustomTitle + $MyMachine + " Error on processing Updates Section: " + $MyErrTitle) ("" + $MyErrDescript + "`r`nError on processing Updates: `r`n Log files location: " + $ErrLogPath + "`r`n Archive Log files location: " + $logsarchive + "`r`n Error Details:`r`n" + $error[0] + "`r`nERR Category: " + $error[0].CategoryInfo + "`r`nERR Exception: " + $error[0].Exception + "`r`nERR FullyQualifiedErrorId: " + $error[0].FullyQualifiedErrorId + "`r`nERR InvocationInfo: " + $Error[0].InvocationInfo + "`r`nERR PipelineIterationInfo: " + $Error[0].PipelineIterationInfo + "`r`nERR ScriptStackTrace: " + $Error[0].ScriptStackTrace + "`r`nERR TargetObject: " + $Error[0].TargetObject + "`r`nERR PSMessageDetails: " + $Error[0].PSMessageDetails + "`r`n") $Mailrelay $EmailRecpt
$error.clear()
$HadError = $true
}
return $HadError
}
function mytime {
return (Get-Date -uFormat "%H:%M:%S %Y_%m_%d")
}
function bad-update ( $myupdatetitle, $BADUPDATES ) {
$myreturn = $false
foreach ( $thisbadtitle in $BADUPDATES ) {
if ($myupdatetitle -like ("*" + $thisbadtitle + "*")) {
$myreturn = $true
log_entry "WINUPDATES" ("Bad Update Being Remove from list:" + $myupdatetitle) $LogPath
Write-Host ("Bad Update Being Remove from list:" + $myupdatetitle) -Fore Red
}
}
return $myreturn
}
function wait-until ($waittime, $waitmonth, $waitday) {
$canwego = $false
$TimeStart = Get-Date
$TimeEnd = get-date ( "" + $waittime + " " + $waitmonth + "/" + $waitday )
if ($TimeEnd -ge $TimeStart) { $canwego = $true }
if (((New-TimeSpan -start $timestart -End $timeend).TotalMinutes) -gt (-60) ){ $canwego = $true }
if (((New-TimeSpan -start $timestart -End $timeend).TotalMinutes) -lt (-60) ){ $canwego = $false }
Write-host ("Scheduled Start Time: " + $TimeEnd)
log_entry "WINUPDATES" ("Scheduled Start Time: " + $TimeEnd) $LogPath
$STime = New-TimeSpan -start $timestart -End $timeend
Write-Host ("Time until scheduled update -[ " + $STime.Hours + " Hours " + $STime.Days + " Days " + $STime.Minutes + " Minutes " + $STime.Seconds + " Seconds " + $STime.Milliseconds + " Milliseconds ] ")
log_entry "WINUPDATES" ("Time until scheduled update -[ " + $STime.Hours + " Hours " + $STime.Days + " Days " + $STime.Minutes + " Minutes " + $STime.Seconds + " Seconds " + $STime.Milliseconds + " Milliseconds ] ") $LogPath
$waitfirst = $true
if ($canwego){
Do {
$TimeNow = Get-Date
if ($TimeNow -ge $TimeEnd) {
Write-host ("It's time to start updating, Schedule met: " + (Get-Date))
log_entry "WINUPDATES" ("It's time to start updating, Schedule met: " + (Get-Date)) $LogPath
} else {
if ($waitfirst) {
log_entry "WINUPDATES" ("Waiting for scheduled start time: " + $TimeEnd) $LogPath
Write-Host "WSUpdates Script Started Time: $TimeStart" -ForegroundColor red
write-host "Waiting until schduled action Time: $TimeEnd" -ForegroundColor red
Write-Host "Waiting for scheduled start time." -ForegroundColor red
$waitfirst = $false
}
}
if ($TimeNow -lt $TimeEnd) {
Start-Sleep -Seconds 10
}
} Until ($TimeNow -ge $TimeEnd)
}#if not long after
else {
log_entry "WINUPDATES" ("Update time and update window has passed: " + $TimeEnd + " START: " + $TimeStart) $LogPath
Write-Host ("Update time and update window has passed: " + $TimeEnd + " START: " + $TimeStart) -ForegroundColor red
exit(15)
}
}
function checkadmin {
$admin = $true
$User = [Security.Principal.WindowsIdentity]::GetCurrent()
$Role = (New-Object Security.Principal.WindowsPrincipal $user).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
if(!$Role) { $admin=$false }
return $admin
}
function get-remoteschedule ( $mycurfilermt ) {
# Could do with a bunch of upgrades
# Doesn't do Nth Day yet
# Doesn't do sub install options yet
$thisinputdata = $null
if ((Test-Path -Path $mycurfilermt)) {
$thisincominginputdata = Import-Csv -Path $mycurfilermt -Delimiter "|"
# File setup: Hostname | Time | Month | Day | Install | Reboot | Exemptions | UseAdvancedSched
foreach ( $thisinputdata in $thisincominginputdata ) {
if (($thisinputdata -ne $null) -and ($thisinputdata.hostname -like (get-content env:computername))) {
$myrmtsched = @($thisinputdata.Time,$thisinputdata.Month,$thisinputdata.Day,$thisinputdata.Install,$thisinputdata.Reboot,$thisinputdata.Hostname,$thisinputdata.Exemptions,$thisinputdata.UseAdvancedSched)
log_entry "WINUPDATES" ("Found data in remote file: " + $thisinputdata.Time + ", " + $thisinputdata.Month + ", " + $thisinputdata.Day + ", " + $thisinputdata.Install + ", " + $thisinputdata.Reboot + ", " + $thisinputdata.Exemptions + ", " + $thisinputdata.UseAdvancedSched) $LogPath
Write-Host ("Found data in remote file: " + $thisinputdata.Time + ", " + $thisinputdata.Month + ", " + $thisinputdata.Day + ", " + $thisinputdata.Install + ", " + $thisinputdata.Reboot + ", " + $thisinputdata.Exemptions + ", " + $thisinputdata.UseAdvancedSched) -ForegroundColor Red
}
}
}
return $myrmtsched
}
function do_mail ($mysubject, $mybody, $myrelay, $myrecipients, $mailfrom, $LogPath) {#Send incoming emails requests
$sendRCPT = ""
$imail = 0
foreach ( $myrecp in $myrecipients ) {#build a email TO field list
if ($imail -eq 0) {#first time thru
$sendRCPT = $myrecp
} else {
$sendRCPT += "," + $myrecp
}
$imail += 1
}
$mymail = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($myrelay) #Dns name or IP of mail relay
$attach = new-object Net.Mail.Attachment($logpath)
$mymail.IsBodyHTML = $true
$mymail.From = $mailfrom
$mymail.To.Add($sendRCPT)
$mymail.Subject = $mysubject
$mymail.Body = $mybody
$mymail.Attachments.Add($attach)
$smtp.Send($mymail)
sleep 5
} #do_mail
function log_entry ($logwhat, $logstr, $logpathfile){#Logs incoming requests to file
#modified log function to deal with using a preexisting log $logpathfile
[string]$thisstr = "Now:" + (mytime) + " : Section: " + $logwhat + " : " + $logstr
$thisstr | Add-Content -path $logpathfile
} #End log entry
function FindSpecialDay ($MyNth, $MyDay) {
[datetime]$Today=[datetime]::NOW
$todayM=$Today.Month.ToString()
$todayY=$Today.Year.ToString()
[datetime]$StrtMonth=$todayM+'/1/'+$todayY
while ($StrtMonth.DayofWeek -ine $MyDay ) { $StrtMonth=$StrtMonth.AddDays(1) }
return ($StrtMonth.AddDays(7*($MyNth-1)))
}
function Get-ResultText ($myresult) {
$MyresTextWUA = $null
Switch ($myresult) {
0 { $MyresTextWUA = "Not Started" }
1 { $MyresTextWUA = "In Progress" }
2 { $MyresTextWUA = "Succeeded" }
3 { $MyresTextWUA = "Succeeded With Errors" }
4 { $MyresTextWUA = "Failed" }
5 { $MyresTextWUA = "Aborted" }
default { $MyresTextWUA = "An Unknown Error Code Was Encountered" }
}
return $MyresTextWUA
}
function Get-RebootResultText ($myresult) {
$MyresTextReboot = $null
Switch ($myresult) {
0 { $MyresTextReboot = "Never Reboots" }
1 { $MyresTextReboot = "Always Requires Reboot" }
2 { $MyresTextReboot = "Can Request Reboot" }
default { $MyresTextReboot = "An Unknown Reboot Required Code Was Encountered" }
}
return $MyresTextReboot
}
function setpostcheck {
# Always remember to force the addition of the job
$taskfull = ("C:\WINDOWS\system32\windowspowershell\v1.0\powershell.exe -ExecutionPolicy bypass -file '" + $ScriptPath + "WSUPDATES.ps1'")
schtasks /create /sc onstart /ru System /v1 /z /tn "WSUPDATES_ONSTART" /tr $taskfull /F
return
}
function setup_searcher_source ($mysearcher) {
$ThisServiceManager = New-Object -ComObject "Microsoft.Update.ServiceManager"
Write-host "Attempt to set source to Microsoft Update (internet)"
log_entry "WINUPDATES" "Attempt to set source to Microsoft Update (internet)" $LogPath
$serviceName = $null
$MyServiceID = $null
Foreach ($MyService in $ThisServiceManager.Services) {
If($MyService.Name -eq "Microsoft Update"){
$mysearcher.ServerSelection = 3
$mysearcher.ServiceID = $MyService.ServiceID
$MyServiceID = $MyService.Name
Break
}
}#Foreach Service
If(-not $MyServiceID) {
Foreach ($MyService in $ThisServiceManager.Services) {# Older windows SUS patch
If($MyService.Name -eq "Windows Update"){
$mysearcher.ServerSelection = 3
$mysearcher.ServiceID = $MyService.ServiceID
$MyServiceID = $MyService.Name
Break
}
}#Foreach Service
If(-not $MyServiceID) {
Write-error "Could not update from the internet"
log_entry "WINUPDATES" "Could not update from the internet" $LogPath
exit(404)
}
}#Enf If -not $serviceName
return $mysearcher
}
###########################################
########## END FUNCTIONS ##############
###########################################
clear
$error.clear()
$mymachine = get-content env:computername
#set variables
$Postrun = $false
$PostCheckTask = $PostCheck
$SkipDownload = $false
$PostRunTwo = $false
#check paths since they are important (check install)
if ($ScriptPath -eq $null) {#If scriptpath blank default to local dir
$scriptpath = "C:\Program Files (x86)\Scripts\Updates\"
}
if (!(Test-Path $ScriptPath)) {
New-Item -ItemType directory -Path $ScriptPath
}
#scriptpath
if ($logpath -eq $null) {#If logpath blank default to local dir
$LogPath = "C:\Program Files (x86)\Scripts\Updates\Logs\"
}
if (!(Test-Path $LogPath)) {
New-Item -ItemType directory -Path $LogPath
}
$logpathold = $LogPath
$LogPath = ($logpath + "WindowsUpdate" + $stamp + ".log")
#logpath
#Logs Archive Directory
if ($logsarchive -eq $null) {
$logsarchive = "C:\Program Files (x86)\scripts\updates\archive\"
}
if (!(Test-Path $logsarchive)) {
New-Item -ItemType directory -Path $logsarchive
}
#logarchive
if (($MailCustomTitle -eq $null) -or ($MailCustomTitle -eq "")) {#if blank assign hosting
$MailCustomTitle = "Hosting: "
}
# end variables
#clean start up files
Get-ChildItem ($scriptpath + "*.csv") | where-object {(new-timespan $_.LastWriteTime).Hours -ge 8} | del -Verbose -filter "*.csv" -Force -WhatIf
#check for rights
if (!(checkadmin)) {
Write-Error "Your are not running the windows update script as administrator, the script will fail to perform any actions."
log_entry "WINUPDATES" "Your are not running the windows update script as administrator, the script will fail to perform any actions." ("Dumping service(s) status ") $LogPath
sleep -Seconds 20
}
#POST check if we ran before
if (Test-Path ($ScriptPath + "SERVICES_STAT.csv")) {
$Postrun = $true
$PostCheckTask = $false
if ($PostSecondRestart) {
if (!(Test-Path ($ScriptPath + "SECOND_STAT.csv"))) {
"PASS 2" | Out-File -FilePath ($ScriptPath + "SECOND_STAT.csv")
$PostCheckTask = $true
$Postrun = $false
$waittime = 1
$PostRunTwo = $true
}
}
#remove the task
schtasks /DELETE /TN WSUPDATES_ONSTART /F
#set last logfile
$LogPath = (Get-ChildItem $LogPathold | where { $_.name -like "*WindowsUpdate*" } | Sort-Object -Property LastWriteTime -Descending)[0].fullname
write-host "Post run starting.. "
log_entry "WINUPDATES" "Post run starting.. " $LogPath
#no schedule check since we are already running
$SKIPSCHEDULES = $true
#Sleep and wait for the machine to start up
sleep -Seconds $waittime
}
######################################
##### SCHEDULING CODE
######################################
#Only run ANY scheduler if SKIPSCHEDULES mode is !! NOT !! enabled
if (!$SKIPSCHEDULES) {
$MySched = $null
#Remote Schedule checking / Handling
if ($useremotesched) {
$MyRemoteSchedStuff = get-remoteschedule $RemoteSchedFile
if (($MyRemoteSchedStuff -ne " ") -or ($MyRemoteSchedStuff -ne $null)) {
$MySched = $MyRemoteSchedStuff
if ($MyRemoteSchedStuff[3] -eq "true") {
$Install = $true
} else { $Install = $false }
if ($MyRemoteSchedStuff[4] -eq "true") {
$Reboot = $true
} else { $Reboot = $false }
} else {
$useremotesched = $false
}
}
#Check for local schedules and fill out the time
if (($LocalSched -ne $null) -or ($NthDaySchedules)) {
if ($LocalSchedNthDay -ge 1) {
$MySched = $LocalSchedNthDay
$MySched[2] = (FindSpecialDay $LocalSchedNthDay[1] $LocalSchedNthDay[2]).Day
$MySched[1] = (FindSpecialDay $LocalSchedNthDay[1] $LocalSchedNthDay[2]).Month
} elseif ($LocalSched -ne $null) {
$MySched = $LocalSched
}
}
#check for defined scheduled start time
if ((($LocalSched -ne $null) -or ($NthDaySchedules) -or ($useremotesched -eq $true)) -and ($MySched -ne $null)) {
log_entry "WINUPDATES" ("Found Schedule Evaluating .. ") $LogPath
wait-until $MySched[0] $MySched[1] $MySched[2]
} else {#No Schedule
exit(15)
}
#Build exemptions from remote file if exists
if ($useremotesched) {
if ($MyRemoteSchedStuff[6] -ne $null) {
foreach ( $nestfield in $MyRemoteSchedStuff[6].split(",") ) {
if (( ($nestfield.trim()) -ne " " ) -and ( ($nestfield.trim()) -ne $null) -and (!(($nestfield.length) -le 0))){
$BADUPDATES += $nestfield
}
}
}
} # Schedules
}#SKIPSCHEDULES MODE CHECK
######################################
##### INFORMATIONAL CODE
######################################
if (!($Postrun)){# Dump current services information for later reference
Write-Host ("")
Write-Host ("Dumping service(s) status " + " | " + (mytime))
log_entry "WINUPDATES" ("Dumping service(s) status ") $LogPath
$SRV_RUNNING = Get-Service | select Name, DisplayName, Status | Sort-Object -Property Name | where { $_.status -like "*RUNN*" }
if (!(Test-Path ($ScriptPath + "SERVICES_STAT.csv"))) { $SRV_RUNNING | export-csv -Path ($ScriptPath + "SERVICES_STAT.csv" ) }
$SRV_RUNNING_TEXT = $SRV_RUNNING | ConvertTo-Html
$AUTO_NOSTART = Get-wmiobject win32_service -Filter "startmode = 'auto' AND state != 'running' AND Exitcode !=0 " -ComputerName localhost | select name, startname, exitcode
$AUTO_NOSTART_TEXT = $AUTO_NOSTART | ConvertTo-Html
}
If (($Postrun) -or ($PostRunTwo)) {# Post run check services against the prior state
Write-Host ("")
Write-Host ("Dumping service(s) status for post check : " + " | " + (mytime))
log_entry "WINUPDATESPOST" ("Dumping service(s) status for post check : ") $logpath
$SRV_RUNNING = Get-Service | select Name, DisplayName, Status | Sort-Object -Property Name | where { $_.status -like "*RUNN*" }
#$SRV_RUNNING | export-csv -Path ($ScriptPath + "SERVICES_AFTER_STAT.csv" )
$SRV_RUNNING_TEXT = $SRV_RUNNING | ConvertTo-Html
$SRV_RUNNING_B4_TEXT = $SRV_RUNNING_B4 | ConvertTo-Html
$SRV_RUNNING_B4 = import-csv ($ScriptPath + "SERVICES_STAT.csv" )
$SRV_RUNNING_B4_TEXT = $SRV_RUNNING_B4 | ConvertTo-Html
#Compare them Pull any Differences
$DIFF_RUNNING = Compare-Object $SRV_RUNNING $SRV_RUNNING_B4 -PassThru -Property Name | where { $_.SideIndicator -eq "=>" } | select Name,DisplayName,Status | sort-object -Descending
$DIFF_RUNNING_TEXT = $DIFF_RUNNING | ConvertTo-Html
$AUTO_NOSTART = Get-wmiobject win32_service -Filter "startmode = 'auto' AND state != 'running' AND Exitcode !=0 " -ComputerName localhost | select name, startname, exitcode
$AUTO_NOSTART_TEXT = $AUTO_NOSTART | ConvertTo-Html
#Attempt to restart down services
if ($PostServicesStart){
foreach ($Bringup in $AUTO_NOSTART) {
$bupres = $null
write-host ("Attempting to start down service: " + $Bringup.name)
log_entry "WINUPDATESPOST" ("Attempting to start down service: " + $Bringup.name) $LogPath
Start-Service -Name $Bringup.name
$bupres = Get-Service $Bringup.name
write-host ( $bupres.name + " service status now: " + $bupres.Status )
log_entry "WINUPDATESPOST" ( $bupres.name + " service status now: " + $bupres.Status ) $LogPath
}
}
}
#Update session
$error.Clear()
#Start a new windows update session
$UpdateSession = New-Object -Com Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
If($InstallFromInternet) { # Set the source to the internet
$UpdateSearcher = setup_searcher_source $UpdateSearcher
} else {
Write-host "Source set to Windows Update (Local)"
log_entry "WINUPDATES" "Source set to Windows Update (Local)" $LogPath
}
#search for updates
Write-Host ("Searching for applicable updates for machine: " + $MailCustomTitle + $MyMachine + "..." + " | " + (mytime) ) -Fore Green
log_entry "WINUPDATES" ("Searching for applicable updates for machine: " + $MailCustomTitle + $MyMachine + "..." ) $LogPath
$UpdatesToInstall = New-Object -Com Microsoft.Update.UpdateColl
#Search for the updates, This part can be highly customized is we can find a break down and time
######################################
##### SEARCH CODE
######################################
####> Notes
#AutoSelectOnWebSites
#EulaAccepted
#IsDownloaded
#IsHidden
#IsInstalled
#IsMandatory
#RebootRequired
$MyQuery = "IsInstalled=0 and Type='Software'"
if (!$Installoptional) { $MyQuery += " and AutoSelectOnWebSites=1"}
if (!$InstallHidden) { $MyQuery += " and IsHidden=0"}
$MyUpdatesSearch = $UpdateSearcher.Search($MyQuery)
Write-Host ("")
Write-Host ("List of applicable updates on the machine:") -Fore Green
log_entry "WINUPDATES" "List of applicable updates on the machine:" $LogPath
#Filter Updates list and make download list minus bad updates also list And Log the actual updates downloading
For ($X = 0; $X -lt $MyUpdatesSearch.Updates.Count; $X++){
$Update = $MyUpdatesSearch.Updates.Item($X)
if (( Bad-Update $Update.Title $BADUPDATES ) -eq $false){
log_entry "WINUPDATES" ("Found update " + ( ($X + 1).ToString() + " Adding: " + $Update.Title) ) $LogPath
Write-Host ("Found updates " + ( ($X + 1).ToString() + " Adding: " + $Update.Title) + " | " + (mytime))
if (!($update.EulaAccepted) -and ($AcceptEULA)) {
$Update.AcceptEula()
} else {
if (!($update.EulaAccepted) -and !($AcceptEULA)) {
continue
}
}
$Null = $UpdatesToInstall.Add($Update)
}
}
#If no updates left exit ( but only if not in post restart )
if ($UpdatesToInstall.Count -le 0) {
if ($UpdatesToInstall.Count -eq 0) {
log_entry "WINUPDATES" "There are no applicable updates for DOWNLOAD." $LogPath
Write-Host("There are no applicable updates for DOWNLOAD.")
}
if (!($PostSecondRestart)) {
#Cleanup
del -Path ($ScriptPath + "SERVICES_STAT.csv") -Force
do_mail ("" + $MailCustomTitle + $MyMachine +" Windows Update Script Completed: " + " | " + (mytime)) ("" + $MailCustomTitle + $MyMachine +" has no applicable updates for DOWNLOAD.") $Mailrelay $EmailRecpt $mailfrom $LogPath
Exit
}
if ($PostSecondRestart) {
$Postrun = $true
$SkipDownload = $true
}
}
######################################
##### DOWNLOAD CODE
######################################
if (!($SkipDownload)){
Write-Host ("")
Write-Host ("Downloading Updates..." + " | " + (mytime)) -Fore Green
log_entry "WINUPDATES" ("Downloading Updates...") $LogPath
$Downloader = $UpdateSession.CreateUpdateDownloader()
$Downloader.Updates = $UpdatesToInstall
$error.clear()
#Download them
$Null = $Downloader.Download()
$Null = DoErrorCheck $Error ("DOWNLOAD_WINUPDATES on server:" + $MailCustomTitle + $MyMachine) ("!!!! ERROR !!!! When attempting to download updates on machine " + $MailCustomTitle + $MyMachine) $LogPath
log_entry "WINUPDATES" ("Downloading Updates Completed") $LogPath
Write-Host ("Downloading Updates Completed" + " | " + (mytime)) -Fore Green
}
######################################
##### INSTALL CODE
######################################
If (!($Postrun)) {
If ($Install){
#register scheduled job to check/compare services after
if ($PostCheckTask) {
Write-Host("Setting system post restart verification script: " + " | " + (mytime))
log_entry "WINUPDATES" ("Setting system post restart verification script: ") $LogPath
setpostcheck
}
Write-Host ("")
Write-Host ("Installing Updates...") -Fore Green
log_entry "WINUPDATES" ("Installing Updates...") $LogPath
$InstallThese = $UpdateSession.CreateUpdateInstaller()
$InstallThese.Updates = $UpdatesToInstall
#Force no popups anywhere
$InstallThese.ForceQuiet = $true
$error.clear()
#Actual INSTALL
$InstallationResult = $InstallThese.Install()
$NULL = DoErrorCheck $Error ("INSTALL_WINUPDATES on server:" + $MailCustomTitle + $MyMachine) ("!!!! ERROR !!!! When attempting to install updates on machine " + $MailCustomTitle + $MyMachine) $LogPath
log_entry "WINUPDATES" ("Installing Updates Completed") $LogPath
Write-Host ("Installing Updates Completed" + " | " + (mytime)) -Fore Green
Write-Host ("")
Write-Host ("List of Updates Installed with Results:") -Fore Green
log_entry "WINUPDATES" "List of Updates Installed with Results:" $LogPath
# List And Log the installed updates results
$MyInstallResult = @()
For ($X = 0; $X -lt $UpdatesToInstall.Count; $X++){
Write-Host($UpdatesToInstall.Item($X).Title + ": " + (get-resulttext (($InstallationResult.GetUpdateResult($X).ResultCode))) + " | Reboot Required: " + $UpdatesToInstall.Item($X).RebootRequired)
log_entry "WINUPDATES" ($UpdatesToInstall.Item($X).Title + ": " + (get-resulttext (($InstallationResult.GetUpdateResult($X).ResultCode))) + " | Reboot Required: " + $UpdatesToInstall.Item($X).RebootRequired) $LogPath
$MyInstallResult += $UpdatesToInstall.Item($X)
}
#$MyInstallResult | Export-Csv -Path ($ScriptPath + "INSTALL_STAT.csv" )
# Handle output and formating of results
Write-Host ("")
$MyInstallResText = (get-resulttext ($InstallationResult.ResultCode))
$MyInstallRebootText = $InstallationResult.RebootRequired
Write-Host ("Installation Result: " + $MyInstallResText)
log_entry "WINUPDATES" ("Installation Result: " + $MyInstallResText) $LogPath
Write-Host (" Reboot Required: " + $MyInstallRebootText)
log_entry "WINUPDATES" (" Reboot Required: " + $MyInstallRebootText) $LogPath
log_entry "WINUPDATES" ("Windows Update Script Completed: ") $LogPath
#Display error in Title if one occured
if (( Get-Content -Raw ($logpath + "WindowsUpdate" + $stamp + ".log") | Select-String -SimpleMatch "Error" ).count -ge 1) {
$MailCustomTitle = ("!!ERROR!! " + $MailCustomTitle)
}
If ($Reboot){
Write-Host("")
Write-Host("Rebooting...") -Fore Green
log_entry "WINUPDATES" "Rebooting System ..." $LogPath
if (!$PostCheckTask){
do_mail ("" + $MailCustomTitle + $MyMachine +" Windows Update Script Completed: " + $MyInstallResText + " | " + (mytime)) ("" + $MailCustomTitle + $MyMachine +" has completed updates result: " + $MyInstallResText + ", please see attached log. <BR><BR> List of all current running services: <BR> " + $SRV_RUNNING_TEXT + " <BR> ") $Mailrelay $EmailRecpt $mailfrom $LogPath
}
sleep -Seconds 10
shutdown /r /t 1
} else {
Write-Host (" Installation completed reboot was NOT ENABLED ") -Fore Green
log_entry "WINUPDATES" (" Installation completed reboot was NOT ENABLED ") $LogPath
if (!$PostCheckTask){
do_mail ("" + $MailCustomTitle + $MyMachine +" Windows Update Script Completed: " + $MyInstallResText + " | " + (mytime)) ("" + $MailCustomTitle + $MyMachine +" has completed updates result: " + $MyInstallResText + ", please see attached log. <BR><BR> List of all current running services: <BR> " + $SRV_RUNNING_TEXT + " <BR> ") $Mailrelay $EmailRecpt $mailfrom $LogPath
}
}
}#install
if (!$install) { # Don't install just simulate
#Cleanup
if (test-path ($ScriptPath + "SERVICES_STAT.csv")) { del -Path ($ScriptPath + "SERVICES_STAT.csv") -Force }
Write-Host (" Simulated run with download only completed ") -Fore Green
log_entry "WINUPDATES" (" Simulated run with download only completed ") $LogPath
do_mail ("" + $MailCustomTitle + $MyMachine +" Windows Update Script SIMULATED RUN Completed: " + " | " + (mytime)) ("" + $MailCustomTitle + $MyMachine +" has completed a Simulated run with download only completed. <BR><BR> List of all current running services: <BR> " + $SRV_RUNNING_TEXT + " <BR> ") $Mailrelay $EmailRecpt $mailfrom $LogPath
}
}#not post
if ($Postrun) {
#Cleanup
if (test-path ($ScriptPath + "SERVICES_STAT.csv")) { del -Path ($ScriptPath + "SERVICES_STAT.csv") -Force }
if (test-path ($ScriptPath + "SECOND_STAT.csv")) {del -Path ($ScriptPath + "SECOND_STAT.csv") -Force }
#Display error in Title if one occured
if (( Get-Content -Raw $logpath | Select-String -SimpleMatch "Error" ).count -ge 1) {
#Error Occured during run
$MailCustomTitle = ("!!ERROR!! " + $MailCustomTitle)
}
Write-Host ("Creating email and sending report : " + " | " + (mytime))
log_entry "WINUPDATESPOST" ("Creating email and sending report : ") $logpath
$thisbody = ("" + $MailCustomTitle + $MyMachine +" has completed updates, please see attached log. <BR><BR> List of any services not running when compared to prepatch: <BR> " + $DIFF_RUNNING_TEXT + " <BR><BR> List of only AUTOMATIC services not started with ERRORS : <BR> " + $AUTO_NOSTART_TEXT + "<BR><BR> List of all current running services: <BR> " + $SRV_RUNNING_TEXT + " <BR><BR> List of any services running before patch: <BR> " + $SRV_RUNNING_B4_TEXT + " <BR> ")
do_mail ("" + $MailCustomTitle + $MyMachine +" Windows Update Script Completed: " + " | " + (mytime)) $thisbody $Mailrelay $EmailRecpt $mailfrom $logpath
}#post