Skip to content

Instantly share code, notes, and snippets.

@cable729
Created June 7, 2012 01:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cable729/2886029 to your computer and use it in GitHub Desktop.
Save cable729/2886029 to your computer and use it in GitHub Desktop.
$Global:VerbosePreference = 'silentlycontinue'
$Global:DebugPreference = 'silentlycontinue'
#Ensure that we are running the GUI from the correct location
Set-Location $(Split-Path $MyInvocation.MyCommand.Path)
$Global:Path = $(Split-Path $MyInvocation.MyCommand.Path)
Write-Debug "Current location: $Path"
#Check for PSExec
Write-Verbose "Checking for psexec.exe"
If (-Not (Test-Path psexec.exe)) {
Write-Warning ("Psexec.exe missing from {0}!`n Please place file in the path so UI can work properly" -f (Split-Path $MyInvocation.MyCommand.Path))
Break
}
#Determine if this instance of PowerShell can run WPF
Write-Verbose "Checking the apartment state"
If ($host.Runspace.ApartmentState -ne "STA") {
Write-Warning "This script must be run in PowerShell started using -STA switch!`nScript will attempt to open PowerShell in STA and run re-run script."
Start-Process -File PowerShell.exe -Argument "-STA -noprofile -WindowStyle hidden -file $($myinvocation.mycommand.definition)"
Break
}
#Validate user is an Administrator
Write-Verbose "Checking Administrator credentials"
If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(`
[Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Warning "You are not running this as an Administrator!`nRe-running script and will prompt for administrator credentials."
Start-Process -Verb "Runas" -File PowerShell.exe -Argument "-STA -noprofile -file $($myinvocation.mycommand.definition)"
Break
}
#Stop and remove any jobs currently running
Write-Verbose "Removing PS jobs"
Get-Job | Remove-Job -Force -ea silentlycontinue
#Load Required Assemblies
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase
Add-Type –assemblyName Microsoft.VisualBasic
Add-Type –assemblyName System.Windows.Forms
#DotSource Help script
. ".\HelpFiles\HelpOverview.ps1"
#DotSource About script
. ".\HelpFiles\About.ps1"
Function Set-PoshPAIGOption {
If (Test-Path (Join-Path $Path 'options.xml')) {
$Optionshash = Import-Clixml -Path (Join-Path $Path 'options.xml')
$Global:maxConcurrentJobs = $Optionshash['MaxJobs']
$Global:MaxRebootJobs = $Optionshash['MaxRebootJobs']
If ($Optionshash['ReportPath']) {
$Global:reportpath = $Optionshash['ReportPath']
} Else {
$Optionshash['ReportPath'] = $Global:reportpath = (Join-Path $Home 'Desktop')
}
} Else {
#Default Options
$optionshash = @{
MaxJobs = 20
MaxRebootJobs = 5
ReportPath = (Join-Path $Home 'Desktop')
}
}
$optionshash | Export-Clixml -Path (Join-Path $pwd 'options.xml') -Force
}
#Function for Debug output
Function Global:Show-DebugState {
Write-Debug ("Number of Items: {0}" -f $Global:Listview.ItemsSource.count)
Write-Debug ("First Item: {0}" -f $Global:Listview.ItemsSource[0].Computer)
Write-Debug ("Last Item: {0}" -f $Global:Listview.ItemsSource[$($Global:Listview.ItemsSource.count) -1].Computer)
Write-Debug ("Max Progress Bar: {0}" -f $Global:ProgressBar.Maximum)
}
Function Global:Start-JobCleanup {
[Float]$Global:ProgressBar.Value = $Global:ProgressBar.Maximum
$End = New-Timespan $Start (Get-Date)
$Global:StatusTextBox.Text = "Completed in: {0}" -f $end
$Global:Runbutton.IsEnabled = $True
$StartImage.Source = "$pwd\Images\Start.jpg"
$Global:Cancelbutton.IsEnabled = $False
$CancelImage.Source = "$pwd\Images\Stop_locked.jpg"
}
#Reboot Warning Message
Function Show-RebootWarning {
$title = "Reboot Server Warning"
$message = "You are about to reboot servers which can affect the environment! `nAre you sure you want to do this?"
$button = [System.Windows.Forms.MessageBoxButtons]::YesNo
$icon = [Windows.Forms.MessageBoxIcon]::Warning
[windows.forms.messagebox]::Show($message,$title,$button,$icon)
}
Function Get-Error {
Process {
ForEach ($err in $error) {
Switch ($err) {
{$err -is [System.Management.Automation.ErrorRecord]} {
$hash = @{
Category = $err.categoryinfo.Category
Activity = $err.categoryinfo.Activity
Reason = $err.categoryinfo.Reason
Type = $err.GetType().ToString()
Exception = ($err.exception -split ": ")[1]
QualifiedError = $err.FullyQualifiedErrorId
CharacterNumber = $err.InvocationInfo.OffsetInLine
LineNumber = $err.InvocationInfo.ScriptLineNumber
Line = $err.InvocationInfo.Line
TargetObject = $err.TargetObject
}
}
Default {
$hash = @{
Category = $err.errorrecord.categoryinfo.category
Activity = $err.errorrecord.categoryinfo.Activity
Reason = $err.errorrecord.categoryinfo.Reason
Type = $err.GetType().ToString()
Exception = ($err.errorrecord.exception -split ": ")[1]
QualifiedError = $err.errorrecord.FullyQualifiedErrorId
CharacterNumber = $err.errorrecord.InvocationInfo.OffsetInLine
LineNumber = $err.errorrecord.InvocationInfo.ScriptLineNumber
Line = $err.errorrecord.InvocationInfo.Line
TargetObject = $err.errorrecord.TargetObject
}
}
}
$object = New-Object PSObject -Property $hash
$object.PSTypeNames.Insert(0,'ErrorInformation')
$object
}
}
}
Function Add-Server {
$server = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a server name or names. Separate servers with a comma (,).", "Add Server/s")
If (-Not [System.String]::IsNullOrEmpty($server)) {
If ($server -match ",") {
[array]$servers = $server -split ","
ForEach ($server in $servers) {
If (-NOT [System.String]::IsNullOrEmpty($server)) {
$dr = $DataTable.NewRow()
#Add Data To Row
$dr.Computer = $server
$dr.Audited = 0
$dr.Installed = 0
$dr.InstallErrors = 0
$dr.Services = 0
$dr.Notes = $Null
#Add Row To Data Table
$DataTable.Rows.Add($dr)
$Global:Listview.DataContext = $DataTable
$b = new-object System.Windows.Data.Binding
$b.source = $DataTable
[void]$Global:Listview.SetBinding([System.Windows.Controls.ListView]::ItemsSourceProperty,$b)
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
Show-DebugState
}
}
} Else {
$dr = $DataTable.NewRow()
#Add Data To Row
$dr.Computer = $server
$dr.Audited = 0
$dr.Installed = 0
$dr.InstallErrors = 0
$dr.Services = 0
$dr.Notes = $Null
#Add Row To Data Table
$DataTable.Rows.Add($dr)
$Global:Listview.DataContext = $DataTable
$b = new-object System.Windows.Data.Binding
$b.source = $DataTable
[void]$Global:Listview.SetBinding([System.Windows.Controls.ListView]::ItemsSourceProperty,$b)
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
Show-DebugState
}
}
}
Function Remove-Server {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
ForEach ($server in $servers) {
$Global:Listview.DataContext.Rows.Find($Server).Delete()
}
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
Show-DebugState
}
#Report Generation function
Function Start-Report {
Write-Debug ("Data: {0}" -f $ReportComboBox.SelectedItem.Text)
Switch ($ReportComboBox.SelectedItem.Text) {
"Audit CSV Report" {
If ($AuditPatchReport.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path $reportpath "AuditReport.csv"
$AuditPatchReport | Export-Csv $savedreport -NoTypeInformation
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Audit UI Report" {
If ($AuditPatchReport.count -gt 0) {
$AuditPatchReport | Out-GridView -Title 'Audit Report'
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Install CSV Report" {
If ($InstallPatchReport.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path $reportpath "InstallReport.csv"
$InstallPatchReport | Export-Csv $savedreport -NoTypeInformation
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Install UI Report" {
If ($InstallPatchReport.count -gt 0) {
$InstallPatchReport | Out-GridView -Title 'Install Report'
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Host File List" {
If ($Global:Listview.Items.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path $reportpath "hosts.txt"
$Listview.DataContext | Select -Expand Computer | Out-File $savedreport
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Computer List Report" {
If ($Global:Listview.Items.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path (Join-Path $home Desktop) "serverlist.csv"
$Listview.DataContext | Export-Csv -NoTypeInformation $savedreport
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Error UI Report" {Get-Error | Out-GridView -Title 'Error Report'}
"Services UI Report" {
If (@($ServicesReport).count -gt 0) {
$ServicesReport | Out-GridView -Title 'Services Report'
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
"Services CSV Report" {
If (@($ServicesReport).count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path $reportpath "ServicesReport.csv"
$ServicesReport | Export-Csv $savedreport -NoTypeInformation
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
}
}
}
#start-RunJob function
Function Start-RunJob {
Write-Debug ("ComboBox {0}" -f $RunOptionComboBox.Text)
If ($Global:Listview.SelectedItems.Count -gt 0) {
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
#Install Patches
If ($RunOptionComboBox.Text -eq 'Install Patches') {
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Installing Patches for all servers...Please Wait"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.SelectedItems | Select -Expand Computer) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
InstallJobFromQueue
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
InstallJobFromQueue
}
}
$server = $Null
} ElseIf ($RunOptionComboBox.Text -eq 'Audit Patches') {
#Audit Patches
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Auditing Patches for all servers...Please Wait"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
Write-Verbose "Creating collection queue"
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.ItemsSource | Select -Expand Computer) {
Write-Verbose "Adding $item to queue"
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.count
for( $i = 0; $i -le $queuecount; $i++ ) {
AuditJobFromQueue
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
AuditJobFromQueue
}
}
} ElseIf ($RunOptionComboBox.Text -eq 'Reboot Systems') {
#Reboot
If ((Show-RebootWarning) -eq "Yes") {
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Rebooting Servers..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
Write-Verbose "Creating collection queue"
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.SelectedItems | Select -Expand Computer) {
Write-Verbose "Adding $item to queue"
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $MaxRebootJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
RebootServerFromQueue
}
} Else {
for( $i = 0; $i -lt $MaxRebootJobs; $i++ ) {
RebootServerFromQueue
}
}
}
} ElseIf ($RunOptionComboBox.Text -eq 'Ping Sweep') {
#Ping
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Checking server connection..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
Write-Verbose "Creating collection queue"
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.SelectedItems | Select -Expand Computer) {
Write-Verbose "Adding $item to queue"
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
PingJobFromQueue
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
PingJobFromQueue
}
}
} ElseIf ($RunOptionComboBox.Text -eq 'Check Pending Reboot') {
#Check Pending Reboot
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Checking for servers with a pending reboot..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.SelectedItems | Select -Expand Computer) {
Write-Verbose "Adding $item to queue"
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
PendingRebootJobQueue
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
PendingRebootJobQueue
}
}
} ElseIf ($RunOptionComboBox.Text -eq 'Services Check') {
#Check Services
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Auditing Service Status on all servers...Please Wait"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
Write-Verbose "Creating collection queue"
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Global:Listview.SelectedItems | Select -Expand Computer) {
Write-Verbose "Adding $item to queue"
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.count
for( $i = 0; $i -le $queuecount; $i++ ) {
Get-NonRunningServicesJob
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
Get-NonRunningServicesJob
}
}
}
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No server/s selected!"
}
}
#Function to check for Non-Running services set to Automatic
Function Global:Get-NonRunningServicesJob {
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Checking for non running services"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Set-Location $location
Try {
@(Get-WmiObject -ComputerName $Server -Class Win32_Service -Filter "StartMode='Auto' AND State!='Running'" -ErrorAction Stop)
} Catch {
New-Object PSObject -Property @{
Error = $_.Exception.Message
}
}
} -ArgumentList $server,$path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
$servicedata = Receive-Job -Job $eventsubscriber.sourceobject
If (-Not $servicedata.Error) {
$servicesdata = $servicedata |
Select @{L='Server';E={$_.__Server}},Name,DisplayName,StartMode,State,ExitCode,StartName
}
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
#Update Service column
Write-Verbose ("Checking count of services")
[int]$count = ($ServiceData | Measure-Object -ErrorAction SilentlyContinue | Select -Expand Count)
Write-Debug ("Servicecount: {0}" -f $Count)
If ($ServiceData.Error) {
Write-Verbose ("{0}: Error returned on service check" -f $serverupdate)
$Listview.DataContext.Rows.Find($serverupdate).Services = 0
$Listview.DataContext.Rows.Find($serverupdate).Notes = $ServiceData.Error
} Else {
Write-Verbose ("{0}: Found {1} Non running services" -f $serverupdate,$Count)
$Listview.DataContext.Rows.Find($serverupdate).Services = $Count
$Listview.DataContext.Rows.Find($serverupdate).Notes = 'Completed'
Write-Verbose ("Completed update of data rows")
If ($Count -gt 0) {
Write-Verbose ("Adding collection of services to global report")
[array]$Global:ServicesReport += $ServiceData | Where {$_.Server}
} Else {
Write-Verbose ("Nothing to report on")
}
}
Write-Verbose ("Checking to see if we need to continue running the jobs")
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running Get-NonRunningServicesJob"
Get-NonRunningServicesJob
} ElseIf (-NOT (Get-Job)) {
Write-Verbose ("Running global job cleanup")
Start-JobCleanup
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to check for pending patch reboot
Function Global:PendingRebootJobQueue {
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Checking for pending reboot"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Set-Location $location
. .\Scripts\Get-ComputerRebootState.ps1
Get-ComputerRebootState -Computer $server
} -ArgumentList $server,$path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
$status = Receive-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
Switch ($status.RebootRequired) {
$True {$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Reboot Required"}
$False {$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "OK"}
"NA" {$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Error"}
"Offline" {$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Offline"}
}
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running PendingRebootJobQueue"
PendingRebootJobQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to Reboot Servers
Function Global:RebootServerFromQueue {
Param ()
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Rebooting"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
$i=0
If (Test-Connection -Computer $server -count 1 -Quiet) {
Try {
Restart-Computer -ComputerName $server -Force -ea stop
Do {
Start-Sleep -Seconds 2
Write-Verbose "Waiting for $server to shutdown..."
}
While ((Test-Connection -ComputerName $server -Count 1 -Quiet))
Do {
Start-Sleep -Seconds 5
$i++
Write-Verbose "$server down...$($i)"
If($i -eq 60) {
Write-Warning "$server did not come back online from reboot!"
Return $False
}
}
While (-NOT(Test-Connection -ComputerName $server -Count 1 -Quiet))
Write-Verbose "$Server is back up"
Return $True
} Catch {
Write-Warning "$($Error[0])"
Return $False
}
}
} -ArgumentList $server,$path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
$results = Receive-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Updating Patch Report"
Write-Verbose "Removing: $($eventsubscriber.sourceobject)"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering: $($eventsubscriber.SourceIdentifier)"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing: $($eventsubscriber.SourceIdentifier)"
Remove-Job -Name $eventsubscriber.SourceIdentifier
If ($results) {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Online"
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Offline"
}
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running RebootServerFromQueue"
RebootServerFromQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to gather Windows Update Log
Function Global:GetUpdateLogFromQueue {
Param ($last)
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Retrieving UpdateLog"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location,$Last)
Set-Location $location
. .\Scripts\Get-UpdateLog.ps1
If ($Last) {
Get-UpdateLog -Computername $server -Last $last
} Else {
Get-UpdateLog -Computername $server
}
} -ArgumentList $server,$path,$last
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
$results = Receive-Job -Job $eventsubscriber.sourceobject |
Select Computer,Date,Time,Type,Message
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Updating Patch Report"
Write-Verbose "Removing: $($eventsubscriber.sourceobject)"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering: $($eventsubscriber.SourceIdentifier)"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing: $($eventsubscriber.SourceIdentifier)"
Remove-Job -Name $eventsubscriber.SourceIdentifier
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running GetUpdateLogFromQueue"
GetUpdateLogFromQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
$results | Out-GridView -Title "Windows Update Log"
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to install patches in background
Function Global:InstallJobFromQueue {
If ($queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Installing Patches"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Set-Location $location
. .\Scripts\Install-Patches.ps1
Install-Patches -Computername $server
} -ArgumentList $server,$Global:path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke("Render", $Global:updatelayout, $null, $null)
[Array]$Global:InstallData = Receive-Job -Job $eventsubscriber.sourceobject |
Select Computer,Title,KB,IsDownloaded,Notes
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
If ($InstallData[0].Title -eq "NA") {
Write-Verbose "No updates to install"
$Global:Listview.DataContext.Rows.Find($serverupdate).Installed = 0
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
} Else {
[array]$good = $InstallData | Where {$_.Notes -ne "Failed to Install Patch"}
If ($good.count -lt 1) {
$Global:Listview.DataContext.Rows.Find($serverupdate).Installed = 0
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Installed = $good.Count
}
$Global:Listview.DataContext.Rows.Find($serverupdate).InstallErrors = ($InstallData.Count - $good.Count)
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
}
Write-Verbose "Updating Patch Report"
[array]$Global:InstallPatchReport += $Global:InstallData
Write-Verbose "Removing: $($eventsubscriber.sourceobject)"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering: $($eventsubscriber.SourceIdentifier)"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing: $($eventsubscriber.SourceIdentifier)"
Remove-Job -Name $eventsubscriber.SourceIdentifier
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running InstallJobFromQueue"
InstallJobFromQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to force Wuauclt Action of update client
Function Global:Invoke-WuaucltAction {
[cmdletbinding()]
Param (
[string]$ServiceAction
)
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = ("Performing Action: {0}" -f $ServiceAction)
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location,$ServiceAction)
Switch ($Action) {
"DetectNow" {
Try {
If ((Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "wuauclt /detectnow" -ErrorAction Stop).ReturnValue -eq 0) {
$Result = 'Success'
} Else {
$Result = 'Failed'
}
} Catch {
$Result = 'Failed'
}
}
"ResetAuthorization" {
Try {
If ((Invoke-WmiMethod -Class Win32_Process -Name Create -ArgumentList "wuauclt /resetauthorization" -ErrorAction Stop).ReturnValue -eq 0) {
$Result = 'Success'
} Else {
$Result = 'Failed'
}
} Catch {
$Result = 'Failed'
}
}
}
Write-Output $Result
} -ArgumentList $server,$Global:path,$ServiceAction
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
Write-Verbose "Kicking off event action"
Write-Debug "Server: $($eventsubscriber.sourceobject.name)"
$result = Receive-Job -Job $eventsubscriber.sourceobject
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
If ($result = 'Success') {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = ("Completed Action: {0}" -f $ServiceAction)
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = ("Unable to Complete Action: {0}" -f $ServiceAction)
}
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
Write-Debug ("Queue: {0}" -f $queue.count)
Write-Debug ("Jobs: {0}" -f @(Get-Job).Count)
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running Invoke-WuaucltAction"
Invoke-WuaucltAction -ServiceAction $ServiceAction
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
Write-Verbose "Invoke-WuaucltAction started"
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to Invoke a service action against client wsus service
Function Global:Invoke-WSUSServiceAction {
[cmdletbinding()]
Param (
[string]$ServiceAction
)
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = ("Performing Action: {0} WUAUSERVER service" -f $ServiceAction)
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location,$ServiceAction)
$Service = Get-Service -ComputerName $Server -Name wuauserv
Switch ($ServiceAction) {
"Stop" {
Try {
Stop-Service -InputObject $Service -ErrorAction Stop
$result = 'Success'
} Catch {
$result = 'Fail'
}
}
"Start" {
Try {
Start-Service -InputObject $Service -ErrorAction Stop
$result = 'Success'
} Catch {
$result = 'Fail'
}
}
"Restart" {
Try {
Restart-Service -InputObject $Service -ErrorAction Stop
$result = 'Success'
} Catch {
$result = 'Fail'
}
}
}
Write-Output $result
} -ArgumentList $server,$Global:path,$ServiceAction
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
Write-Verbose "Kicking off event action"
Write-Debug"Server: $($eventsubscriber.sourceobject.name)"
$result = Receive-Job -Job $eventsubscriber.sourceobject
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
If ($result = 'Success') {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = ("Completed Service {0}" -f $ServiceAction)
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = ("Unable to {0} service" -f $ServiceAction)
}
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running Invoke-WSUSServiceAction"
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
Remove-Variable -Scope Global -Name Action
}
Write-Verbose "Invoke-WSUSServiceAction started"
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to Gather all installed patches on servers
Function Global:ListInstalledUpdates {
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Retrieving Installed Patches"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Get-HotFix -ComputerName $server | Where {$_.Description -ne ""}
} -ArgumentList $server,$Global:path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
[Array]$Global:InstalledPatches += Receive-Job -Job $eventsubscriber.sourceobject |
Select @{L="Computer";E={$serverupdate}},Description,@{L="KB";E={$_.HotFixID}},InstalledBy,InstalledOn
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running ListInstalledUpdates"
ListInstalledUpdates
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
$Global:InstalledPatches | Out-GridView -Title "Installed Patches"
}
Write-Verbose "ListInstalledUpdates started"
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
#Function to check network connection to servers
Function Global:PingJobFromQueue {
Write-Debug "Queue Count: $($queue.count)"
if( $queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Checking connection"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Test-Connection -ComputerName $server -Count 1 -Quiet
} -ArgumentList $server,$Global:path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
$status = Receive-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
If ($status) {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Online"
} ElseIf (-Not $status) {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Offline"
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Unknown"
}
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running PingJobFromQueue"
PingJobFromQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
Write-Verbose "PingJobFromQueue started"
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
# Function to Audit patches in background
Function Global:AuditJobFromQueue {
Write-Debug "Queue Count: $($queue.count)"
if ($queue.Count -gt 0) {
$server = $queue.Dequeue()
$Global:Listview.DataContext.Rows.Find($server).Notes = "Auditing Patches"
$j = Start-Job -Name $server -ScriptBlock {
param($server,$location)
Set-Location $location
. .\Scripts\Get-PendingUpdates.ps1
Get-PendingUpdates -Computer $server
} -ArgumentList $server,$Global:path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
#Declare value for server to be updated in grid
$serverupdate = $eventsubscriber.sourceobject.name
$Global:ProgressBar.Value++
$Global:Window.Dispatcher.Invoke( "Render", $Global:updatelayout, $null, $null)
[Array]$Global:AuditData = Receive-Job -Job $eventsubscriber.sourceobject |
Select Computer,Title,KB,IsDownloaded,Notes
Write-Verbose "Updating: $($eventsubscriber.sourceobject.name)"
If ($Global:AuditData[0].Title -eq "NA") {
$Global:Listview.DataContext.Rows.Find($serverupdate).Audited = 0
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
} ElseIf ($Global:AuditData[0].Title -eq "ERROR") {
$Global:Listview.DataContext.Rows.Find($serverupdate).Audited = 0
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Error with Audit"
} ElseIf ($Global:AuditData[0].Title -eq "OFFLINE") {
$Global:Listview.DataContext.Rows.Find($serverupdate).Audited = 0
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Offline"
} Else {
$Global:Listview.DataContext.Rows.Find($serverupdate).Audited = $Global:AuditData.Count
$Global:Listview.DataContext.Rows.Find($serverupdate).Notes = "Completed"
}
[array]$Global:AuditPatchReport += $Global:AuditData
Write-Verbose "Removing Event Job"
Remove-Job -Job $eventsubscriber.sourceobject
Write-Verbose "Unregistering Event"
Unregister-Event $eventsubscriber.SourceIdentifier
Write-Verbose "Removing background Job"
Remove-Job -Name $eventsubscriber.SourceIdentifier
If ($queue.count -gt 0 -OR (Get-Job)) {
Write-Verbose "Running AuditJob"
AuditJobFromQueue
} ElseIf (-NOT (Get-Job)) {
Start-JobCleanup
}
Write-Verbose "AuditJob started"
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
# Function to Query Active Directory for Servers
Function ActiveDirectoryJobFromQueue {
if( $queue.Count -gt 0)
{
$domain = $queue.Dequeue()
Write-Verbose "Starting job for Active Directory against Domain: $domain"
$j = Start-Job -Name ActiveDirectoryQuery -ScriptBlock {
Param($domain,$location)
Write-Debug "Location: $location"
Set-Location $location
$strCategory = "computer"
$strOS = "Windows*Server*"
$objSearcher = [adsisearcher]""
$objSearcher.SearchRoot= [adsi]"LDAP://$domain"
$objSearcher.Filter = ("(&(objectCategory=$strCategory)(OperatingSystem=$strOS))")
[void]$objSearcher.PropertiesToLoad.Add('name')
Write-Verbose "Checking for exempt list"
If (Test-Path Exempt.txt) {
Write-Verbose "Collecting systems from exempt list"
[array]$exempt = Get-Content Exempt.txt
}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults) {
[string]$Computer = $objResult.Properties.name
If ($Exempt -notcontains $Computer) {
Write-Output $Computer
} Else {
Write-Verbose "Excluding $computer"
}
}
} -ArgumentList $domain,$Path
Register-ObjectEvent -InputObject $j -EventName StateChanged -Action {
[Array]$Global:Data = Receive-Job -Job $eventsubscriber.sourceobject -Keep | Sort
If (-Not([string]::IsNullOrEmpty($data))) {
$data | ForEach {
$dr = $DataTable.NewRow()
#Add Data To Row
$dr.Computer = $_
$dr.Audited = 0
$dr.Installed = 0
$dr.InstallErrors = 0
$dr.Services = 0
#Add Row To Data Table
$DataTable.Rows.Add($dr)
}
$Global:Listview.DataContext = $DataTable
$b = new-object System.Windows.Data.Binding
$b.source = $DataTable
[void]$Global:Listview.SetBinding([System.Windows.Controls.ListView]::ItemsSourceProperty,$b)
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
Show-DebugState
Remove-Job -Job $eventsubscriber.sourceobject
Unregister-Event $eventsubscriber.SourceIdentifier
Remove-Job -Name $eventsubscriber.SourceIdentifier
If (-NOT(Get-Job)) {
Start-JobCleanup
} Else {
ActiveDirectoryJobFromQueue
}
}
} | Out-Null
Write-Verbose "Created Event for $($J.Name)"
}
}
Function Open-FileDialog {
$dlg = new-object microsoft.win32.OpenFileDialog
$dlg.DefaultExt = "*.txt"
$dlg.Filter = "Text Files |*.txt;*.log"
$dlg.InitialDirectory = $path
[void]$dlg.showdialog()
Write-Output $dlg.FileName
}
Function Reset-DataTable {
#New Data Table
$Global:DataTable = New-Object System.Data.DataTable
$DataTable.TableName = 'WSUSClientTable'
#Create Columns with Names and Types
$colComp = New-Object System.Data.DataColumn Computer,([string])
$colAudit = New-Object System.Data.DataColumn Audited, ([int])
$colInstall = New-Object System.Data.DataColumn Installed, ([int])
$colInstallError = New-Object System.Data.DataColumn InstallErrors, ([int])
$colServices = New-Object System.Data.DataColumn Services, ([int])
$colNotes = New-Object System.Data.DataColumn Notes, ([string])
#Add Columns into Data Table
$DataTable.Columns.Add($colComp)
$DataTable.Columns.Add($colAudit)
$DataTable.Columns.Add($colInstall)
$DataTable.Columns.Add($colInstallError)
$DataTable.Columns.Add($colServices)
$DataTable.Columns.Add($colNotes)
#Set Primary Key for Computer
$DataTable.PrimaryKey = @($DataTable.Columns[0])
}
Function Open-DomainDialog {
$domain = [Microsoft.VisualBasic.Interaction]::InputBox("Enter the LDAP path for the Domain or press OK to use the default domain.",
"Domain Query", "$(([adsisearcher]'').SearchRoot.distinguishedName)")
If (-Not [string]::IsNullOrEmpty($domain)) {
Write-Output $domain
}
}
#Build the GUI
[xml]$xaml = @"
<Window
xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Name='Window' Title='PowerShell Patch/Audit Utility' WindowStartupLocation = 'CenterScreen'
Width = '865' Height = '575' ShowInTaskbar = 'True'>
<Window.Background>
<LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
<LinearGradientBrush.GradientStops> <GradientStop Color='#C4CBD8' Offset='0' /> <GradientStop Color='#E6EAF5' Offset='0.2' />
<GradientStop Color='#CFD7E2' Offset='0.9' /> <GradientStop Color='#C4CBD8' Offset='1' /> </LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Window.Background>
<Grid x:Name = 'Grid' ShowGridLines = 'false'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = '*'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
</Grid.RowDefinitions>
<Menu Width = 'Auto' HorizontalAlignment = 'Stretch' Grid.Row = '0'>
<Menu.Background>
<LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
<LinearGradientBrush.GradientStops> <GradientStop Color='#C4CBD8' Offset='0' /> <GradientStop Color='#E6EAF5' Offset='0.2' />
<GradientStop Color='#CFD7E2' Offset='0.9' /> <GradientStop Color='#C4CBD8' Offset='1' /> </LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Menu.Background>
<MenuItem x:Name = 'FileMenu' Header = '_File'>
<MenuItem x:Name = 'RunMenu' Header = '_Run' ToolTip = 'Initiate Run operation' InputGestureText ='F5'> </MenuItem>
<MenuItem x:Name = 'GenerateReportMenu' Header = 'Generate R_eport' ToolTip = 'Generate Report' InputGestureText ='F8'> </MenuItem>
<Separator />
<MenuItem x:Name = 'OptionMenu' Header = '_Options' ToolTip = 'Open up options window.' InputGestureText ='Ctrl+O'> </MenuItem>
<Separator />
<MenuItem x:Name = 'ExitMenu' Header = 'E_xit' ToolTip = 'Exits the utility.' InputGestureText ='Ctrl+E'> </MenuItem>
</MenuItem>
<MenuItem x:Name = 'EditMenu' Header = '_Edit'>
<MenuItem x:Name = 'SelectAllMenu' Header = 'Select _All' ToolTip = 'Selects all rows.' InputGestureText ='Ctrl+A'> </MenuItem>
<Separator />
<MenuItem x:Name = 'ClearErrorMenu' Header = 'Clear ErrorLog' ToolTip = 'Clears error log.'> </MenuItem>
<MenuItem x:Name = 'ClearAllMenu' Header = 'Clear All' ToolTip = 'Clears everything on the WSUS utility.'> </MenuItem>
</MenuItem>
<MenuItem x:Name = 'ActionMenu' Header = '_Action'>
<MenuItem Header = 'Reports'>
<MenuItem x:Name = 'ClearAuditReportMenu' Header = 'Clear Audit Report'
ToolTip = 'Clears the current report.'> </MenuItem>
<MenuItem x:Name = 'ClearInstallReportMenu' Header = 'Clear Install Report'
ToolTip = 'Clears the current report.'> </MenuItem>
<MenuItem x:Name = 'ClearInstalledUpdateMenu' Header = 'Clear Installed Update Report'
ToolTip = 'Clears the installed update report.'> </MenuItem>
</MenuItem>
<MenuItem Header = 'Server List'>
<MenuItem x:Name = 'ClearServerListMenu' Header = 'Clear Server List'
ToolTip = 'Clears the server list.'> </MenuItem>
<MenuItem x:Name = 'ClearServerListNotesMenu' Header = 'Clear Server List Notes'
ToolTip = 'Clears the server list notes column.'> </MenuItem>
<MenuItem x:Name = 'OfflineHostsMenu' Header = 'Remove Offline Servers'
ToolTip = 'Removes all offline hosts from Server List'> </MenuItem>
<MenuItem x:Name = 'ResetDataMenu' Header = 'Reset Computer List Data'
ToolTip = 'Resets the audit and patch data on Server List'> </MenuItem>
</MenuItem>
<Separator />
<MenuItem x:Name = 'HostListMenu' Header = 'Create Host List'
ToolTip = 'Creates a list of all servers and saves to a text file.'> </MenuItem>
<MenuItem x:Name = 'ServerListReportMenu' Header = 'Create Server List Report'
ToolTip = 'Creates a CSV file listing the current Server List.'> </MenuItem>
<Separator/>
<MenuItem x:Name = 'ViewErrorMenu' Header = 'View ErrorLog' ToolTip = 'Clears error log.'> </MenuItem>
</MenuItem>
<MenuItem x:Name = 'HelpMenu' Header = '_Help'>
<MenuItem x:Name = 'AboutMenu' Header = '_About' ToolTip = 'Show the current version and other information.'> </MenuItem>
<MenuItem x:Name = 'HelpFileMenu' Header = 'WSUS Utility _Help'
ToolTip = 'Displays a help file to use the WSUS Utility.' InputGestureText ='F1'> </MenuItem>
</MenuItem>
</Menu>
<ToolBarTray Grid.Row = '1' Grid.Column = '0'>
<ToolBarTray.Background>
<LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
<LinearGradientBrush.GradientStops> <GradientStop Color='#C4CBD8' Offset='0' /> <GradientStop Color='#E6EAF5' Offset='0.2' />
<GradientStop Color='#CFD7E2' Offset='0.9' /> <GradientStop Color='#C4CBD8' Offset='1' /> </LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</ToolBarTray.Background>
<ToolBar Background = 'Transparent' Band = '1' BandIndex = '1'>
<Button x:Name = 'RunButton' Width = 'Auto' ToolTip = 'Performs action against all servers in the server list based on checked radio button.'>
<Image x:Name = 'StartImage' Source = '$Pwd\Images\Start.jpg'/>
</Button>
<Separator Background = 'Black'/>
<Button x:Name = 'CancelButton' Width = 'Auto' ToolTip = 'Cancels currently running operations.' IsEnabled = 'False'>
<Image x:Name = 'CancelImage' Source = '$pwd\Images\Stop_locked.jpg' />
</Button>
<Separator Background = 'Black'/>
<ComboBox x:Name = 'RunOptionComboBox' Width = 'Auto' IsReadOnly = 'True'
SelectedIndex = '0'>
<TextBlock> Audit Patches </TextBlock>
<TextBlock> Install Patches </TextBlock>
<TextBlock> Check Pending Reboot </TextBlock>
<TextBlock> Ping Sweep </TextBlock>
<TextBlock> Services Check </TextBlock>
<TextBlock> Reboot Systems </TextBlock>
</ComboBox>
</ToolBar>
<ToolBar Background = 'Transparent' Band = '1' BandIndex = '1'>
<Button x:Name = 'GenerateReportButton' Width = 'Auto' ToolTip = 'Generates a report based on user selection.'>
<Image Source = '$pwd\Images\Gen_Report.gif' />
</Button>
<ComboBox x:Name = 'ReportComboBox' Width = 'Auto' IsReadOnly = 'True'
SelectedIndex = '0'>
<TextBlock> Audit CSV Report </TextBlock>
<TextBlock> Audit UI Report </TextBlock>
<TextBlock> Install CSV Report </TextBlock>
<TextBlock> Install UI Report </TextBlock>
<TextBlock> Services CSV Report </TextBlock>
<TextBlock> Services UI Report </TextBlock>
<TextBlock> Host File List </TextBlock>
<TextBlock> Computer List Report </TextBlock>
<TextBlock> Error UI Report </TextBlock>
</ComboBox>
<Separator Background = 'Black'/>
</ToolBar>
<ToolBar Background = 'Transparent' Band = '1' BandIndex = '1'>
<Button x:Name = 'BrowseFileButton' Width = 'Auto'
ToolTip = 'Open a file dialog to select a host file. Upon selection, the contents will be loaded into Server list.'>
<Image Source = '$pwd\Images\BrowseFile.gif' />
</Button>
<Button x:Name = 'LoadADButton' Width = 'Auto'
ToolTip = 'Creates a list of computers from Active Directory to use in Server List.'>
<Image Source = '$pwd\Images\ActiveDirectory.gif' />
</Button>
<Separator Background = 'Black'/>
</ToolBar>
</ToolBarTray>
<Grid Grid.Row = '2' Grid.Column = '0' ShowGridLines = 'false'>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = '*'/>
<RowDefinition Height = '*'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
<RowDefinition Height = 'Auto'/>
</Grid.RowDefinitions>
<GroupBox Header = "Computer List" Grid.Column = '0' Grid.Row = '2' Grid.ColumnSpan = '11' Grid.RowSpan = '3'>
<Grid Width = 'Auto' Height = 'Auto' ShowGridLines = 'false'>
<ListView x:Name = 'Listview' AllowDrop = 'True'
ToolTip = 'Server List that displays all information regarding statuses of servers and patches.'>
<ListView.View>
<GridView x:Name = 'GridView' AllowsColumnReorder = 'True'>
<GridViewColumn x:Name = 'ComputerColumn' Width = '110' DisplayMemberBinding = '{Binding Path = Computer}'>
<GridViewColumnHeader x:Name = 'ComputerColumnHeader' Content = 'Computer' />
</GridViewColumn>
<GridViewColumn x:Name = 'AuditedColumn' Width = '110' DisplayMemberBinding = '{Binding Path = Audited}'>
<GridViewColumnHeader x:Name = 'AuditedColumnHeader' Content = 'Audited' />
</GridViewColumn>
<GridViewColumn x:Name = 'InstalledColumn' Width = '110' DisplayMemberBinding = '{Binding Path = Installed}'>
<GridViewColumnHeader x:Name = 'InstalledColumnHeader' Content = 'Installed' />
</GridViewColumn>
<GridViewColumn x:Name = 'InstallErrorColumn' Width = '110' DisplayMemberBinding = '{Binding Path = InstallErrors}'>
<GridViewColumnHeader x:Name = 'InstallErrorsColumnHeader' Content = 'InstallErrors' />
</GridViewColumn>
<GridViewColumn x:Name = 'ServicesColumn' Width = '115' DisplayMemberBinding = '{Binding Path = Services}'>
<GridViewColumnHeader x:Name = 'ServicesColumnHeader' Content = 'NonRunningServices' />
</GridViewColumn>
<GridViewColumn x:Name = 'NotesColumn' Width = '275' DisplayMemberBinding = '{Binding Path = Notes}'>
<GridViewColumnHeader x:Name = 'NotesColumnHeader' Content = 'Notes' />
</GridViewColumn>
</GridView>
</ListView.View>
<ListView.ContextMenu>
<ContextMenu x:Name = 'ListViewContextMenu'>
<MenuItem x:Name = 'AddServerMenu' Header = 'Add Server' InputGestureText ='Ctrl+S'> </MenuItem>
<MenuItem x:Name = 'RemoveServerMenu' Header = 'Remove Server' InputGestureText ='Ctrl+D'> </MenuItem>
<Separator />
<MenuItem x:Name = 'WindowsUpdateServiceMenu' Header = 'Windows Update Service' >
<MenuItem x:Name = 'WUStopServiceMenu' Header = 'Stop Service' > </MenuItem>
<MenuItem x:Name = 'WUStartServiceMenu' Header = 'Start Service' > </MenuItem>
<MenuItem x:Name = 'WURestartServiceMenu' Header = 'Restart Service' > </MenuItem>
</MenuItem>
<MenuItem x:Name = 'WindowsUpdateLogMenu' Header = 'WindowsUpdateLog' >
<MenuItem x:Name = 'EntireLogMenu' Header = 'View Entire Log' > </MenuItem>
<MenuItem x:Name = 'Last25LogMenu' Header = 'View Last 25' > </MenuItem>
<MenuItem x:Name = 'Last50LogMenu' Header = 'View Last 50' > </MenuItem>
<MenuItem x:Name = 'Last100LogMenu' Header = 'View Last 100' > </MenuItem>
</MenuItem>
<MenuItem x:Name = 'WUAUCLTMenu' Header = 'WUAUCLT' >
<MenuItem x:Name = 'DetectNowMenu' Header = 'Run Detect Now' > </MenuItem>
<MenuItem x:Name = 'ResetAuthorizationMenu' Header = 'Run Reset Authorization' > </MenuItem>
</MenuItem>
<MenuItem x:Name = 'InstalledUpdatesMenu' Header = 'Installed Updates' >
<MenuItem x:Name = 'GUIInstalledUpdatesMenu' Header = 'View Installed Updates' > </MenuItem>
</MenuItem>
</ContextMenu>
</ListView.ContextMenu>
</ListView>
</Grid>
</GroupBox>
</Grid>
<ProgressBar x:Name = 'ProgressBar' Grid.Row = '3' Height = '20' ToolTip = 'Displays progress of current action via a graphical progress bar.'> </ProgressBar>
<TextBox x:Name = 'StatusTextBox' Grid.Row = '4' ToolTip = 'Displays current status of operation'> Waiting for Action... </TextBox>
</Grid>
</Window>
"@
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Global:Window=[Windows.Markup.XamlReader]::Load( $reader )
#Connect to all controls
$GenerateReportMenu = $Global:Window.FindName("GenerateReportMenu")
$ClearAuditReportMenu = $Global:Window.FindName("ClearAuditReportMenu")
$ClearInstallReportMenu = $Global:Window.FindName("ClearInstallReportMenu")
$SelectAllMenu = $Global:Window.FindName("SelectAllMenu")
$OptionMenu = $Global:Window.FindName("OptionMenu")
$WUStopServiceMenu = $Global:Window.FindName("WUStopServiceMenu")
$WUStartServiceMenu = $Global:Window.FindName("WUStartServiceMenu")
$WURestartServiceMenu = $Global:Window.FindName("WURestartServiceMenu")
$WindowsUpdateServiceMenu = $Global:Window.FindName("WindowsUpdateServiceMenu")
$GenerateReportButton = $Global:Window.FindName("GenerateReportButton")
$Global:ReportComboBox = $Global:Window.FindName("ReportComboBox")
$StartImage = $Global:Window.FindName("StartImage")
$CancelImage = $Global:Window.FindName("CancelImage")
$RunOptionComboBox = $Global:Window.FindName("RunOptionComboBox")
$ClearErrorMenu = $Global:Window.FindName("ClearErrorMenu")
$ViewErrorMenu = $Global:Window.FindName("ViewErrorMenu")
$EntireLogMenu = $Global:Window.FindName("EntireLogMenu")
$Last25LogMenu = $Global:Window.FindName("Last25LogMenu")
$Last50LogMenu = $Global:Window.FindName("Last50LogMenu")
$Last100LogMenu = $Global:Window.FindName("Last100LogMenu")
$ResetDataMenu = $Global:Window.FindName("ResetDataMenu")
$ResetAuthorizationMenu = $Global:Window.FindName("ResetAuthorizationMenu")
$ClearServerListNotesMenu = $Global:Window.FindName("ClearServerListNotesMenu")
$ServerListReportMenu = $Global:Window.FindName("ServerListReportMenu")
$OfflineHostsMenu = $Global:Window.FindName("OfflineHostsMenu")
$HostListMenu = $Global:Window.FindName("HostListMenu")
$InstalledUpdatesMenu = $Global:Window.FindName("InstalledUpdatesMenu")
$DetectNowMenu = $Global:Window.FindName("DetectNowMenu")
$WindowsUpdateLogMenu = $Global:Window.FindName("WindowsUpdateLogMenu")
$WUAUCLTMenu = $Global:Window.FindName("WUAUCLTMenu")
$GUIInstalledUpdatesMenu = $Global:Window.FindName("GUIInstalledUpdatesMenu")
$NotesColumnHeader = $Global:Window.FindName("NotesColumnHeader")
$InstallErrorsColumnHeader = $Global:Window.FindName("InstallErrorsColumnHeader")
$InstalledColumnHeader = $Global:Window.FindName("InstalledColumnHeader")
$AuditedColumnHeader = $Global:Window.FindName("AuditedColumnHeader")
$ComputerColumnHeader = $Global:Window.FindName("ComputerColumnHeader")
$AddServerMenu = $Global:Window.FindName("AddServerMenu")
$RemoveServerMenu = $Global:Window.FindName("RemoveServerMenu")
$Global:ListviewContextMenu = $Global:Window.FindName("ListViewContextMenu")
$ExitMenu = $Global:Window.FindName("ExitMenu")
$ClearInstalledUpdateMenu = $Global:Window.FindName("ClearInstalledUpdateMenu")
$RunMenu = $Window.FindName('RunMenu')
$ClearAllMenu = $Global:Window.FindName("ClearAllMenu")
$ClearServerListMenu = $Global:Window.FindName("ClearServerListMenu")
$AboutMenu = $Global:Window.FindName("AboutMenu")
$HelpFileMenu = $Global:Window.FindName("HelpFileMenu")
$Global:Listview = $Global:Window.FindName("Listview")
$LoadFileButton = $Global:Window.FindName("LoadFileButton")
$BrowseFileButton = $Global:Window.FindName("BrowseFileButton")
$LoadADButton = $Global:Window.FindName("LoadADButton")
$Global:StatusTextBox = $Global:Window.FindName("StatusTextBox")
$Global:ProgressBar = $Global:Window.FindName("ProgressBar")
$Global:RunButton = $Global:Window.FindName("RunButton")
$Global:CancelButton = $Global:Window.FindName("CancelButton")
$GridView = $Global:Window.FindName("GridView")
##Define Event Handlers
#Cancel Button Event
$CancelButton.Add_Click({
Write-Verbose "Unregistering Events"
Get-EventSubscriber | Unregister-Event -Force -ErrorAction silentlycontinue
Write-Verbose "Cancelling all jobs"
Get-Job | Remove-Job -Force -ErrorAction silentlycontinue
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Action cancelled"
[Float]$Global:ProgressBar.Value = 0
$runbutton.IsEnabled = $True
$StartImage.Source = "$pwd\Images\Start.jpg"
$Cancelbutton.IsEnabled = $False
$CancelImage.Source = "$pwd\Images\Stop_locked.jpg"
})
#EntireUpdateLog Event
$EntireLogMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Retrieving Windows Update log from Server..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
GetUpdateLogFromQueue
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
GetUpdateLogFromQueue
}
}
}
})
#Last100UpdateLog Event
$Last100LogMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Retrieving Windows Update log from Server..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $queue.count; $i++ ) {
GetUpdateLogFromQueue -last 100
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
GetUpdateLogFromQueue -last 100
}
}
}
})
#Last50UpdateLog Event
$Last50LogMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Retrieving Windows Update log from Server..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $queue.count; $i++ ) {
GetUpdateLogFromQueue -last 50
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
GetUpdateLogFromQueue -last 50
}
}
}
})
#Last25UpdateLog Event
$Last25LogMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Retrieving Windows Update log from Server..."
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $queue.count; $i++ ) {
GetUpdateLogFromQueue -last 25
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
GetUpdateLogFromQueue -last 25
}
}
}
})
#Offline server removal
$OfflineHostsMenu.Add_Click({
Write-Verbose "Removing any server that is shown as offline"
$Offline = $Listview.DataContext.rows | Where {$_.Notes -eq "Offline"}
$Offline | ForEach {
Write-Verbose "Removing $($_.Computer)"
$_.Delete()
}
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
})
#ResetAuthorization Event
$ResetAuthorizationMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Forcing Reset Authorization on Servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
#Set global variable for Starting service
$Global:ServiceAction = 'ResetAuthorization'
Write-Debug ("ServiceAction: {0}" -f $ServiceAction)
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $queue.count; $i++ ) {
Invoke-WuaucltAction -ServiceAction $ServiceAction
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
Invoke-WuaucltAction -ServiceAction $ServiceAction
}
}
}
})
#DetectNow Event
$DetectNowMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Forcing Detection on Servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
#Set global variable for Starting service
$Global:ServiceAction = 'DetectNow'
Write-Debug ("ServiceAction: {0}" -f $ServiceAction)
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers)
{
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
for( $i = 0; $i -le $queue.count; $i++ ) {
Invoke-WuaucltAction -ServiceAction $ServiceAction
}
} Else {
for( $i = 0; $i -lt $maxConcurrentJobs; $i++ ) {
Invoke-WuaucltAction -ServiceAction $ServiceAction
}
}
}
})
#Window Load Events
$Window.Add_Loaded({
#Configure Options
Write-Verbose 'Updating configuration based on options'
Set-PoshPAIGOption
Write-Debug ("maxConcurrentJobs: {0}" -f $maxConcurrentJobs)
Write-Debug ("MaxRebootJobs: {0}" -f $MaxRebootJobs)
Write-Debug ("reportpath: {0}" -f $reportpath)
##Configure a timer to refresh window##
#Create Timer object
Write-Verbose "Creating timer object"
$Global:timer = new-object System.Windows.Threading.DispatcherTimer
#Fire off every 5 seconds
Write-Verbose "Adding 5 second interval to timer object"
$timer.Interval = [TimeSpan]"0:0:5.00"
#Add event per tick
Write-Verbose "Adding Tick Event to timer object"
$timer.Add_Tick({
[Windows.Input.InputEventHandler]{ $Global:Window.UpdateLayout() }
#Write-Verbose "Updating Window"
})
#Start timer
Write-Verbose "Starting Timer"
$timer.Start()
If (-NOT $timer.IsEnabled) {
$Window.Close()
}
###Configure Event Handlers###
##Sort Columns by Column Headers##
#Define hashtable of settings
$SortHash = @{}
Write-Verbose "Configuring Event handlers"
[System.Windows.RoutedEventHandler]$ColumnSortHandler = {
If ($_.OriginalSource -AND $_.OriginalSource.Role -ne 'Padding') {
$Column = $_.Originalsource.Column.DisplayMemberBinding.Path.Path
Write-Debug ("Sort: {0}" -f $Column)
If ($SortHash[$Column] -eq 'Ascending') {
Write-Debug "Descending"
$SortHash[$Column] = 'Descending'
} Else {
Write-Debug "Ascending"
$SortHash[$Column] = 'Ascending'
}
Write-Debug ("Direction: {0}" -f $SortHash[$Column])
$lastColumnsort = $Column
#$view = [system.Windows.Data.CollectionViewSource]::GetDefaultView($listview.ItemsSource)
Write-Verbose "Clearing sort descriptions"
$Listview.Items.SortDescriptions.clear()
Write-Verbose ("Sorting {0} by {1}" -f $Column, $SortHash[$Column])
$Listview.Items.SortDescriptions.Add((New-Object System.ComponentModel.SortDescription $Column, $SortHash[$Column]))
Write-Verbose "Refreshing View"
$Listview.Items.Refresh()
}
}
$ListView.AddHandler([System.Windows.Controls.GridViewColumnHeader]::ClickEvent, $ColumnSortHandler)
})
#Window Events
$Global:Window.Add_Closed({
#Stop and remove any jobs currently running
Write-Verbose "Removing PS jobs"
Get-Job | Remove-Job -Force -ea silentlycontinue
Remove-Variable AuditPatchReport -force -scope Global -ea 0
Remove-Variable InstallPatchReport -force -scope Global -ea 0
Remove-Variable content -force -scope Global -ea 0
Remove-Variable StatusTextBox -Scope Global -ea 0
Remove-Variable ProgressBar -Scope Global -ea 0
Remove-Variable ListView -scope Global -ea 0
Remove-Variable FileTextBox -ea 0
Remove-Variable RunButton -scope Global -ea 0
Remove-Variable ListViewContextMenu -scope Global -ea 0
Remove-Variable Window -scope Global -ea 0
Remove-Variable InstalledPatches -scope Global -force -ea 0
Remove-Variable ServicesReport -scope Global -force -ea 0
$timer.Stop()
})
#Rightclick Event
$Global:Listview.Add_MouseRightButtonUp({
Write-Debug "$($This.SelectedItem.Row.Computer)"
If ($Listview.SelectedItems.count -eq 0) {
$RemoveServerMenu.IsEnabled = $False
$InstalledUpdatesMenu.IsEnabled = $False
$WindowsUpdateLogMenu.IsEnabled = $False
$WindowsUpdateServiceMenu.IsEnabled = $False
$WUAUCLTMenu.IsEnabled = $False
} ElseIf ($Listview.SelectedItems.count -eq 1) {
$RemoveServerMenu.IsEnabled = $True
$InstalledUpdatesMenu.IsEnabled = $True
$WindowsUpdateLogMenu.IsEnabled = $True
$WindowsUpdateServiceMenu.IsEnabled = $True
$WUAUCLTMenu.IsEnabled = $True
} Else {
$RemoveServerMenu.IsEnabled = $True
$InstalledUpdatesMenu.IsEnabled = $True
$WindowsUpdateLogMenu.IsEnabled = $False
$WUAUCLTMenu.IsEnabled = $True
}
})
#ListView drop file Event
$Global:Listview.add_Drop({
$content = Get-Content $_.Data.GetFileDropList()
$content | ForEach {
$dr = $DataTable.NewRow()
#Add Data To Row
$dr.Computer = $_
$dr.Audited = 0
$dr.Installed = 0
$dr.InstallErrors = 0
$dr.Notes = $Null
#Add Row To Data Table
$DataTable.Rows.Add($dr)
}
$Global:Listview.DataContext = $DataTable
$b = new-object System.Windows.Data.Binding
$b.source = $DataTable
[void]$Global:Listview.SetBinding([System.Windows.Controls.ListView]::ItemsSourceProperty,$b)
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
Show-DebugState
})
#FindFile Button
$BrowseFileButton.Add_Click({
$File = Open-FileDialog
If (-Not ([system.string]::IsNullOrEmpty($File))) {
Get-Content $File | Where {$_ -ne ""} | ForEach {
$dr = $DataTable.NewRow()
#Add Data To Row
$dr.Computer = $_
$dr.Audited = 0
$dr.Installed = 0
$dr.InstallErrors = 0
$dr.Notes = $Null
Try {
#Add Row To Data Table
$DataTable.Rows.Add($dr)
} Catch {
Write-Warning "$($Error[0])"
}
}
$Global:Listview.DataContext = $DataTable
$b = new-object System.Windows.Data.Binding
$b.source = $DataTable
[void]$Global:Listview.SetBinding([System.Windows.Controls.ListView]::ItemsSourceProperty,$b)
$Global:ProgressBar.Maximum = $Global:Listview.ItemsSource.count
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Waiting for action..."
Show-DebugState
}
})
#LoadADButton Events
$LoadADButton.Add_Click({
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Querying Active Directory for Computers..."
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in (Open-DomainDialog)) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
ActiveDirectoryJobFromQueue
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
ActiveDirectoryJobFromQueue
}
}
})
##RunButton Events
$RunButton.add_Click({
Start-RunJob
})
##Client WSUS Service Action
#Stop Service
$WUStopServiceMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Stopping WSUS Client service on selected servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
#Set global variable for Starting service
$Global:ServiceAction = 'Stop'
Write-Debug ("ServiceAction: {0}" -f $ServiceAction)
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
}
}
})
#Start Service
$WUStartServiceMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Starting WSUS Client service on selected servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
#Set global variable for Starting service
$Global:ServiceAction = 'Start'
Write-Debug ("ServiceAction: {0}" -f $ServiceAction)
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
}
}
})
#Restart Service
$WURestartServiceMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Restarting WSUS Client service on selected servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
#Set global variable for Starting service
$Global:ServiceAction = 'Restart'
Write-Debug ("ServiceAction: {0}" -f $ServiceAction)
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
Invoke-WSUSServiceAction -ServiceAction $ServiceAction
}
}
}
})
#View Installed Update Event
$GUIInstalledUpdatesMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Servers = $Listview.SelectedItems | Select -ExpandProperty Computer
[Float]$Global:ProgressBar.Maximum = $servers.count
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
$runbutton.IsEnabled = $False
$StartImage.Source = "$pwd\Images\Start_locked.jpg"
$Cancelbutton.IsEnabled = $True
$CancelImage.Source = "$pwd\Images\Stop.jpg"
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Gathering all installed patches on Servers"
$Global:updatelayout = [Windows.Input.InputEventHandler]{ $Global:ProgressBar.UpdateLayout() }
$Global:Start = Get-Date
[Float]$Global:ProgressBar.Value = 0
# Read the input and queue it up
$Global:queue = [System.Collections.Queue]::Synchronized( (New-Object System.Collections.Queue) )
foreach($item in $Servers) {
$queue.Enqueue($item)
}
# Start up to the max number of concurrent jobs
# Each job will take care of running the rest
If ($queue.count -lt $maxConcurrentJobs) {
$queuecount = $queue.Count
for( $i = 0; $i -le $queuecount; $i++ ) {
ListInstalledUpdates
}
} Else {
for( $i = 0; $i -le $maxConcurrentJobs; $i++ ) {
ListInstalledUpdates
}
}
}
})
#ClearAuditReportMenu Events
$ClearAuditReportMenu.Add_Click({
Remove-Variable AuditPatchReport -scope Global -force -ea 'silentlycontinue'
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Audit Report Cleared!"
})
#ClearInstallReportMenu Events
$ClearInstallReportMenu.Add_Click({
Remove-Variable InstallPatchReport -scope Global -force -ea 'silentlycontinue'
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Install Report Cleared!"
})
#ClearInstalledUpdateMenu
$ClearInstalledUpdateMenu.Add_Click({
Remove-Variable InstalledPatches -scope Global -force -ea 'silentlycontinue'
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Installed Updates Report Cleared!"
})
#ClearServerListMenu Events
$ClearServerListMenu.Add_Click({
$Global:Listview.DataContext.Clear()
$Global:StatusTextBox.Foreground = "Black"
$Global:StatusTextBox.Text = "Server List Cleared!"
})
#AboutMenu Event
$AboutMenu.Add_Click({
Open-PoshPAIGAbout
})
#Options Menu
$OptionMenu.Add_Click({
#Launch options window
Write-Verbose "Launching Options Menu"
.\Options.ps1
#Process Updates Options
Set-PoshPAIGOption
})
#Select All
$SelectAllMenu.Add_Click({
$ListView.SelectAll()
})
#HelpFileMenu Event
$HelpFileMenu.Add_Click({
Open-PoshPAIGHelp
})
$Window.Add_KeyDown({
$key = $_.Key
If ([System.Windows.Input.Keyboard]::IsKeyDown("RightCtrl") -OR [System.Windows.Input.Keyboard]::IsKeyDown("LeftCtrl")) {
Switch ($Key) {
"E" {$This.Close()}
"A" {$ListView.SelectAll()}
"O" {
.\Options.ps1
#Process Updates Options
Set-PoshPAIGOption
}
"S" {Add-Server}
"D" {Remove-Server}
Default {$Null}
}
} ElseIf ([System.Windows.Input.Keyboard]::IsKeyDown("LeftShift") -OR [System.Windows.Input.Keyboard]::IsKeyDown("RightShift")) {
Switch ($Key) {
"RETURN" {Write-Host "Hit Shift+Return"}
}
}
})
$Global:Window.Add_KeyUp({
$Global:Test = $_
Write-Debug ("Key Pressed: {0}" -f $_.Key)
Switch ($_.Key) {
"F1" {Open-PoshPAIGHelp}
"F5" {Start-RunJob}
"F8" {Start-Report}
Default {$Null}
}
})
#AddServer Menu
$AddServerMenu.Add_Click({
Add-Server
})
#RemoveServer Menu
$RemoveServerMenu.Add_Click({
Remove-Server
})
#Run Menu
$RunMenu.Add_Click({
Start-RunJob
})
#Report Menu
$GenerateReportMenu.Add_Click({
Start-Report
})
#Exit Menu
$ExitMenu.Add_Click({
$Global:Window.Close()
})
#ClearAll Menu
$ClearAllMenu.Add_Click({
Reset-DataTable
If ($Global:Listview.DataContext) {
$Global:Listview.DataContext.Clear()
}
$content = $Null
[Float]$Global:ProgressBar.value = 0
$Global:StatusTextBox.Foreground = "Black"
$Global:patchreport = $Null
$Global:StatusTextBox.Text = "Waiting for action..."
})
#Clear Server List notes
$ClearServerListNotesMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Listview.ItemsSource | ForEach {$_.Notes = $Null}
}
})
#Save Server List
$ServerListReportMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path (Join-Path $home Desktop) "serverlist.csv"
$Listview.DataContext | Export-Csv -NoTypeInformation $savedreport
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
})
#HostListMenu
$HostListMenu.Add_Click({
If ($Global:Listview.Items.count -gt 0) {
$Global:StatusTextBox.Foreground = "Black"
$savedreport = Join-Path $reportpath "hosts.txt"
$Listview.DataContext | Select -Expand Computer | Out-File $savedreport
$Global:StatusTextBox.Text = "Report saved to $savedreport"
} Else {
$Global:StatusTextBox.Foreground = "Red"
$Global:StatusTextBox.Text = "No report to create!"
}
})
#Report Generation
$GenerateReportButton.Add_Click({
Start-Report
})
#Clear Error log
$ClearErrorMenu.Add_Click({
Write-Verbose "Clearing error log"
$Error.Clear()
})
#View Error Event
$ViewErrorMenu.Add_Click({
Get-Error | Out-GridView
})
#ResetServerListData Event
$ResetDataMenu.Add_Click({
Write-Verbose "Resetting Server List data"
$Listview.ItemsSource | ForEach {$_.Notes = $Null;$_.Audited = 0;$_.Installed = 0;$_.InstallErrors = 0;$_.Services = 0}
})
Reset-DataTable
[void]$Global:Window.ShowDialog()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment