0

I adapted an AD replication powershell script I found online to include the code below:

function ExitWithCode {
param
(
    $exitcode
)

$host.SetShouldExit($exitcode)
exit
}

function Write-Log {
<#
.SYNOPSIS
    Write-Log writes a message to a logfile

.DESCRIPTION
    The Write-Log function is designed to add logging capability to other scripts. 
    In addition to writing output and/or verbose you can write to a log file for 
    later debugging. 
#>
[CmdletBinding()]
Param
(
    [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)]
    [ValidateNotNullOrEmpty()]
    [Alias('LogContent')]
    [string]$Message,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Error", "Info", "Status")]
    [string]$Level = "Info",

    [Parameter(Mandatory = $false)]
    [Alias('LogPath')]
    [string]$Path = 'C:\dataloop\ADHealthCheck.log'

)

BEGIN {

    [string]$FormattedDate = Get-Date -Format "dd-MM-yyyy HH:mm"

    If (-NOT (Test-Path $path)) {
        Write-Verbose "Creating $Path"
        [System.IO.FileInfo]$LogFile = New-Item $Path -Force -ItemType file
    }
}
PROCESS {   
    [string]$LogLine = "$FormattedDate - $Level - $message"
    $LogLine | Out-File -FilePath $Path -Append

    Switch ($Level) {

        "Info" {Write-Verbose $LogLine}
        "Status" {Write-Output $LogLine}
        "Error" {Write-Error $LogLine}
    }
}
END {}
}

function Get-ADHealthCheck {
    [CmdletBinding()]
        param()

        BEGIN {
        Write-Log "Beginning the AD Health Check..."
        }


        PROCESS {
        $DCs = Get-ADDomainController -Filter * |sort name

        Write-Log "$($DCs.Count) Domain Controllers found" -level Info

        $results = @()

        ForEach ($DC in $DCs) {

            Write-Log "Getting replication metadata for $($DC.HostName)" -level Status
            $ReplStatuses = Get-ADReplicationPartnerMetadata -target $DC.HostName -PartnerType Both -ErrorAction SilentlyContinue 

            If ($ReplStatuses) {

                 Write-Log "$($ReplStatuses.Count) replication links found for $($DC.HostName)" -level Info

                ForEach ($ReplStatus in $ReplStatuses) {

                    $Partner = $ReplStatus.Partner.Split(",")[1].Replace("CN=","")

                    $results += [pscustomobject] @{
                        'Source DC' = $DC.HostName.ToUpper()
                        'Partner DC' = (Get-ADComputer $Partner).DNSHostName.ToUpper()
                        'Direction' = $ReplStatus.PartnerType
                        'Type' = $ReplStatus.IntersiteTransportType
                        'Last Attempt' = $ReplStatus.LastReplicationAttempt
                        'Last Success' = $ReplStatus.LastReplicationSuccess
                        'Last Result' = $ReplStatus.LastReplicationResult
                    }
                }
            }

            Else {

                Write-Log "Unable to get replication status for $($DC.HostName)" -level Error
                $results += [pscustomobject] @{
                    'Source DC' = $DC.HostName.ToUpper()
                    'Partner DC' = "N/A"
                    Direction = "N/A"
                    Type = "N/A"
                    'Last Attempt' = "N/A"
                    'Last Success' = "N/A"
                    'Last Result' = "N/A"
                }
             }
        }

        ForEach ($result in $results) {

            If ("$($results.'Last Result')" -eq "0") {

                Write-Log "There were no replication issues found" -Level Info
                ExitWithCode -exitcode 0 
            }

            Else {

                Write-Log "These domain controllers have replication errors. Please review them..." -Level Error
                $error = $results | where {"$($_.'Last Result')" -ne "0"} | select 'Source DC','Partner DC','Direction' | ft -AutoSize
                Write-Log $error -Level Error
                ExitWithCode -exitcode 2
            }
        }
    }
}
Get-ADHealthCheck

Basically the only issue I'm having now is the last if/else block. I need it to loop through every entry in the $results hash table and if the "Last Result" key only contains "0", then exit with code 0. If it finds any other values, it should output the source, partner, and direction value(s) fromt he hash table.

Currently, if it encounters an issue, it jumps to the else block, outputs the information requested and then runs the ExitWithCode function which eventually kills the script so anything that comes after the error is not checked.

I've been looking at this too long and have been unsuccessful so I'm throwing it out to there since it may just be something simple I'm missing.

Niag Ntawv
  • 197
  • 1
  • 4
  • 16
  • Here's an alternative you might want to check out https://github.com/markwragg/Test-ActiveDirectory/blob/master/README.md – Mark Wragg Mar 30 '17 at 22:46
  • I think you want `If ($result.'Last Result' -eq 0) {`. Mostly it needs to be singular – Matt Mar 30 '17 at 22:51

1 Answers1

1

Look at your for loop variables

ForEach ($result in $results) {

For each single $result in the $results. In the following if statement you should be looking at one $result but instead you are doing a comparison against all results. Your subexpression syntax here is also not required.

 If ("$($results.'Last Result')" -eq "0") 

Note that this is perfectly valid code but it will not get you the results you expect. It will return all 'last result's that are 0. So if even one in the whole collection is 0 the true condition will always fire.

So lets just make some minor changes and use the singular $result

If ($result.'Last Result' -eq "0") 

That should get you the result you are looking for. I notice that you are looking for equality with the string 0. That will not be an issue here since the LHS sets the type for the comparison. Have a look at this other post to get a better understanding of what PowerShell does here.

Why is $false -eq "" true?

If your last result was the int 0 this would be true

0 -eq "0"

Your logic appears to be flawed as you have mentioned in comments on how to handle overall success and individual failures. Instead of looping through the results like you were I think we need to check the collection as a whole and loop only if errors are encountered.

# Lets check if any of the results contain failure
$failedChecks = $results | Where-object{$_.'Last Result' -ne 0}

# Evaluate $failedChecks as a boolean. If there are no failed checks Else can be assumed that everything is fine. 
if($failedChecks){
    Write-Log "These domain controllers have replication errors. Please review them..." -Level Error
    $error = $failedChecks | select 'Source DC','Partner DC','Direction' | ft -AutoSize | Out-String
    Write-Log $error -Level Error
    ExitWithCode -exitcode 2        
} else {
    Write-Log "There were no replication issues found" -Level Info
    ExitWithCode -exitcode 0 
}
Community
  • 1
  • 1
Matt
  • 45,022
  • 8
  • 78
  • 119
  • I see what you're talking about and made the adjustment. It seems to somewhat work but the output isn't what I expected. When i run the script there is actually 1 domain controller that it can't get replication info from so it outputs the "unable to get replication status for " message...this part is correct but the 2nd foreach loop, i would it expect it to output the "These domain controllers have replication errors. Please review them..." message since there is one problematic host where the last result field is not 0. – Niag Ntawv Mar 31 '17 at 14:10
  • I think this is because when it finds the first entry where the last result field = 0, it runs the ExitWithCode function and exits the script before it has a chance to iterate through the rest of the objects in $results. – Niag Ntawv Mar 31 '17 at 14:12
  • Your assertion appears to be correct. You would need to adjust your logic to get the results you want. I will try and show an example. – Matt Mar 31 '17 at 14:18
  • another thing I wanted to add is that, I need to run the ExitWithCode function and supply it with an appropriate exit code because this will be used in Dataloop and that monitoring tool uses Nagios exit codes to report on statuses. – Niag Ntawv Mar 31 '17 at 14:24
  • @NiagNtawv I added an option of something you could try that might get you better results. – Matt Mar 31 '17 at 14:55
  • cool, looks like that did it. I was thinking about doing it this way too but I don't write enough powershell and couldn't figure out how to make it work. Accepted this as the answer. – Niag Ntawv Mar 31 '17 at 15:26