From b40c4e36b893b27c6e09c039f923a2e7dc0388e0 Mon Sep 17 00:00:00 2001 From: Palmer Date: Thu, 28 May 2020 11:38:11 -0400 Subject: [PATCH 1/5] Verbosity in Test-SRVRecords --- PSADHealth/PSADHealth.psm1 | 15 ++++++++ PSADHealth/Public/Test-SRVRecords.ps1 | 53 ++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/PSADHealth/PSADHealth.psm1 b/PSADHealth/PSADHealth.psm1 index e69de29..6402bd1 100644 --- a/PSADHealth/PSADHealth.psm1 +++ b/PSADHealth/PSADHealth.psm1 @@ -0,0 +1,15 @@ +$Private = @(Get-ChildItem -Path $PSScriptRoot\Private\*.ps1 -Exclude *.tests.ps1 -ErrorAction SilentlyContinue) +$Public = @(Get-ChildItem -Path $PSScriptRoot\Public\*.ps1 -Exclude *.tests.ps1 -ErrorAction SilentlyContinue) + +ForEach ($import in @($Public + $Private)) { + Try { + . $import.FullName + } + Catch { + Write-Error -Message "Failed to import function $($import.FullName): $_" + } +} +foreach ($function in $Public) { + $func = $function.BaseName -replace 'Function-', '' + Export-ModuleMember -Function $func +} diff --git a/PSADHealth/Public/Test-SRVRecords.ps1 b/PSADHealth/Public/Test-SRVRecords.ps1 index a503205..94596de 100644 --- a/PSADHealth/Public/Test-SRVRecords.ps1 +++ b/PSADHealth/Public/Test-SRVRecords.ps1 @@ -1,21 +1,66 @@ Function Test-SRVRecords { - + <# + .SYNOPSIS + Compares DNS entries to the number of domain controllers and notifies of inconsistencies + + .DESCRIPTION + Compares DNS entries to the number of domain controllers and notifies of inconsistencies + Checks the various records in the _msdcs.domainname zone for consistency with the number of DCs in the environment + + .EXAMPLE + PS C:\> Test-SRVRecords + + Runs tests silently and only notifies you of issues + + .EXAMPLE + PS C:\> Test-SRVRecords -Verbose + + Runs tests with feedback of progress and only notifies you via email if there are issues + + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-SRVRecords -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-SRVRecords} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-SRVRecords on an hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + + .NOTES + Changes by Charles Palmer 5/28/2020 + Verbosity Updates: + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Added Verbose statement for each populated variable + Added Verbose statement for object counts + Added Comment based Help section (and these notes) + Commentary: + The version from the PSGallery doesn't contain the foreach/tolower code + I had the extra records as called out in Issue #96. I had added the verbose statements to help me figure out what was wrong + The assumption is missing records when sending the email. + Sometimes it has to do with extra records (retired DCs not cleaned up properly, duplicate entries based on case, etc.) + #> [cmdletBinding()] Param() begin { - Import-Module ActiveDirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" #Creates a global $configuration variable $null = Get-ADConfig } process { $DomainFQDN = (Get-ADDomain).dnsroot + Write-Verbose -Message "DomainFQDN: $DomainFQDN" $DCList = ((Get-ADGroupMember "Domain Controllers").name).tolower() + Write-Verbose -Message "DCList: $DCList" $DCCount = $DCList.Length + Write-Verbose -Message "DCCount: $DCCount" $PDCEmulator = ((Get-ADDomainController -Discover -Service PrimaryDC).name).tolower() + Write-Verbose -Message "PDCEmulator: $PDCEmulator" $MSDCSZoneName = "_msdcs." + $DomainFQDN + Write-Verbose -Message "MSDCSZoneName: $MSDCSZoneName" $DC_SRV_Record = '_ldap._tcp.dc' $GC_SRV_Record = '_ldap._tcp.gc' @@ -24,11 +69,15 @@ Function Test-SRVRecords { $DC_SRV_RecordCount = (@(Get-DnsServerResourceRecord -ZoneName $MSDCSZoneName -Name $DC_SRV_Record -RRType srv -ComputerName $PDCEmulator | ForEach-Object { $_.RecordData.DomainName.toLower() } | Sort-Object | Get-Unique).count) + Write-Verbose -Message "DC_SRV_RecordCount: $DC_SRV_RecordCount" $GC_SRV_RecordCount = (@(Get-DnsServerResourceRecord -ZoneName $MSDCSZoneName -Name $GC_SRV_Record -RRType srv -ComputerName $PDCEmulator | ForEach-Object { $_.RecordData.DomainName.toLower() } | Sort-Object | Get-Unique).count) + Write-Verbose -Message "GC_SRV_RecordCount: $GC_SRV_RecordCount" $KDC_SRV_RecordCount = (@(Get-DnsServerResourceRecord -ZoneName $MSDCSZoneName -Name $KDC_SRV_Record -RRType srv -ComputerName $PDCEmulator | ForEach-Object { $_.RecordData.DomainName.toLower() } | Sort-Object | Get-Unique).count) + Write-Verbose -Message "KDC_SRV_RecordCount: $KDC_SRV_RecordCount" $PDC_SRV_RecordCount = (@(Get-DnsServerResourceRecord -ZoneName $MSDCSZoneName -Name $PDC_SRV_Record -RRType srv -ComputerName $PDCEmulator).Count) + Write-Verbose -Message "PDC_SRV_RecordCount: $PDC_SRV_RecordCount" $DCHash = @{ } $DCHash.add($dc_SRV_Record, $dc_SRV_RecordCount) From 615c6e0253999567a02bab3ffe1077d74eee90c4 Mon Sep 17 00:00:00 2001 From: Charles Palmer Date: Thu, 28 May 2020 14:57:33 -0400 Subject: [PATCH 2/5] Verbosity updates and a few small functionality updates --- PSADHealth/Public/Get-ADConfig.ps1 | 16 ++- PSADHealth/Public/Get-ADLastBackupDate.ps1 | 33 +++--- .../Public/Test-ADObjectReplication.ps1 | 61 ++++++----- PSADHealth/Public/Test-DCsOnline.ps1 | 39 ++++++- PSADHealth/Public/Test-ExternalDNSServers.ps1 | 102 +++++++++++++----- PSADHealth/Public/Test-SRVRecords.ps1 | 7 +- 6 files changed, 186 insertions(+), 72 deletions(-) diff --git a/PSADHealth/Public/Get-ADConfig.ps1 b/PSADHealth/Public/Get-ADConfig.ps1 index fe82e3c..a551944 100644 --- a/PSADHealth/Public/Get-ADConfig.ps1 +++ b/PSADHealth/Public/Get-ADConfig.ps1 @@ -10,7 +10,8 @@ function Get-ADConfig { Get-ADConfig "C:\configs\ADConfig.json" - + .NOTES + Added configuration file location test to ease testing of individual functions #> [cmdletBinding()] [Alias('Get-ADHealthConfig')] @@ -21,7 +22,18 @@ function Get-ADConfig { $ConfigurationFile = "$PSScriptRoot\Config\ADConfig.json" ) - begin {} + begin { + Write-Verbose -Message "Verifying Configuration File Path valid: $ConfigurationFile" + If (-not (Test-Path -Path $ConfigurationFile)) { + #When testing the module during development, the default pathing doesn't work + $ConfigurationFile = $ConfigurationFile.Replace('Public\', '') + If (Test-Path -Path $ConfigurationFile) { + Write-Verbose -Message "Configuration path updated: $ConfigurationFile" + } else { + Write-Warning -Message "Unable to find configuration File!!!" + } + } + } process { diff --git a/PSADHealth/Public/Get-ADLastBackupDate.ps1 b/PSADHealth/Public/Get-ADLastBackupDate.ps1 index b7f0c09..79ea4fd 100644 --- a/PSADHealth/Public/Get-ADLastBackupDate.ps1 +++ b/PSADHealth/Public/Get-ADLastBackupDate.ps1 @@ -9,7 +9,12 @@ function Get-ADLastBackupDate { This script Checks AD for the last backup date .EXAMPLE - Run as a scheduled task. Use Event Log consolidation tools to pull and alert on issues found. + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADLastBackupDate -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADLastBackupDate} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADLastBackupDate on a daily basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege .EXAMPLE Run in verbose mode if you want on-screen feedback for testing @@ -30,23 +35,26 @@ function Get-ADLastBackupDate { #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $null = Get-ADConfig $SupportArticle = $Configuration.SupportArticle - if (![System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { - write-verbose "Adding Event Source." + If (-not ([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { + Write-Verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" - }#end if + } #end if Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17051 -EntryType Information -message "START of AD Backup Check ." -category "17051" $Domain = (Get-ADDomain).DNSRoot + Write-Verbose -Message "Domain: $Domain" $Regex = '\d\d\d\d-\d\d-\d\d' $CurrentDate = Get-Date $MaxDaysSinceBackup = $Configuration.MaxDaysSinceBackup + Write-Verbose -Message "Maximum allowed days since last backup: $MaxDaysSinceBackup" }#End Begin @@ -56,12 +64,10 @@ function Get-ADLastBackupDate { #Compare the last backup date to today's date $Result = (New-TimeSpan -Start $LastBackup -End $CurrentDate).Days - Write-Verbose "Last Active Directory backup occurred on $LastBackup! $Result days is less than the alert criteria of $MaxDaysSinceBackup day." - #Test if result is greater than max allowed days without backup If ($Result -gt $MaxDaysSinceBackup) { - Write-Verbose "Last Active Directory backup occurred on $LastBackup! $Result days is higher than the alert criteria of $MaxDaysSinceBackup day." + Write-Verbose -Message "Last Active Directory backup occurred on $LastBackup! $Result days is higher than the alert criteria of $MaxDaysSinceBackup day." $emailOutput = "Last Active Directory backup occurred on $LastBackup! $Result days is higher than the alert criteria of $MaxDaysSinceBackup day." @@ -79,9 +85,11 @@ function Get-ADLastBackupDate { } Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for Last AD Backup Date" #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - AD Last Backup is $Result days old" }else { + Write-Verbose -Message "Last Active Directory backup occurred on $LastBackup! $Result days is less than the alert criteria of $MaxDaysSinceBackup day." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17052 -EntryType Information -message "SUCCESS - Last Active Directory backup occurred on $LastBackup! $Result days is less than the alert criteria of $MaxDaysSinceBackup day." -category "17052" }#end else @@ -93,11 +101,11 @@ function Get-ADLastBackupDate { Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17053 -EntryType Information -message "END of AD Backup Check ." -category "17053" If (!$CurrentFailure){ - Write-Verbose "No Issues found in this run" - $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | where {($_.InstanceID -Match "17050")} + Write-Verbose -Message "No Issues found in this run" + $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | Where-Object {($_.InstanceID -Match "17050")} If ($InError) { - Write-Verbose "Previous Errors Seen" + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared $alertclearedParams = @{ @@ -110,6 +118,7 @@ function Get-ADLastBackupDate { } Send-MailMessage @alertclearedParams + Write-Verbose -Message "Sent email notification for Last AD Backup Date Recovery" #Write-Verbose "Sending Slack Message - AD Backup Alert Cleared" #New-SlackPost "The previous alert, for AD Last Backup has cleared." #Write-Output $InError @@ -119,4 +128,4 @@ function Get-ADLastBackupDate { }#End End -}#End Function \ No newline at end of file +}#End Function diff --git a/PSADHealth/Public/Test-ADObjectReplication.ps1 b/PSADHealth/Public/Test-ADObjectReplication.ps1 index 2589f47..2e17482 100644 --- a/PSADHealth/Public/Test-ADObjectReplication.ps1 +++ b/PSADHealth/Public/Test-ADObjectReplication.ps1 @@ -38,31 +38,38 @@ function Test-ADObjectReplication { #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $NBN = (Get-ADDomain).NetBIOSName + Write-Verbose -Message "NetBIOSName: $NBN" $Domain = (Get-ADDomain).DNSRoot + Write-Verbose -Message "Domain: $Domain" $domainname = (Get-ADDomain).dnsroot + Write-Verbose -Message "FQDN: $domainname" $null = Get-ADConfig $SupportArticle = $Configuration.SupportArticle - if (![System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { - write-verbose "Adding Event Source." + if (-not ([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { + Write-Verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" } $continue = $true $CurrentFailure = $false $existingObj = $null $DCs = (Get-ADDomainController -Filter *).Name + Write-Verbose -Message "DCList: $DCs" $SourceSystem = (Get-ADDomain).pdcemulator + Write-Verbose -Message "PDC: $SourceSystem" [int]$MaxCycles = $Configuration.MaxObjectReplCycles + Write-Verbose -Message "Testing will commence for $MaxCycles cycles at maximum (1 minute sleep between cycles)" } Process { if (Test-NetConnection $SourceSystem -Port 445 -InformationLevel Quiet) { - Write-Verbose 'PDCE is online' + Write-Verbose -Message 'PDCE is online' $tempObjectPath = (Get-ADDomain).computersContainer $existingObj = Get-ADComputer -filter 'name -like "ADRT-*"' -prop * -SearchBase "$tempObjectPath" |Select-Object -ExpandProperty Name If ($existingObj){ - Write-Verbose "Warning - Cleanup of a old object(s) may not have occured. Object(s) starting with 'ADRT-' exists in $tempObjectPath : $existingObj - Please review, and cleanup if required." + Write-Verbose -Message "Warning - Cleanup of a old object(s) may not have occured. Object(s) starting with 'ADRT-' exists in $tempObjectPath : $existingObj - Please review, and cleanup if required." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17019 -EntryType Warning -message "WARNING - AD Object Replication Cleanup of old object(s) may not have occured. Object(s) starting with 'ADRT-' exists in $tempObjectPath : $existingObj. Please review, and cleanup if required." -category "17019" #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - Cleanup of a old object(s) may not have occured. Object(s) starting with 'ADRT-' exists in $tempObjectPath : $existingObj - Please review, and cleanup if required." @@ -74,12 +81,12 @@ function Test-ADObjectReplication { New-ADComputer -Name "$tempObjectName" -samAccountName "$tempObjectName" -Path "$tempObjectPath" -Server $SourceSystem -Enabled $False - Write-Verbose "Object created for tracking - $tempObjectName in $site" + Write-Verbose -Message "Object created for tracking - $tempObjectName in $site" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17016 -EntryType Information -message "CREATED AD Object Replication Test object - $tempObjectName - has been created on $SourceSystem in site - $site" -category "17016" $i = 0 } else { - Write-Verbose 'PDCE is offline. You should really resolve that before continuing.' + Write-Verbose -Message 'PDCE is offline. You should really resolve that before continuing.' Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17010 -EntryType Error -message "FAILURE AD Object Replication - Failed to connect to PDCE - $SourceSystem in site - $site" -category "17010" $Alert = "In $domainname Failed to connect to PDCE - $dc in site - $site. Test stopping! See the following support article $SupportArticle" $CurrentFailure = $true @@ -91,21 +98,21 @@ function Test-ADObjectReplication { While ($continue) { $i++ - Write-Verbose 'Sleeping for 1 minute.' + Write-Verbose -Message 'Sleeping for 1 minute.' Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17018 -EntryType Information -message "SLEEPING AD Object Replication for 1 minute" -category "17018" Start-Sleep 60 $replicated = $true - Write-Verbose "Cycle - $i" + Write-Verbose -Message "Cycle - $i" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17011 -EntryType Information -message "CHECKING AD Object Replication ADRepl Cycle $i" -category "17011" Foreach ($dc in $DCs) { $site = (Get-ADDomainController $dc).site if (Test-NetConnection $dc -Port 445 -InformationLevel Quiet) { - Write-Verbose "Online - $dc" + Write-Verbose -Message "Online - $dc" $connectionResult = "SUCCESS" } else { - Write-Verbose "!!!!!OFFLINE - $dc !!!!!" + Write-Verbose -Message "!!!!!OFFLINE - $dc !!!!!" $connectionResult = "FAILURE" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17010 -EntryType Error -message "FAILURE AD Object Replication failed to connect to DC - $dc in site - $site" -category "17010" @@ -123,12 +130,12 @@ function Test-ADObjectReplication { # If The Connection To The DC Is Successful If ($connectionResult -eq "SUCCESS") { Try { - $Milliseconds = (Measure-Command {$Query = Get-ADComputer $tempObjectName -Server $dc | select Name}).TotalMilliseconds + $Milliseconds = (Measure-Command {$Query = Get-ADComputer $tempObjectName -Server $dc | Select-Object Name}).TotalMilliseconds Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17013 -EntryType information -message "SUCCESS AD Object Replication Test object replicated to - $dc in site - $site - in $Milliseconds ms. " -category "17013" - write-Verbose "SUCCESS! - Replicated - $($query.Name) - $($dc) - $site - $Milliseconds" + write-Verbose -Message "SUCCESS! - Replicated - $($query.Name) - $($dc) - $site - $Milliseconds" } Catch { - write-Verbose "PENDING! - Test object $tempObjectName does not exist on $dc in $site." + write-Verbose -Message "PENDING! - Test object $tempObjectName does not exist on $dc in $site." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17012 -EntryType information -message "PENDING AD Object Replication Test object pending replication to - $dc in site - $site. " -category "17012" $replicated = $false } @@ -136,7 +143,7 @@ function Test-ADObjectReplication { # If The Connection To The DC Is Unsuccessful If ($connectionResult -eq "FAILURE") { - Write-Verbose " - Unable To Connect To DC/GC And Check For The Temp Object..." + Write-Verbose -Message " - Unable To Connect To DC/GC And Check For The Temp Object..." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17010 -EntryType Error -message "FAILURE AD Object Replication failed to connect to DC - $dc in site - $site" -category "17010" $Alert = "In $domainname Failed to connect to DC - $dc in site - $site. See the following support article $SupportArticle" $CurrentFailure = $true @@ -151,9 +158,9 @@ function Test-ADObjectReplication { If ($i -gt $MaxCycles) { $continue = $false #gather event history to see which DC did, and which did not, get the replication - $list = Get-EventLog application -After (Get-Date).AddHours(-2) | where {($_.InstanceID -Match "17012") -OR ($_.InstanceID -Match "17013") -OR ($_.InstanceID -Match "17016")} - $RelevantEvents = $list |Select InstanceID,Message |Out-String - Write-Verbose "Cycle has run $i times, and replication hasn't finished. Need to generate an alert." + $list = Get-EventLog application -After (Get-Date).AddHours(-2) | Where-Object {($_.InstanceID -Match "17012") -OR ($_.InstanceID -Match "17013") -OR ($_.InstanceID -Match "17016")} + $RelevantEvents = $list |Select-Object InstanceID,Message |Out-String + Write-Verbose -Message "Cycle has run $i times, and replication hasn't finished. Need to generate an alert." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17014 -EntryType Warning -message "INCOMPLETE AD Object Replication Test cycle has run $i times without the object succesfully replicating to all DCs" -category "17014" $Alert = "In $domainname - the AD Object Replication Test cycle has run $i times without the object succesfully replicating to all DCs. @@ -179,21 +186,21 @@ function Test-ADObjectReplication { $output = $output + "`n Duration........: $duration Seconds" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17015 -EntryType Information -message "Test cycle has Ended - $output" -category "17015" - Write-Verbose "`n Start Time......: $(Get-Date $startDateTime -format "yyyy-MM-dd HH:mm:ss")" - Write-Verbose " End Time........: $(Get-Date $endDateTime -format "yyyy-MM-dd HH:mm:ss")" - Write-Verbose " Duration........: $duration Seconds" + Write-Verbose -Message "`n Start Time......: $(Get-Date $startDateTime -format "yyyy-MM-dd HH:mm:ss")" + Write-Verbose -Message " End Time........: $(Get-Date $endDateTime -format "yyyy-MM-dd HH:mm:ss")" + Write-Verbose -Message " Duration........: $duration Seconds" # Delete The Temp Object On The RWDC - Write-Verbose " Deleting AD Object File..." + Write-Verbose -Message " Deleting AD Object File..." Remove-ADComputer $tempObjectName -Confirm:$False - Write-Verbose " AD Object [$tempObjectName] Has Been Deleted." + Write-Verbose -Message " AD Object [$tempObjectName] Has Been Deleted." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17017 -EntryType Information -message "DELETED AD Object Replication test object - $tempObjectName - has been deleted." -category "17017" If (!$CurrentFailure){ - Write-Verbose "No Issues found in this run" - $InError = Get-EventLog application -After (Get-Date).AddHours(-2) | where {($_.InstanceID -Match "17010") -or ($_.InstanceID -Match "17014")} + Write-Verbose -Message "No Issues found in this run" + $InError = Get-EventLog application -After (Get-Date).AddHours(-2) | Where-Object {($_.InstanceID -Match "17010") -or ($_.InstanceID -Match "17014")} If ($InError) { - Write-Verbose "Previous Errors Seen" + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared Send-AlertCleared @@ -204,4 +211,4 @@ function Test-ADObjectReplication { }#End if } -} \ No newline at end of file +} diff --git a/PSADHealth/Public/Test-DCsOnline.ps1 b/PSADHealth/Public/Test-DCsOnline.ps1 index be291db..dbb9a91 100644 --- a/PSADHealth/Public/Test-DCsOnline.ps1 +++ b/PSADHealth/Public/Test-DCsOnline.ps1 @@ -1,19 +1,49 @@ Function Test-DCsOnline { + <# + .SYNOPSIS + Simple connection test to all domain controllers to verify they are online + + .DESCRIPTION + Simple connection test to all domain controllers to verify they are online + + .EXAMPLE + PS C:\> Test-DCsOnline + + This will silently test all Domain Controllers are online and only email notification if there is a failure + + .EXAMPLE + PS C:\> Test-DCsOnline -Verbose + + This will provide a status while testing from all Domain Controllers and only email notification if there is a failure + In the event of a Ping and/or TCP-53 test failure, there will be a warning messages displayed in the console when interactive + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-DCsOnline -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-DCsOnline} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-DCsOnline on an hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES + + #> [cmdletBinding()] Param() Begin { - Import-Module ActiveDirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" #Creates a global $configuration variable $null = Get-ADConfig } Process { $DClist = (get-adgroupmember "Domain Controllers").name + Write-Verbose -Message "DCList: $DCList" ForEach ($server in $DClist){ + Write-Verbose -Message "Testing $server is online" - if ((!(Test-Connection -ComputerName $Server -quiet -count 4))) + if ((-not (Test-Connection -ComputerName $Server -quiet -count 4))) { $Subject = "Server $Server is offline" $EmailBody = @" @@ -34,9 +64,12 @@ Function Test-DCsOnline { BodyAsHtml = $true } Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for failed DC online test" } #End if }#End Foreach } - End {} + End { + Write-Verbose -Message "Finished verifying all DCs are online" + } } \ No newline at end of file diff --git a/PSADHealth/Public/Test-ExternalDNSServers.ps1 b/PSADHealth/Public/Test-ExternalDNSServers.ps1 index 3ffcf66..f756bdf 100644 --- a/PSADHealth/Public/Test-ExternalDNSServers.ps1 +++ b/PSADHealth/Public/Test-ExternalDNSServers.ps1 @@ -1,47 +1,95 @@ # Test-ExternalDNSServers.ps1 Function Test-ExternalDNSServers { + <# + .SYNOPSIS + Queries external DNS servers from each domain controller to verify access + .DESCRIPTION + Queries external DNS servers from each domain controller to verify access + Default tests query OpenDNS servers and verify access. Basic test is a ping test. + If Ping fails, TCP-53 is tested to see if perhaps ICMP is disallowed but the DNS server would otherwise work + .EXAMPLE + PS C:\> Test-ExternalDNSServers + + This will silently test from all Domain Controllers and only email notification if there is a failure + In the event of a Ping and/or TCP-53 test failure, there will be a warning messages displayed in the console when interactive + .EXAMPLE + PS C:\> Test-ExternalDNSServers -Verbose + + This will provide a status while testing from all Domain Controllers and only email notification if there is a failure + In the event of a Ping and/or TCP-53 test failure, there will be a warning messages displayed in the console when interactive + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ExternalDNSServers -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ExternalDNSServers} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ExternalDNSServers on an hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES + Functionality Updates: + Sometimes ping fails to an otherwise working DNS server (Azure VM that is a DC may not be able to ping out to the Internet) + Added a warning and followup check that uses Test-NetConnection to verify TCP-53 access is working and considering that a pass + Updated email notification to include this additional functionality in the notification + Added Comment based help section (and these notes) + Verbosity Updates: + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Added Verbose statement to display server and external DNS being worked on (showing progress) + #> [cmdletBinding()] Param() begin { - Import-Module ActiveDirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" #Creates a global $configuration variable $null = Get-ADConfig } process { $DClist = (get-adgroupmember "Domain Controllers").name - $ExternalDNSServers = $Configuration.ExternalDNSServers + Write-Verbose -Message "DCList: $DCList" + $ExternalDNSServers = $Configuration.ExternalDNSServers + Write-Verbose -Message "ExternalDNSServers: $ExternalDNSServers" ForEach ($server in $DClist){ + Write-Verbose -Message "Testing $server External DNS access" ForEach ($DNSServer in $ExternalDNSServers) { + Write-Verbose -Message "--Testing $DNSServer External DNS access" - if ((!(Invoke-Command -ComputerName $server -ScriptBlock { Test-Connection $args[0] -Quiet -Count 1} -ArgumentList $DNSServer))) - { + If ((-not (Invoke-Command -ComputerName $server -ScriptBlock { Test-Connection $args[0] -Quiet -Count 1} -ArgumentList $DNSServer))) { + + #This server can't ping the necessary DNS server, but maybe it can access them via TCP53? + Write-Warning -Message "----DC ($server) cannot ping the DNS Server: $DNSServer; testing TCP/53 access" + If ((-not (Invoke-Command -ComputerName $server -ScriptBlock { Test-NetConnection -ComputerName $args[0] -Port 53 } -ArgumentList $DNSServer))) { + Write-Warning -Message "----$server cannot connect to port 53 for DNS Server: $DNSServer; External DNS access is failing" + - $Subject = "External DNS $DNSServer is unreachable" - $EmailBody = @" - - - A Test connection from $Server to $DNSServer was unsuccessful! - Time of Event: """$((get-date))"""
-
- THIS EMAIL WAS AUTO-GENERATED. PLEASE DO NOT REPLY TO THIS EMAIL. + $Subject = "External DNS $DNSServer is unreachable" + $EmailBody = @" + + + A Test connection from $Server to $DNSServer was unsuccessful! + A Test connection via TCP-53 from $server to $DNSServer was unsuccessful as well! + Time of Event: """$((get-date))"""
+
+ THIS EMAIL WAS AUTO-GENERATED. PLEASE DO NOT REPLY TO THIS EMAIL. "@ - $mailParams = @{ - To = $Configuration.MailTo - From = $Configuration.MailFrom - SmtpServer = $Configuration.SmtpServer - Subject = $Subject - Body = $EmailBody - BodyAsHtml = $true - } - - Send-MailMessage @mailParams - - } #End if + $mailParams = @{ + To = $Configuration.MailTo + From = $Configuration.MailFrom + SmtpServer = $Configuration.SmtpServer + Subject = $Subject + Body = $EmailBody + BodyAsHtml = $true + } + + Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for external DNS test from $server to $DNSServer" + } #end if TCP53 + + } #End if Ping }# End Foreach (DCLIst) @@ -49,5 +97,7 @@ Function Test-ExternalDNSServers { } - end {} -} \ No newline at end of file + end { + Write-Verbose -Message "Finished testing External DNS Servers for all DCs" + } +} diff --git a/PSADHealth/Public/Test-SRVRecords.ps1 b/PSADHealth/Public/Test-SRVRecords.ps1 index 94596de..65a4eb3 100644 --- a/PSADHealth/Public/Test-SRVRecords.ps1 +++ b/PSADHealth/Public/Test-SRVRecords.ps1 @@ -113,7 +113,7 @@ Function Test-SRVRecords { } Send-MailMessage @mailParams - + Write-Verbose -Message "Sent email notification for failed SRV record in $($Record.keys) zone" } #End if }#End Foreach @@ -138,7 +138,10 @@ Function Test-SRVRecords { } Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for failed PDC record in $MSDCSZoneName zone" } #END PDC If } - end { } + end { + Write-Verbose -Message "Finished testing DNS SRV Records for all DCs" + } } From 94392fbc8cc851acdc9fda27ed57d687a369c7ca Mon Sep 17 00:00:00 2001 From: Charles Palmer Date: Fri, 29 May 2020 18:46:18 -0400 Subject: [PATCH 3/5] Verbosity updates and renamed files to match Function name --- PSADHealth/PSADHealth.psd1 | Bin 9386 -> 9514 bytes ...meSync.ps1 => Test-ADExternalTimeSync.ps1} | 44 +++++--- ...meSync.ps1 => Test-ADInternalTimeSync.ps1} | 46 +++++--- .../Public/Test-ADObjectReplication.ps1 | 10 +- PSADHealth/Public/Test-ADReplication.ps1 | 43 +++++--- PSADHealth/Public/Test-ADServices.ps1 | 14 ++- ...ication.ps1 => Test-SYSVOLReplication.ps1} | 103 ++++++++++++------ 7 files changed, 179 insertions(+), 81 deletions(-) rename PSADHealth/Public/{Test-ExternalTimeSync.ps1 => Test-ADExternalTimeSync.ps1} (69%) rename PSADHealth/Public/{Test-InternalTimeSync.ps1 => Test-ADInternalTimeSync.ps1} (64%) rename PSADHealth/Public/{Test-SYSVOL-Replication.ps1 => Test-SYSVOLReplication.ps1} (63%) diff --git a/PSADHealth/PSADHealth.psd1 b/PSADHealth/PSADHealth.psd1 index 6df21c48eb295ce1f04289e70dfcd82f7ef4e1ce..440df0a85c356b26604db6802008bdb5c8d7b372 100644 GIT binary patch delta 207 zcmZ4GxyoyU3lpQoW>=2%B9jUeGLu9TD3f#(ItM2JQvhWEbd&!R7n9Tz x6qE21E|Vq{E|XjoD3gp7CX>1pIFtGmIFmXRIFou6D3hucHk0TTDzg?A6c18J9iIRI diff --git a/PSADHealth/Public/Test-ExternalTimeSync.ps1 b/PSADHealth/Public/Test-ADExternalTimeSync.ps1 similarity index 69% rename from PSADHealth/Public/Test-ExternalTimeSync.ps1 rename to PSADHealth/Public/Test-ADExternalTimeSync.ps1 index 43152af..95dcb3f 100644 --- a/PSADHealth/Public/Test-ExternalTimeSync.ps1 +++ b/PSADHealth/Public/Test-ADExternalTimeSync.ps1 @@ -28,21 +28,33 @@ function Test-ADExternalTimeSync { 17043 - End of test 17044 - Alert Email Sent 17045 - Automated Repair Attempted + + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Primarily adding -Message for good code hygiene and expanding any aliases + File name verse function name is inconsistent. Renamed file to be consistent with function name + The CurrentFailure notification piece isn't working. I am getting notification every time the script runs that it is no longer failing + The reason is because it was checking for $server in the error text and it should have been $PDCEmulator. Corrected #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $CurrentFailure = $null $null = Get-ADConfig - if (![System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { - write-verbose "Adding Event Source." + if (-not ([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { + write-verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" }#end if #$DClist = (Get-ADGroupMember -Identity 'Domain Controllers').name #For RWDCs only, RODCs are not in this group. $PDCEmulator = (Get-ADDomainController -Discover -Service PrimaryDC).name + Write-Verbose -Message "PDC Emulator : $PDCEmulator" $ExternalTimeSvr = $Configuration.ExternalTimeSvr + Write-Verbose -Message "External Time Server: $ExternalTimeSvr" $MaxTimeDrift = $Configuration.MaxExtTimeDrift + Write-Verbose -Message "Maximum Time Drift : $MaxTimeDrift" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17041 -EntryType Information -message "START of External Time Sync Test Cycle ." -category "17041" }#End Begin @@ -52,10 +64,10 @@ function Test-ADExternalTimeSync { $ExternalTime = (w32tm /stripchart /dataonly /computer:$ExternalTimeSvr /samples:1)[-1].split("[")[0] $ExternalTimeOutput = [Regex]::Match($ExternalTime, "\d+\:\d+\:\d+").value $result = (New-TimeSpan -Start $ExternalTimeOutput -End $PDCeTime).Seconds - + $emailOutput = "$PDCEmulator - Offset: $result - Time:$PDCeTime - ReferenceTime: $ExternalTimeOutput `r`n " - Write-Verbose "ServerName $PDCEmulator - Offset: $result - ExternalTime: $ExternalTimeOutput - PDCE Time: $PDCeTime" + Write-Verbose -Message "ServerName $PDCEmulator - Offset: $result - ExternalTime: $ExternalTimeOutput - PDCE Time: $PDCeTime" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17042 -EntryType Information -message "CHECKING External Time Sync on Server - $PDCEmulator - $emailOutput" -category "17042" #If result is a negative number (ie -6 seconds) convert to positive number @@ -64,7 +76,7 @@ function Test-ADExternalTimeSync { #test if result is greater than max time drift If ($result -gt $MaxTimeDrift) { - Write-Verbose "ALERT - Time drift above maximum allowed threshold on - $server - $emailOutput" + Write-Verbose -Message "ALERT - Time drift above maximum allowed threshold on - $server - $emailOutput" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17040 -EntryType Warning -message "FAILURE External time drift above maximum allowed on $emailOutput `r`n " -category "17040" #attempt to automatically fix the issue @@ -77,22 +89,25 @@ function Test-ADExternalTimeSync { To = $Configuration.MailTo From = $Configuration.MailFrom SmtpServer = $Configuration.SmtpServer - Subject = $"AD External Time Sync Alert!" + Subject = "AD External Time Sync Alert!" Body = $emailOutput BodyAsHtml = $true } Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for External Time Sync discrepancy" #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - External Time drift above max threashold - $emailOutput" }#end if - If (!$CurrentFailure) { - Write-Verbose "No Issues found in this run" - $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | where {($_.InstanceID -Match "17040")} - $errtext = $InError |out-string - If ($errtext -like "*$server*") { - Write-Verbose "Previous Errors Seen" +#<# + If (-not $CurrentFailure) { + Write-Verbose -Message "No Issues found in this run" + $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | Where-Object {($_.InstanceID -Match "17040")} + $errtext = $InError | Out-String + Write-Verbose -Message "$errtext" + If ($errtext -like "*$PDCEmulator*") { + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared @@ -110,15 +125,18 @@ function Test-ADExternalTimeSync { } Send-MailMessage @alertParams + Write-Verbose -Message "Sent email notification for External Time Sync recovery" #Write-Verbose "Sending Slack Message - Alert Cleared" #New-SlackPost "The previous alert, for AD External Time Sync, has cleared." }#End if }#End if +#> }#End Process End { + Write-Verbose -Message "Finished validating External Time Sync" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17043 -EntryType Information -message "END of External Time Sync Test Cycle ." -category "17043" }#End End diff --git a/PSADHealth/Public/Test-InternalTimeSync.ps1 b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 similarity index 64% rename from PSADHealth/Public/Test-InternalTimeSync.ps1 rename to PSADHealth/Public/Test-ADInternalTimeSync.ps1 index f76a2fb..38147be 100644 --- a/PSADHealth/Public/Test-InternalTimeSync.ps1 +++ b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 @@ -28,21 +28,33 @@ function Test-ADInternalTimeSync { 17033 - End of test 17034 - Alert Email Sent 17035 - Automated Repair Attempted + + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Primarily adding -Message for good code hygiene and expanding any aliases + Added a few extra Verbose statements + Disabled the Slack Notification since that isn't enabled in any of the other functions + Send-AlertCleared was not passing the $InError variable (I think this is a Script to Function issue). Corrected #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $CurrentFailure = $null $null = Get-ADConfig $SupportArticle = $Configuration.SupportArticle $SlackToken = $Configuration.SlackToken - if (!([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { - write-verbose "Adding Event Source." + if (-not ([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { + write-verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" }#end if $DClist = (Get-ADDomainController -Filter *).name # For ALL DCs + Write-Verbose -Message "DCList: $DCList" $PDCEmulator = (Get-ADDomainController -Discover -Service PrimaryDC).name + Write-Verbose -Message "PDC Emulator: $PDCEmulator" $MaxTimeDrift = $Configuration.MaxIntTimeDrift + Write-Verbose -Message "Maximum Time Drift: $MaxTimeDrift" $beginEventLog = @{ LogName = "Application" @@ -62,13 +74,13 @@ function Test-ADInternalTimeSync { Foreach ($server in $DClist) { Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17032 -EntryType Information -message "CHECKING Internal Time Sync on Server - $server" -category "17032" - Write-Verbose "CHECKING - $server" + Write-Verbose -Message "CHECKING - $server" $OutputDetails = $null $Remotetime = ([WMI]'').ConvertToDateTime((Get-WmiObject -Class win32_operatingsystem -ComputerName $server).LocalDateTime) $Referencetime = ([WMI]'').ConvertToDateTime((Get-WmiObject -Class win32_operatingsystem -ComputerName $PDCEmulator).LocalDateTime) $result = (New-TimeSpan -Start $Referencetime -End $Remotetime).Seconds - Write-Verbose "$server - Offset: $result - Time:$Remotetime - ReferenceTime: $Referencetime" + Write-Verbose -Message "$server - Offset: $result - Time:$Remotetime - ReferenceTime: $Referencetime" #If result is a negative number (ie -6 seconds) convert to positive number # for easy comparison @@ -81,28 +93,31 @@ function Test-ADInternalTimeSync { #test if result is greater than max time drift If ($result -gt $MaxTimeDrift) { $emailOutput = "$server - Offset: $result - Time:$Remotetime - ReferenceTime: $Referencetime `r`n " - Write-Verbose "ALERT - Time drift above maximum allowed threshold on - $server - $emailOutput" + Write-Verbose -Message "ALERT - Time drift above maximum allowed threshold on - $server - $emailOutput" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17030 -EntryType Warning -message "FAILURE Internal time drift above maximum allowed on $emailOutput `r`n " -category "17030" #attempt to automatically fix the issue Invoke-Command -ComputerName $server -ScriptBlock { 'w32tm /resync' } Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17035 -EntryType Information -message "REPAIR Internal Time Sync remediation was attempted `r`n " -category "17035" - CurrentFailure = $true + $CurrentFailure = $true Send-Mail $emailOutput - Write-Verbose "Sending Slack Alert" - New-SlackPost "Alert - Internal Time drift above max threashold - $emailOutput" + Write-Verbose -Message "Sent email notification for Internal Time Sync Discrepancy" + #Write-Verbose -Message "Sending Slack Alert" + #New-SlackPost "Alert - Internal Time drift above max threashold - $emailOutput" }#end if - If (!$CurrentFailure) { + If (-not $CurrentFailure) { Write-Verbose "No Issues found in this run" $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | where {($_.InstanceID -Match "17030")} - $errtext = $InError |out-string + $errtext = $InError | Out-String + Write-Verbose -Message "$errtext" If ($errtext -like "*$server*") { - Write-Verbose "Previous Errors Seen" + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared - Send-AlertCleared - Write-Verbose "Sending Slack Message - Alert Cleared" - New-SlackPost "The previous alert, for AD Internal Time Sync, has cleared." + Send-AlertCleared -InError $InError + Write-Verbose -Message "Sent Alert Cleared notification for Internal Time Sync Discrepancy recovery" + #Write-Verbose -Message "Sending Slack Message - Alert Cleared" + #New-SlackPost "The previous alert, for AD Internal Time Sync, has cleared." #Write-Output $InError }#End if @@ -113,6 +128,7 @@ function Test-ADInternalTimeSync { }#End Process End { + Write-Verbose -Message "Finished processing all Domain Controllers for Internal Time Sync" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17033 -EntryType Information -message "END of Internal Time Sync Test Cycle ." -category "17033" }#End End diff --git a/PSADHealth/Public/Test-ADObjectReplication.ps1 b/PSADHealth/Public/Test-ADObjectReplication.ps1 index 2e17482..b18f263 100644 --- a/PSADHealth/Public/Test-ADObjectReplication.ps1 +++ b/PSADHealth/Public/Test-ADObjectReplication.ps1 @@ -35,6 +35,14 @@ function Test-ADObjectReplication { 17017 - Test Object Deleted 17018 - 1 minute Sleep 17019 - Posible old object detected + + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Primarily adding -Message for good code hygiene and expanding any aliases + Added a few more verbose statements for derived information + All in all, this function already had pretty good verbosity + Send-AlertCleared was not passing the $InError variable (I think this is a Script to Function issue). Corrected #> Begin { @@ -203,7 +211,7 @@ function Test-ADObjectReplication { Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared - Send-AlertCleared + Send-AlertCleared -InError $InError #Write-Verbose "Sending Slack Message - Alert Cleared" #New-SlackPost "The previous alert, for AD Object Replication, has cleared." #Write-Output $InError diff --git a/PSADHealth/Public/Test-ADReplication.ps1 b/PSADHealth/Public/Test-ADReplication.ps1 index 00c4258..15b6417 100644 --- a/PSADHealth/Public/Test-ADReplication.ps1 +++ b/PSADHealth/Public/Test-ADReplication.ps1 @@ -27,40 +27,54 @@ function Test-ADReplication { 17022 - Testing individual systems 17023 - End of test 17024 - Alert Email Sent + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Primarily adding -Message for good code hygiene and expanding any aliases + Added some additional Verbose statements + Implemented best practice for comparing to $null + Send-AlertCleared was not passing the $InError variable (I think this is a Script to Function issue). Corrected #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $null = Get-ADConfig $SupportArticle = $Configuration.SupportArticle - if (![System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { - write-verbose "Adding Event Source." + if (-not ([System.Diagnostics.EventLog]::SourceExists("PSMonitor"))) { + write-verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" } #$DClist = (Get-ADGroupMember -Identity 'Domain Controllers').name #For RWDCs only, RODCs are not in this group. $DClist = (Get-ADDomainController -Filter *).name # For ALL DCs + Write-Verbose -Message "DCList: $DCList" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17021 -EntryType Information -message "START AD Replication Test Cycle ." -category "17021" }#End Begin Process { Foreach ($server in $DClist) { Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17022 -EntryType Information -message "CHECKING AD Replication - Server - $server" -category "17022" - Write-Verbose "TESTING - $server" + Write-Verbose -Message "TESTING - $server" $OutputDetails = $null $Result = (Get-ADReplicationFailure -Target $server).failurecount - Write-Verbose "$server - $Result" + Write-Verbose -Message "$server - $Result" $Details = Get-ADReplicationFailure -Target $server $errcount = $Details.FailureCount + Write-Verbose -Message "--Error Count : $errcount" $name = $Details.server + Write-Verbose -Message "--Name : $name" $Fail = $Details.FirstFailureTime + Write-Verbose -Message "--FirstFailure: $Fail" $Partner = $Details.Partner + Write-Verbose -Message "--Partner : $Partner" - If ($result -ne $null -and $Result -gt 1) { + If ($null -ne $result -and $Result -gt 1) { $OutputDetails = "ServerName: `r`n $name `r`n FailureCount: $errcount `r`n `r`n FirstFailureTime: `r`n $Fail `r`n `r`n Error with Partner: `r`n $Partner `r`n `r`n - See the following support article $SupportArticle" - Write-Verbose "Failure - $OutputDetails" + Write-Verbose -Message "Failure - $OutputDetails" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17020 -EntryType Warning -message "FAILURE AD Replicaion on $server - $OutputDetails ." -category "17020" $global:CurrentFailure = $true Send-Mail $OutputDetails + Write-Verbose -Message "Sent email notification for $name replication failure with $Partner" #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - FAILURE AD Replicaion on $server - $OutputDetails ." } #End if @@ -71,17 +85,18 @@ function Test-ADReplication { End { Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17023 -EntryType Information -message "END of AD Replication Test Cycle ." -category "17023" If (!$CurrentFailure){ - Write-Verbose "No Issues found in this run" - $InError = Get-EventLog application -After (Get-Date).AddHours(-1) | where {($_.InstanceID -Match "17020")} + Write-Verbose -Message "No Issues found in this run" + $InError = Get-EventLog application -After (Get-Date).AddHours(-1) | Where-Object {($_.InstanceID -Match "17020")} If ($InError.Count -gt 1) { - Write-Verbose "Previous Errors Seen" + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert - #No errors foun during this test so send email that the previous error(s) have cleared - Send-AlertCleared - #Write-Verbose "Sending Slack Message - Alert Cleared" + #No errors found during this test so send email that the previous error(s) have cleared + Send-AlertCleared -InError $InError + Write-Verbose -Message "Sent email notification for cleared replication failure" + #Write-Verbose -Message "Sending Slack Message - Alert Cleared" #New-SlackPost "The previous alert, for AD Replication, has cleared." #Write-Output $InError }#End if }#End if }#End End -}#End Function \ No newline at end of file +}#End Function diff --git a/PSADHealth/Public/Test-ADServices.ps1 b/PSADHealth/Public/Test-ADServices.ps1 index aa36637..abea2b6 100644 --- a/PSADHealth/Public/Test-ADServices.ps1 +++ b/PSADHealth/Public/Test-ADServices.ps1 @@ -20,10 +20,12 @@ function Test-ADServices { #> begin { - Import-Module ActiveDirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" #Creates a global $configuration variable $null = Get-ADConfig $DClist = (Get-ADGroupMember "Domain Controllers").name + Write-Verbose -Message "DCList: $DCList" $Collection = @('ADWS', 'DHCPServer', 'DNS', @@ -39,20 +41,25 @@ function Test-ADServices { 'RPCSS', 'SAMSS', 'W32Time') + Write-Verbose -Message "Services to test: $Collection" $ServiceFilter = ($Collection | ForEach-Object { "name='$_'" }) -join " OR " $ServiceFilter = "State='Stopped' and ($ServiceFilter)" + Write-Verbose -Message "ServiceFilter: $ServiceFilter" } process { try { - $services = Get-CimInstance Win32_Service -filter $ServiceFilter -Computername $DClist -ErrorAction Stop + Write-Verbose -Message "Querying all Domain Controllers for Filtered list of essential services" + $services = Get-CimInstance Win32_Service -filter $ServiceFilter -Computername $DClist -ErrorAction Stop -Verbose:$false + Write-Verbose -Message "Finished querying all Domain Controllers" } catch { Out-Null + Write-Verbose -Message "Failed to query any of the servers. Get-CimInstance didn't work" } foreach ($service in $services) { - $Subject = "Windows Service: $($service.Displayname), is stopped on $service.PSComputerName " + $Subject = "Windows Service: $($service.Displayname), is stopped on $($service.PSComputerName)" $EmailBody = @" Service named $($service.Displayname) is stopped! Time of Event: """$((get-date))"""
@@ -68,6 +75,7 @@ function Test-ADServices { BodyAsHtml = $true } Send-MailMessage @mailParams + Write-Verbose -Message "Sent email notification for stopped service ($($service.DisplayName)) on $($service.PSComputerName)" } } #Process } #function diff --git a/PSADHealth/Public/Test-SYSVOL-Replication.ps1 b/PSADHealth/Public/Test-SYSVOLReplication.ps1 similarity index 63% rename from PSADHealth/Public/Test-SYSVOL-Replication.ps1 rename to PSADHealth/Public/Test-SYSVOLReplication.ps1 index bc9f874..3fbcdf8 100644 --- a/PSADHealth/Public/Test-SYSVOL-Replication.ps1 +++ b/PSADHealth/Public/Test-SYSVOLReplication.ps1 @@ -41,47 +41,73 @@ function Test-SysvolReplication { 17007 - Test Object Deleted 17008 - 1 minute Sleep 17009 - Alert Email Sent + + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Primarily adding -Message for good code hygiene and expanding any aliases + File name verse function name is inconsistent. Renamed file to be consistent with function name + There were some redundant variables that I have reconciled (commenting out old in case I am missing a reason for something being the way it was) + If it fails to create the test object or the PDCE isn't online, the script just exits silently (since Slack isn't configured/enabled) + We probably should notify more than just Write-Verbose in that case (just in case people aren't monitoring the event log...) + Used a modified version of the Sent-Mail $Alert used further down in the script (I haven't investigated that Private function yet) + Send-AlertCleared was not passing the $InError variable (I think this is a Script to Function issue). Corrected #> Begin { - Import-Module activedirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" $null = Get-ADConfig $SupportArticle = $Configuration.SupportArticle - if (![System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { - write-verbose "Adding Event Source." + if (-not [System.Diagnostics.EventLog]::SourceExists("PSMonitor")) { + write-verbose -Message "Adding Event Source." New-EventLog -LogName Application -Source "PSMonitor" } $continue = $true $CurrentFailure = $false $domainname = (Get-ADDomain).dnsroot + Write-Verbose -Message "Domain : $domainname" $DCList = (Get-ADDomainController -Filter *).name + Write-Verbose -Message "DCList : $DCList" $SourceSystem = (Get-ADDomain).pdcemulator + Write-Verbose -Message "PDCEmulator: $SourceSystem" [int]$MaxCycles = $Configuration.MaxSysvolReplCycles + Write-Verbose -Message "Max Cycles : $MaxCycles" } Process { if ($(Test-NetConnection $SourceSystem -Port 445).TcpTestSucceeded) { - Write-Verbose 'PDCE is online' + Write-Verbose -Message 'PDCE is online' $TempObjectLocation = "\\$SourceSystem\SYSVOL\$domainname\Scripts" + Write-Verbose -Message "Temp Object Location: $TempObjectLocation" $tempObjectName = "sysvolReplTempObject" + (Get-Date -f yyyyMMddHHmmss) + ".txt" - $objectPath = "\\$SourceSystem\SYSVOL\$domainname\Scripts\$tempObjectName" - "...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." | Out-File -FilePath $($TempObjectLocation + "\" + $tempObjectName) + Write-Verbose -Message "Temp Object Name : $tempObjectName" + #$objectPath = "\\$SourceSystem\SYSVOL\$domainname\Scripts\$tempObjectName" + $objectPath = Join-Path -Path $TempObjectLocation -ChildPath $tempObjectName + "...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." | Out-File -FilePath $objectPath # $($TempObjectLocation + "\" + $tempObjectName) #Redundant $site = (Get-ADDomainController $SourceSystem).site Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17006 -EntryType Information -message "CREATE SYSVOL Test object - $tempObjectName - has been created on $SourceSystem in site - $site" -category "17006" Start-Sleep 30 - If (!(Test-Path -Path $objectPath)){ - Write-Verbose "Object wasn't created properly, trying a second time" + If (-not (Test-Path -Path $objectPath)){ + Write-Verbose -Message "Object wasn't created properly, trying a second time" $tempObjectName = "sysvolReplTempObject" + (Get-Date -f yyyyMMddHHmmss) + ".txt" - $objectPath = "\\$SourceSystem\SYSVOL\$domainname\Scripts\$tempObjectName" - "...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." | Out-File -FilePath $($TempObjectLocation + "\" + $tempObjectName) + #$objectPath = "\\$SourceSystem\SYSVOL\$domainname\Scripts\$tempObjectName" + $objectPath = Join-Path -Path $TempObjectLocation -ChildPath $tempObjectName + "...!!!...TEMP OBJECT TO TEST AD REPLICATION LATENCY/CONVERGENCE...!!!..." | Out-File -FilePath $objectPath # $($TempObjectLocation + "\" + $tempObjectName) Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17006 -EntryType Information -message "CREATE SYSVOL Test object attempt Number 2 - $tempObjectName - has been created on $SourceSystem in site - $site" -category "17006" Start-Sleep 30 } - If (!(Test-Path -Path $objectPath)){ - Write-Verbose "Object wasn't created properly after 2 tries, exiting..." + If (-not (Test-Path -Path $objectPath)){ + Write-Verbose -Message "Object wasn't created properly after 2 tries, exiting..." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17000 -EntryType Error -message "FAILURE to write SYSVOL test object to PDCE - $SourceSystem in site - $site" -category "17000" + #We should say something urgent + $Alert = "In $domainname - Unable to create test Object in SYSVOL (failed twice). + Please see the following support article $SupportArticle to help investigate. + Review the event log for relevant events: PSMonitor 17000-17009" + + Send-Mail $Alert #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - FAILURE to write SYSVOL test object to PDCE - $SourceSystem in site - $site" Exit @@ -89,33 +115,39 @@ function Test-SysvolReplication { $startDateTime = Get-Date $i = 0 - } + } #end if PDC Emulator is available else { - Write-Verbose 'PDCE is offline. You should really resolve that before continuing.' + Write-Verbose -Message 'PDCE is offline. You should really resolve that before continuing.' Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17000 -EntryType Error -message "FAILURE to connect to PDCE - $SourceSystem in site - $site" -category "17000" + $Alert = "In $domainname - PDC Emulator is offline. You should really resolve that before continuing. + Please see the following support article $SupportArticle to help investigate. + Review the event log for relevant events: PSMonitor 17000-17009" + + Send-Mail $Alert #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - FAILURE to connect to PDCE - $SourceSystem in site - $site" Exit - } + } #end else PDC Emulator is available While ($continue) { $i++ - Write-Verbose 'Sleeping for 1 minute.' + Write-Verbose -Message 'Sleeping for 1 minute.' Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17008 -EntryType Information -message "SLEEPING SYSVOL test for 1 minute" -category "17008" Start-Sleep 60 $replicated = $true - Write-Verbose "Cycle - $i" + Write-Verbose -Message "Cycle - $i" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17001 -EntryType Information -message "CHECKING SYSVOL ADRepl Cycle $i" -category "17001" Foreach ($dc in $DCList) { $site = (Get-ADDomainController $dc).site + Write-Verbose -Message "Testing $dc in $site" if ($(Test-NetConnection $dc -Port 445).TcpTestSucceeded) { - Write-Verbose "Online - $dc" + Write-Verbose -Message "Online - $dc" $objectPath = "\\$dc\SYSVOL\$domainname\Scripts\$tempObjectName" $connectionResult = "SUCCESS" } else { - Write-Verbose "!!!!!OFFLINE - $dc !!!!!" + Write-Verbose -Message "!!!!!OFFLINE - $dc !!!!!" $connectionResult = "FAILURE" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17000 -EntryType Error -message "FAILURE to connect to DC - $dc in site - $site" -category "17000" } @@ -123,12 +155,12 @@ function Test-SysvolReplication { If ($connectionResult -eq "SUCCESS") { If (Test-Path -Path $objectPath) { # If The Temp Object Already Exists - Write-Verbose " - Object [$tempObjectName] Now Does Exist In The NetLogon Share" + Write-Verbose -Message " - Object [$tempObjectName] Now Does Exist In The NetLogon Share" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17003 -EntryType Information -message "SUCCESS SYSVOL Object Successfully replicated to - $dc in site - $site" -category "17003" } Else { # If The Temp Object Does Not Yet Exist - Write-Verbose " - Object [$tempObjectName] Does NOT Exist Yet In The NetLogon Share" + Write-Verbose -Message " - Object [$tempObjectName] Does NOT Exist Yet In The NetLogon Share" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17002 -EntryType Information -message "PENDING SYSVOL Object replication pending for - $dc in site - $site" -category "17002" $replicated = $false } @@ -136,10 +168,10 @@ function Test-SysvolReplication { # If The Connection To The DC Is Unsuccessful If ($connectionResult -eq "FAILURE") { - Write-Verbose " - Unable To Connect To DC/GC And Check For The Temp Object..." + Write-Verbose -Message " - Unable To Connect To DC/GC And Check For The Temp Object..." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17000 -EntryType Error -message "FAILURE to connect to DC - $dc in site - $site" -category "17000" } - } + } #end foreach DC If ($replicated) { $continue = $false } @@ -148,9 +180,9 @@ function Test-SysvolReplication { $continue = $false #gather event history to see which DC did, and which did not, get the replication $list = Get-EventLog application -After (Get-Date).AddHours(-2) | where {($_.InstanceID -Match "17002") -OR ($_.InstanceID -Match "17003") -OR ($_.InstanceID -Match "17006")} - $RelevantEvents = $list |Select InstanceID,Message |Out-String + $RelevantEvents = $list | Select-Object InstanceID,Message | Out-String - Write-Verbose "Cycle has run $i times, and replication hasn't finished. Need to generate an alert." + Write-Verbose -Message "Cycle has run $i times, and replication hasn't finished. Need to generate an alert." Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17004 -EntryType Warning -message "INCOMPLETE SYSVOL Test cycle has run $i times without the object succesfully replicating to all DCs" -category "17004" $Alert = "In $domainname - the SYSVOL test cycle has run $i times without the object succesfully replicating to all DCs. Please see the following support article $SupportArticle to help investigate @@ -160,6 +192,7 @@ function Test-SysvolReplication { " $CurrentFailure = $true Send-Mail $Alert + Write-Verbose -Message "Sent notification about SYSVOL Replication Test" #Write-Verbose "Sending Slack Alert" #New-SlackPost "Alert - Incomplete SYSVOL Replication Cycle in the domain: $domainname" } @@ -175,28 +208,28 @@ function Test-SysvolReplication { $output = $output + "`n Duration........: $duration Seconds" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17005 -EntryType Information -message "END of SYSVOL Test cycle - $output" -category "17005" - Write-Verbose "`n Start Time......: $(Get-Date $startDateTime -format "yyyy-MM-dd HH:mm:ss")" - Write-Verbose " End Time........: $(Get-Date $endDateTime -format "yyyy-MM-dd HH:mm:ss")" - Write-Verbose " Duration........: $duration Seconds" + Write-Verbose -Message "`n Start Time......: $(Get-Date $startDateTime -format "yyyy-MM-dd HH:mm:ss")" + Write-Verbose -Message " End Time........: $(Get-Date $endDateTime -format "yyyy-MM-dd HH:mm:ss")" + Write-Verbose -Message " Duration........: $duration Seconds" # Delete The Temp Object On The RWDC - Write-Verbose " Deleting Temp Text File..." + Write-Verbose -Message " Deleting Temp Text File..." Remove-Item "$TempObjectLocation\$tempObjectName" -Force - Write-Verbose " Temp Text File [$tempObjectName] Has Been Deleted On The Source System" + Write-Verbose -Message " Temp Text File [$tempObjectName] Has Been Deleted On The Source System" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17007 -EntryType Information -message "DELETED SYSVOL Test object - $tempObjectName - has been deleted." -category "17007" - If (!$CurrentFailure){ - Write-Verbose "No Issues found in this run" + If (-not $CurrentFailure){ + Write-Verbose -Message "No Issues found in this run" $InError = Get-EventLog application -After (Get-Date).AddHours(-2) | where {($_.InstanceID -Match "17000") -or ($_.InstanceID -Match "17004")} If ($InError) { - Write-Verbose "Previous Errors Seen" + Write-Verbose -Message "Previous Errors Seen" #Previous run had an alert #No errors foun during this test so send email that the previous error(s) have cleared - Send-AlertCleared + Send-AlertCleared -InError $InError #Write-Verbose "Sending Slack Message - Alert Cleared" #New-SlackPost "The previous alert, for AD SYSVOL Replication, has cleared." #Write-Output $InError }#End if }#End if }#End End -} \ No newline at end of file +} From 161c1f7e9ebb19dcc8b66c9a87c461292a515564 Mon Sep 17 00:00:00 2001 From: Charles Palmer Date: Fri, 29 May 2020 21:40:48 -0400 Subject: [PATCH 4/5] Verbosity and consistency updates --- PSADHealth/Config/ADConfig.json | 40 ++++++++-------- PSADHealth/Private/Send-AlertCleared.ps1 | 46 +++++++++++++++---- PSADHealth/Private/Send-Mail.ps1 | 34 +++++++++++--- PSADHealth/Public/Get-DCDiskSpace.ps1 | 33 +++++++++++-- PSADHealth/Public/Set-PSADHealthConfig.ps1 | 28 +++++++++-- PSADHealth/Public/Test-ADInternalTimeSync.ps1 | 2 +- 6 files changed, 140 insertions(+), 43 deletions(-) diff --git a/PSADHealth/Config/ADConfig.json b/PSADHealth/Config/ADConfig.json index 497eba2..7b1e517 100644 --- a/PSADHealth/Config/ADConfig.json +++ b/PSADHealth/Config/ADConfig.json @@ -1,21 +1,21 @@ { - "SMTPServer": "smtp.foobar.com", - "MailFrom": "ad_status_alert@foobar.com", - "MailTo": [ - "ceo@foobar.com", - "supreme_it_leader@foobar.com" - ], - "MaxDaysSinceBackup": "1", - "MaxIntTimeDrift": "45", - "MaxExtTimeDrift": "15", - "ExternalTimeSvr": "time.google.com", - "ExternalDNSServers": [ - "208.67.222.222", - "208.67.220.220" - ], - "FreeDiskThreshold": "70", - "MaxObjectReplCycles": "50", - "MaxSysvolReplCycles": "50", - "SupportArticle": "https://help.foobar.com/YourTroubleshootingArticles", - "SlackToken": "2343-woeij2-jwoeij223-b2334322" -} \ No newline at end of file + "SMTPServer": "smtp.foobar.com", + "MailFrom": "ad_status_alert@foobar.com", + "MailTo": [ + "ceo@foobar.com", + "supreme_it_leader@foobar.com" + ], + "MaxDaysSinceBackup": "1", + "MaxIntTimeDrift": "45", + "MaxExtTimeDrift": "15", + "ExternalTimeSvr": "time.google.com", + "ExternalDNSServers": [ + "208.67.222.222", + "208.67.220.220" + ], + "FreeDiskThreshold": "70", + "MaxObjectReplCycles": "50", + "MaxSysvolReplCycles": "50", + "SupportArticle": "https://help.foobar.com/YourTroubleshootingArticles", + "SlackToken": "2343-woeij2-jwoeij223-b2334322" +} diff --git a/PSADHealth/Private/Send-AlertCleared.ps1 b/PSADHealth/Private/Send-AlertCleared.ps1 index 7b8e68f..234409a 100644 --- a/PSADHealth/Private/Send-AlertCleared.ps1 +++ b/PSADHealth/Private/Send-AlertCleared.ps1 @@ -1,12 +1,41 @@ function Send-AlertCleared { + <# + .SYNOPSIS + Take InError as input and determines calling function to send email to notify that a formerly failing problem has cleared + + .DESCRIPTION + Take InError as input and determines calling function to send email to notify that a formerly failing problem has cleared + + .EXAMPLE + PS C:\> Send-AlertCleared -InError + Loads configuration from the parent scope $Configuration and then sends an SMTP email with the InError text as part of the body + + .NOTES + Updated: 05/29/2020 + Cleaned up formatting a little bit + Added #Requires -Modules ActiveDirectory + Added a Comment Help section + Made this an advance function by adding [cmdletbinding()] so that Verbose will work + This function was written for Test-ADInternalTimeSync function but is being used by 3 other functions currently (Test-ADObjectReplication, Test-ADReplication, Test-SysvolReplication) + I defined $CallingFunction by getting the name of the function that called Send-AlertCleared (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Name + I then modified the email subject and body to utilize this variable so that the clear message will make more sense. I also include the $InError parameter in the body of the email + + #> + [cmdletbinding()] Param($InError) - Write-Verbose "Sending Email" - Write-Verbose "Output is -- $InError" + #Requires -Modules ActiveDirectory + $CallingFunction = (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Name + Write-Verbose -Message "Calling Function: $CallingFunction" + Write-Verbose -Message "Sending Email" + Write-Verbose -Message "Output is -- $InError" #Mail Server Config $NBN = (Get-ADDomain).NetBIOSName + Write-Verbose -Message "NetBIOSName: $NBN" $Domain = (Get-ADDomain).DNSRoot + Write-Verbose -Message "Domain : $Domain" $smtpServer = $Configuration.SMTPServer + Write-Verbose -Message "SMTPServer : $smtpServer" $smtp = new-object Net.Mail.SmtpClient($smtpServer) $msg = new-object Net.Mail.MailMessage @@ -15,23 +44,24 @@ function Send-AlertCleared { If ($emailCount -gt 0){ $Emails = $Configuration.MailTo foreach ($target in $Emails){ - Write-Verbose "email will be sent to $target" - $msg.To.Add("$target") + Write-Verbose -Message "email will be sent to $target" + $msg.To.Add("$target") } } Else{ - Write-Verbose "No email addresses defined" + Write-Verbose -Message "No email addresses defined" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17030 -EntryType Error -message "ALERT - No email addresses defined. Alert email can't be sent!" -category "17030" } #Message: $msg.From = $Configuration.MailFrom $msg.ReplyTo = $Configuration.MailFrom - $msg.subject = "$NBN AD Internal Time Sync - Alert Cleared!" + $msg.subject = "$NBN - $CallingFunction - Alert Cleared!" $msg.body = @" - The previous Internal AD Time Sync alert has now cleared. + $InError + The previous $CallingFunction alert has now cleared. Thanks. "@ #Send it $smtp.Send($msg) -} \ No newline at end of file +} diff --git a/PSADHealth/Private/Send-Mail.ps1 b/PSADHealth/Private/Send-Mail.ps1 index d306321..20ad7bb 100644 --- a/PSADHealth/Private/Send-Mail.ps1 +++ b/PSADHealth/Private/Send-Mail.ps1 @@ -1,4 +1,21 @@ function Send-Mail { + <# + .SYNOPSIS + Takes provided text block and sends an email using configuration values + .DESCRIPTION + Takes provided text block and sends an email using configuration values + .EXAMPLE + PS C:\> Send-Mail -emailOutput + Will email a message to configured recipients with provided in the body of the message + .NOTES + Updated: 05/29/2020 + Cleaned up formatting a little bit + Added #Requires -Modules ActiveDirectory + Addded Comment Help section + This function was written for AD Internal Time Sync function but is being used by 3 other functions currently (Test-ADObjectReplication, Test-ADReplication, Test-SysvolReplication) + I defined $CallingFunction by getting the name of the function that called Send-AlertCleared (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Name + I then modified the email subject and body to utilize this variable so that the clear message will make more sense. I also include the $InError parameter in the body of the email + #> [cmdletBinding()] Param( [Parameter(Mandatory,ValueFromPipeline)] @@ -6,13 +23,18 @@ function Send-Mail { $emailOutput ) - Write-Verbose "Sending Email" + #Requires -Modules ActiveDirectory + $CallingFunction = (Get-Variable -Name MyInvocation -Scope 1).Value.MyCommand.Name + Write-Verbose -Message "Calling Function: $CallingFunction" + Write-Verbose -Message "Sending Email" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17034 -EntryType Information -message "ALERT Email Sent" -category "17034" - Write-Verbose "Output is -- $emailOutput" + Write-Verbose -Message "Output is -- $emailOutput" #Mail Server Config $NBN = (Get-ADDomain).NetBIOSName + Write-Verbose -Message "NetBIOSName: $NBN" $Domain = (Get-ADDomain).DNSRoot + Write-Verbose -Message "Domain : $Domain" #Send to list: @@ -21,13 +43,13 @@ function Send-Mail { If ($emailCount -gt 0){ $Emails = $Configuration.MailTo foreach ($target in $Emails){ - Write-Verbose "email will be sent to $target" - $msg.To.Add("$target") + Write-Verbose -Message "email will be sent to $target" + $msg.To.Add("$target") } } Else{ - Write-Verbose "No email addresses defined" + Write-Verbose -Message "No email addresses defined" Write-eventlog -logname "Application" -Source "PSMonitor" -EventID 17030 -EntryType Error -message "ALERT - No email addresses defined. Alert email can't be sent!" -category "17030" } @@ -38,7 +60,7 @@ function Send-Mail { From = $Configuration.MailFrom ReplyTo = $Configuration.MailFrom SMTPServer = $Configuration.SMTPServer - Subject = "$NBN AD Internal Time Sync Alert!" + Subject = "$NBN - $CallingFunction Alert!" Body = @" Time of Event: $((get-date))`r`n $emailOutput See the following support article $SupportArticle diff --git a/PSADHealth/Public/Get-DCDiskSpace.ps1 b/PSADHealth/Public/Get-DCDiskSpace.ps1 index 12c8525..d2697ca 100644 --- a/PSADHealth/Public/Get-DCDiskSpace.ps1 +++ b/PSADHealth/Public/Get-DCDiskSpace.ps1 @@ -1,7 +1,7 @@ Function Get-DCDiskspace { [cmdletBinding()] Param( - [Parameter(Mandatory, Position = 0)] + [Parameter(Mandatory, Position = 0,HelpMessage="Please provide in 'C:' format")] [String] $DriveLetter ) @@ -12,6 +12,9 @@ Function Get-DCDiskspace { .DESCRIPTION This function is used to Monitor AD Domain Controller Disk space and send alerts if below the specified threshold + .PARAMETER DriveLetter + Provide the drive letter to be tested/monitored in 'C:' format (without the quotes) + .EXAMPLE Run as a scheduled task on a tool server to remotely monitor disk space on all DCs in a specified domain. @@ -21,24 +24,42 @@ Function Get-DCDiskspace { Version: 0.0.5 Version Date: 10/30/2019 + Updated: 05/29/2020 + Silenced the import of ActiveDirectory module because we don't really want to see that + Added "Silently loaded ActiveDirectory module" statement in its place + Added some Verbose statements for when troubleshooting or testing + Default free space in the config file is 70. If you don't have at least 70% free, this will alert + I think expecting 70% free on all DCs is excessive personally + I think the config file should be changed to 30% free as a default + Also, there is not an option in Set-PSADHealthConfig for this value (Yet!) + Instead of Making DriveLetter a Mandatory Parameter, shouldn't it just default to C:? + When I went to use it the first time, I just provided 'C' instead of 'C:' so it failed to find my disk + Either that or provide a help message for the proper format for ease of discovery #> begin { - Import-Module ActiveDirectory + Import-Module ActiveDirectory -Verbose:$false + Write-Verbose -Message "Silently loaded ActiveDirectory module" #Creates a global $configuration variable $null = Get-ADConfig } process { $DClist = (get-adgroupmember "Domain Controllers").name + Write-Verbose -Message "DCList: $DCList" $FreeDiskThreshold = $Configuration.FreeDiskThreshold + Write-Verbose -Message "Expected Minimum Free Disk Space: $FreeDiskThreshold" ForEach ($server in $DClist) { - + Write-Verbose -Message "Processing $server" $disk = Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType=3" -ComputerName $server | Where-Object { $_.DeviceId -eq $DriveLetter } + Write-Verbose -Message "Disk : $($disk.Name)" $Size = (($disk | Measure-Object -Property Size -Sum).sum / 1gb) + Write-Verbose -Message "DiskSize : $Size" $FreeSpace = (($disk | Measure-Object -Property FreeSpace -Sum).sum / 1gb) + Write-Verbose -Message "FreeSpace : $FreeSpace" $freepercent = [math]::round(($FreeSpace / $size) * 100, 0) + Write-Verbose -Message "FreePercent: $freepercent" $Diskinfo = [PSCustomObject]@{ Drive = $disk.Name "Total Disk Size (GB)" = [math]::round($size, 2) @@ -69,7 +90,7 @@ Function Get-DCDiskspace { BodyAsHtml = $true } Send-MailMessage @mailParams - + Write-Verbose -Message "Sent email notification about low free disk space on $Server" } #End If @@ -77,6 +98,8 @@ Function Get-DCDiskspace { } - end { } + end { + Write-Verbose -Message "Finished check Free Disk Space for all Domain Controllers" + } } \ No newline at end of file diff --git a/PSADHealth/Public/Set-PSADHealthConfig.ps1 b/PSADHealth/Public/Set-PSADHealthConfig.ps1 index fd914fc..1bdd797 100644 --- a/PSADHealth/Public/Set-PSADHealthConfig.ps1 +++ b/PSADHealth/Public/Set-PSADHealthConfig.ps1 @@ -12,6 +12,10 @@ function Set-PSADHealthConfig The smtp server this module will use for reports. + .PARAMETER ExternalDNSServers + + Provide an array of servers: @('1.2.3.4', '4.3.2.1') + .EXAMPLE Set-PSADHealthConfig -SMTPServer email.company.com @@ -21,14 +25,18 @@ function Set-PSADHealthConfig .EXAMPLE Set-PSADHealthConfig -MaxDaysSinceBackup 12 - + .NOTES + Updated: 05/29/2020 + Added FreeDiskThreshold parameter and setting update + Added ExternalDNSServers parameter, setting update and PARAMETER help message + Modified assignment to PSADHealthConfigPath parameter so that it will work with Public folder during development (and should still work fine in production) #> [cmdletBinding()] Param( [Parameter(Position=0)] - $PSADHealthConfigPath = "$($PSScriptRoot)\Config\ADConfig.json", + $PSADHealthConfigPath = ("$($PSScriptRoot)\Config\ADConfig.json").Replace('Public\',''), [Parameter(ValueFromPipeline,ValueFromPipelineByPropertyName)] [string] @@ -58,6 +66,14 @@ function Set-PSADHealthConfig [string] $ExternalTimeServer, + [Parameter()] + [array] + $ExternalDNSServers, + + [Parameter()] + [Int] + $FreeDiskThreshold, + [Parameter()] [Int] $MaxObjectReplCycles, @@ -100,6 +116,12 @@ function Set-PSADHealthConfig 'ExternalTimeServer' { $config.ExternalTimeSvr = $ExternalTimeServer } + 'ExternalDNSServers' { + $config.ExternalDNSServers = $ExternalDNSServers + } + 'FreeDiskThreshold' { + $config.FreeDiskThreshold = $FreeDiskThreshold + } 'MaxObjectReplCycles' { $config.MaxObjectReplCycles = $MaxObjectReplCycles } @@ -117,4 +139,4 @@ function Set-PSADHealthConfig $config | ConvertTo-Json | Set-Content $PSADHealthConfigPath -} \ No newline at end of file +} diff --git a/PSADHealth/Public/Test-ADInternalTimeSync.ps1 b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 index 38147be..b6c75bf 100644 --- a/PSADHealth/Public/Test-ADInternalTimeSync.ps1 +++ b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 @@ -107,7 +107,7 @@ function Test-ADInternalTimeSync { }#end if If (-not $CurrentFailure) { Write-Verbose "No Issues found in this run" - $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | where {($_.InstanceID -Match "17030")} + $InError = Get-EventLog application -After (Get-Date).AddHours(-24) | Where-Object {($_.InstanceID -Match "17030")} $errtext = $InError | Out-String Write-Verbose -Message "$errtext" If ($errtext -like "*$server*") { From cb99070954ead2e7acd9ffcf6c4fbe02d5ebc98c Mon Sep 17 00:00:00 2001 From: Charles Palmer Date: Mon, 1 Jun 2020 18:36:13 -0400 Subject: [PATCH 5/5] Added additional examples, modified build to include sample script, further cleanup --- Build/build.ps1 | 5 +- Non-Module Scripts/New-TaskRegistration.ps1 | 42 +++++++++----- .../New-TaskRegistration.ps1 | 52 +++++++++++------- PSADHealth/PSADHealth.psd1 | Bin 9514 -> 9514 bytes PSADHealth/Public/Get-DCDiskSpace.ps1 | 7 +++ PSADHealth/Public/Test-ADExternalTimeSync.ps1 | 11 +++- PSADHealth/Public/Test-ADInternalTimeSync.ps1 | 10 +++- .../Public/Test-ADObjectReplication.ps1 | 8 +++ PSADHealth/Public/Test-ADReplication.ps1 | 10 +++- PSADHealth/Public/Test-ADServices.ps1 | 7 +++ PSADHealth/Public/Test-SYSVOLReplication.ps1 | 8 +++ 11 files changed, 121 insertions(+), 39 deletions(-) diff --git a/Build/build.ps1 b/Build/build.ps1 index ed08aba..301368b 100644 --- a/Build/build.ps1 +++ b/Build/build.ps1 @@ -62,6 +62,9 @@ if ($Compile.IsPresent) { "`$PublicFunctions = '$($Public.BaseName -join "', '")'" | Add-Content .\Output\PSADHealth.psm1 + #Let's add New-TaskRegistration.ps1 as part of Non-Module Scripts that get published with the module + Copy-Item -Path '.\Non-Module Scripts\*' -Filter '*.*' -Destination '.\Output\Non-Module Scripts' -Force + Remove-Item -Path .\PSADHealth -Recurse -Force Rename-Item -Path .\Output -NewName 'PSADHealth' @@ -111,4 +114,4 @@ if($Deploy.IsPresent) { } Catch { throw $_ } -} \ No newline at end of file +} diff --git a/Non-Module Scripts/New-TaskRegistration.ps1 b/Non-Module Scripts/New-TaskRegistration.ps1 index c9e003e..29576e0 100644 --- a/Non-Module Scripts/New-TaskRegistration.ps1 +++ b/Non-Module Scripts/New-TaskRegistration.ps1 @@ -30,34 +30,46 @@ alternatively you could use the following, or one of many other options: https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0 This is not an endorsement of those modules, just inclduing for awareness. + +Updated: 06/01/2020 + Removed old commented out command lines for running as scripts + Fixed Get-ADLastBackup to Get-ADLastBackupDate as that is the function name + Change Test-ADObjectReplication from every 2 hours (12 new computer objects created and deleted daily) to daily (1 computer object daily) + Added Register-ScheduledJob commands for the rest of the functions: + Get-DCDiskSpace - hourly + Test-ADServices - hourly + Test-DCsOnline - hourly + Test-ExternalDNSServers - hourly + Test-SRVRecords - hourly #> #Create the EventLog Source here that will be used by all the other scripts, so they don't need to be run as administrator. New-EventLog -LogName Application -Source "PSMonitor" -#Define the interval to repeat job -$trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely - #Get user credential so that the job has access to the network -$cred = Get-Credential -Credential DOMAIN\Serviceaccount +$cred = Get-Credential #-Credential DOMAIN\Serviceaccount #Set job options $opt = New-ScheduledJobOption -RunElevated -RequireNetwork -#Register-ScheduledJob -Name Test-InternalTimeSync -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADTimeSync.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADInternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADInternalTimeSync} -MaxResultCount 5 -scheduledjoboption $opt -#Register-ScheduledJob -Name Test-ExternalTimeSync -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADTimeSyncToExternalNTP.ps1" -MaxResultCount 5 -scheduledjoboption $opt +#Items scheduled to run Daily +#Define the interval to repeat job +$trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely +Register-ScheduledJob -name Test-ADInternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADInternalTimeSync } -MaxResultCount 5 -scheduledjoboption $opt Register-ScheduledJob -name Test-ADExternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADExternalTimeSync} -MaxResultCount 5 -scheduledjoboption $opt -#Register-ScheduledJob -Name Test-ADLastBackup -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADLastBackupDate.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Get-ADLastBackup -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Get-ADLastBackup} -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -name Get-ADLastBackupDate -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Get-ADLastBackupDate} -MaxResultCount 5 -scheduledjoboption $opt +#Shifting schedule half hour off +$trigger = New-JobTrigger -Once -At 6:30AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely +Register-ScheduledJob -name Test-ADObectReplication -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADObectReplication } -MaxResultCount 5 -scheduledjoboption $opt +#Items scheduled to run hourly $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADReplication.ps1" -MaxResultCount 5 -scheduledjoboption $opt Register-ScheduledJob -name Test-ADReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADReplication} -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Get-DCDiskSpace -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Get-DCDiskSpace } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-ADServices -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-ADServices } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-DCsOnline -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-DCsOnline } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-ExternalDNSServers -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-ExternalDNSServers } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-SRVRecords -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-SRVRecords } -MaxResultCount 5 -scheduledjoboption $opt -$trigger = New-JobTrigger -Once -At 6:30AM -RepetitionInterval (New-TimeSpan -Hours 2) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADObectReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADObjectReplication.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADObectReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADObectReplication} -MaxResultCount 5 -scheduledjoboption $opt - +#Items scheduled to run every two hours $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 2) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADSYSVOLReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-SYSVOL-Replication.ps1" -MaxResultCount 5 -scheduledjoboption $opt Register-ScheduledJob -name Test-SYSVOLReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-SYSVOLReplication} -MaxResultCount 5 -scheduledjoboption $opt diff --git a/PSADHealth/Non-Module Scripts/New-TaskRegistration.ps1 b/PSADHealth/Non-Module Scripts/New-TaskRegistration.ps1 index 8cff45f..838117e 100644 --- a/PSADHealth/Non-Module Scripts/New-TaskRegistration.ps1 +++ b/PSADHealth/Non-Module Scripts/New-TaskRegistration.ps1 @@ -4,8 +4,8 @@ .NOTES Author: Greg Onstot -Version: 0.3 -Version Date: 04/14/2019 +Version: 0.4 +Version Date: 04/26/2019 The expectation is that you run these scripts on a separate Tier0 Tool server, to monitor your AD. It must be a Tier0 systems as the service account monitoring AD should be in Domain Admin to perform a number of these tasks. @@ -30,34 +30,46 @@ alternatively you could use the following, or one of many other options: https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0 This is not an endorsement of those modules, just inclduing for awareness. + +Updated: 06/01/2020 + Removed old commented out command lines for running as scripts + Fixed Get-ADLastBackup to Get-ADLastBackupDate as that is the function name + Change Test-ADObjectReplication from every 2 hours (12 new computer objects created and deleted daily) to daily (1 computer object daily) + Added Register-ScheduledJob commands for the rest of the functions: + Get-DCDiskSpace - hourly + Test-ADServices - hourly + Test-DCsOnline - hourly + Test-ExternalDNSServers - hourly + Test-SRVRecords - hourly #> #Create the EventLog Source here that will be used by all the other scripts, so they don't need to be run as administrator. New-EventLog -LogName Application -Source "PSMonitor" -#Define the interval to repeat job -$trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely - #Get user credential so that the job has access to the network -$cred = Get-Credential -Credential DOMAIN\Serviceaccount +$cred = Get-Credential #-Credential DOMAIN\Serviceaccount #Set job options $opt = New-ScheduledJobOption -RunElevated -RequireNetwork -#Register-ScheduledJob -Name Test-InternalTimeSync -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADTimeSync.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADInternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADInternalTimeSync} -MaxResultCount 5 -scheduledjoboption $opt -#Register-ScheduledJob -Name Test-ExternalTimeSync -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADTimeSyncToExternalNTP.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADExternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADExternalTimeSync} -MaxResultCount 5 -scheduledjoboption $opt -#Register-ScheduledJob -Name Test-ADLastBackup -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADLastBackupDate.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Get-ADLastBackup -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Get-ADLastBackup} -MaxResultCount 5 -scheduledjoboption $opt +#Items scheduled to run Daily +#Define the interval to repeat job +$trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely +Register-ScheduledJob -name Test-ADInternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADInternalTimeSync } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -name Test-ADExternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADExternalTimeSync } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -name Get-ADLastBackupDate -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Get-ADLastBackupDate } -MaxResultCount 5 -scheduledjoboption $opt +#Shifting schedule half hour off +$trigger = New-JobTrigger -Once -At 6:30AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely +Register-ScheduledJob -name Test-ADObectReplication -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADObectReplication } -MaxResultCount 5 -scheduledjoboption $opt +#Items scheduled to run hourly $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADReplication.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADReplication} -MaxResultCount 5 -scheduledjoboption $opt - -$trigger = New-JobTrigger -Once -At 6:30AM -RepetitionInterval (New-TimeSpan -Hours 2) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADObectReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-ADObjectReplication.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADObectReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADObectReplication} -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -name Test-ADReplication -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-ADReplication } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Get-DCDiskSpace -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Get-DCDiskSpace } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-ADServices -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-ADServices } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-DCsOnline -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-DCsOnline } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-ExternalDNSServers -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-ExternalDNSServers } -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -Name Test-SRVRecords -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth); Test-SRVRecords } -MaxResultCount 5 -scheduledjoboption $opt +#Items scheduled to run every two hours $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 2) -RepeatIndefinitely -#Register-ScheduledJob -Name Test-ADSYSVOLReplication -Trigger $trigger -Credential $cred -FilePath "C:\Scripts\Test-SYSVOL-Replication.ps1" -MaxResultCount 5 -scheduledjoboption $opt -Register-ScheduledJob -name Test-ADSYSVOLReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -name PSADHealth) ; Test-ADSYSVOLReplication} -MaxResultCount 5 -scheduledjoboption $opt +Register-ScheduledJob -name Test-SYSVOLReplication -Trigger $trigger -Credential $cred -ScriptBlock { (Import-Module -name PSADHealth) ; Test-SYSVOLReplication } -MaxResultCount 5 -scheduledjoboption $opt diff --git a/PSADHealth/PSADHealth.psd1 b/PSADHealth/PSADHealth.psd1 index 440df0a85c356b26604db6802008bdb5c8d7b372..43989968c8814743a7e25b013bd4f99c8b48640a 100644 GIT binary patch delta 26 hcmZ4GwaRP51V!FZh608ZhD3%EhE#@>&2trdnE`ep2p<3d delta 26 gcmZ4GwaRP51Vvs?hCGH$h7ur7WXRb(SFx8F0Cn~VA^-pY diff --git a/PSADHealth/Public/Get-DCDiskSpace.ps1 b/PSADHealth/Public/Get-DCDiskSpace.ps1 index d2697ca..6e7c2fc 100644 --- a/PSADHealth/Public/Get-DCDiskSpace.ps1 +++ b/PSADHealth/Public/Get-DCDiskSpace.ps1 @@ -18,6 +18,13 @@ Function Get-DCDiskspace { .EXAMPLE Run as a scheduled task on a tool server to remotely monitor disk space on all DCs in a specified domain. + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Get-DCDiskspace -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Get-DCDiskspace} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Get-DCDiskspace on a hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege .NOTES Authors: Mike Kanakos, Greg Onstot diff --git a/PSADHealth/Public/Test-ADExternalTimeSync.ps1 b/PSADHealth/Public/Test-ADExternalTimeSync.ps1 index 95dcb3f..e2c95e2 100644 --- a/PSADHealth/Public/Test-ADExternalTimeSync.ps1 +++ b/PSADHealth/Public/Test-ADExternalTimeSync.ps1 @@ -13,7 +13,16 @@ function Test-ADExternalTimeSync { .EXAMPLE Run in verbose mode if you want on-screen feedback for testing - + + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADExternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADExternalTimeSync} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADExternalTimeSync on a daily basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + + .NOTES Authors: Mike Kanakos, Greg Onstot Version: 0.7.2 diff --git a/PSADHealth/Public/Test-ADInternalTimeSync.ps1 b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 index b6c75bf..7742c67 100644 --- a/PSADHealth/Public/Test-ADInternalTimeSync.ps1 +++ b/PSADHealth/Public/Test-ADInternalTimeSync.ps1 @@ -13,7 +13,15 @@ function Test-ADInternalTimeSync { .EXAMPLE Run in verbose mode if you want on-screen feedback for testing - + + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADInternalTimeSync -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADInternalTimeSync} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADInternalTimeSync on a daily basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES Authors: Mike Kanakos, Greg Onstot Version: 0.8.2 diff --git a/PSADHealth/Public/Test-ADObjectReplication.ps1 b/PSADHealth/Public/Test-ADObjectReplication.ps1 index b18f263..1bc9348 100644 --- a/PSADHealth/Public/Test-ADObjectReplication.ps1 +++ b/PSADHealth/Public/Test-ADObjectReplication.ps1 @@ -15,6 +15,14 @@ function Test-ADObjectReplication { .EXAMPLE Run in verbose mode if you want on-screen feedback for testing + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:30AM -RepetitionInterval (New-TimeSpan -Hours 24) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADObjectReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADObjectReplication} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADObjectReplication on a daily basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES Author Greg Onstot Version: 0.6.3 diff --git a/PSADHealth/Public/Test-ADReplication.ps1 b/PSADHealth/Public/Test-ADReplication.ps1 index 15b6417..3f6893d 100644 --- a/PSADHealth/Public/Test-ADReplication.ps1 +++ b/PSADHealth/Public/Test-ADReplication.ps1 @@ -13,7 +13,15 @@ function Test-ADReplication { .EXAMPLE Run in verbose mode if you want on-screen feedback for testing - + + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADReplication} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADReplication on a hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES Authors: Mike Kanakos, Greg Onstot Version: 0.6.2 diff --git a/PSADHealth/Public/Test-ADServices.ps1 b/PSADHealth/Public/Test-ADServices.ps1 index abea2b6..6e88f1e 100644 --- a/PSADHealth/Public/Test-ADServices.ps1 +++ b/PSADHealth/Public/Test-ADServices.ps1 @@ -12,6 +12,13 @@ function Test-ADServices { .EXAMPLE Run as a scheduled task on a tool server to remotely monitor service status on all DCs in a specified domain. + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 1) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-ADServices -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-ADServices} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-ADServices on a hourly basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege .NOTES Authors: Mike Kanakos, Greg Onstot diff --git a/PSADHealth/Public/Test-SYSVOLReplication.ps1 b/PSADHealth/Public/Test-SYSVOLReplication.ps1 index 3fbcdf8..f576b39 100644 --- a/PSADHealth/Public/Test-SYSVOLReplication.ps1 +++ b/PSADHealth/Public/Test-SYSVOLReplication.ps1 @@ -22,6 +22,14 @@ function Test-SysvolReplication { .EXAMPLE Run in verbose mode if you want on-screen feedback for testing + .EXAMPLE + PS C:\> $trigger = New-JobTrigger -Once -At 6:00AM -RepetitionInterval (New-TimeSpan -Hours 2) -RepeatIndefinitely + PS C:\> $cred = Get-Credential DOMAIN\ServiceAccount + PS C:\> $opt = New-ScheduledJobOption -RunElevated -RequireNetwork + PS C:\> Register-ScheduledJob -Name Test-SysvolReplication -Trigger $trigger -Credential $cred -ScriptBlock {(Import-Module -Name PSADHealth); Test-SysvolReplication} -MaxResultCount 5 -ScheduledJobOption $opt + + Creates a scheduled task to run Test-SysvolReplication on an every two hour basis. NOTE: Service account needs to be a Domain Admin or equivalent (Tier0) and must have the RunAsBatch and RunAsService privilege + .NOTES Author Greg Onstot This script must be run from a Win10, or Server 2016 system. It can target older OS Versions.