0

I originally asked this question here: Powershell script for checking AD replication which helped resolve the logic for my script. I've since added additional information and checks to the script and the final result is below:

function ExitWithCode {
<#
.SYNOPSIS
Specifies custom exit code. 

.DESCRIPTION
The ExitWithCode function allows you to pass in a custom exit code 
throughout your script by calling the function.

.PARAMETER
Use the -exitcode parameter followed by an integer to specify the error 
code

.EXAMPLE
Calling ExitWithCode -exitcode 2 will stop the powershell.exe process 
and report an exit code of 2 to the system.
#>

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\ADHealthCheck' + '_' + "$(Get-Date -Format MM-dd-yyyy)" + '.log'),

    [Parameter(Mandatory = $false)]
    [string]$logFolderPath = 'c:\dataloop\ADHealthCheck\'
)

BEGIN {

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

    #Test to see if the 'c:\dataloop\ADHealthCheck\' directory exist. If it does not, than create it.
    If (-NOT (Test-Path $logFolderPath)) {
        Write-Verbose "Creating the folder, $logFolderPath"
        New-Item $logFolderPath -Force -ItemType directory
    }

    #Test to see if the file 'c:\dataloop\ADHeatlhCheck\ADHealthCheck_{current_date}.log' exist. If it does not, than create it.
    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 {
    #Obtain a list of all the domain controllers and sort them by name in ascedening order
    $DCs = Get-ADDomainController -Filter * |sort name

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

    #Create an empty array to store object properties later
    $results = @()

    ForEach ($DC in $DCs) {

        Write-Log "Getting replication metadata for $($DC.HostName)" -level Status

        #Grab replication metadata for each domain controller. Reports metadata for both inbound and outbound partners
        $ReplStatuses = Get-ADReplicationPartnerMetadata -target $DC.HostName -PartnerType Both -ErrorAction SilentlyContinue 

        If ($ReplStatuses) {

            #Reports the number of replication links found for each successful query
            Write-Log "$($ReplStatuses.Count) replication links found for $($DC.HostName)" -level Info

            ForEach ($ReplStatus in $ReplStatuses) {

                #Retrieves the hostname of each partner by splitting the 'Partner' key
                $Partner = $ReplStatus.Partner.Split(",")[1].Replace("CN=","")

                #create a custom object and set custom properties 
                $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 {

            #creates a custom object to store information about any domain controller where replication data could not be retrieved
            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"
            }

        }
    }

    #Start checking for outdated log files to purge
    Write-Log "Cleaning out outdated Log files..."

    #Define the log path
    $logPath = 'c:\dataloop\ADHealthCheck\'

    #Do the actual check based on the log path defined above
    $checkLogPath = Get-ChildItem -Path $logPath -Recurse

    #Performs check against the log path and returns any files where the last modified date is greater than 14 days
    $retentionPeriod = $checkLogPath | Where {$_.LastWriteTime -lt (Get-Date).AddDays(-14)}

    foreach ($file in $retentionPeriod) {
        if ($retentionPeriod) {
            Write-Log "Deleting file $file since it is older than 14 days" -Level Info

            #Sets the current working directory to the log path
            Set-Location $logPath

            #Deletes any files where the modified date is greater than 14 days
            Remove-Item $file -Force
        }

        else {
            Write-Log "There were no files older than two weeks. Nothing to delete at this time" -Level Info
        }
    }

    #Check to see 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 
    }
  }
}

Get-ADHealthCheck

There doesn't appear to be any problems with the script. When I run it locally on a server to test, everything works. It creates the correct log file with the right name in the right directory. I check the log messages and it all checks out.

The problem is when I try and port the script as a plugin to Dataloop/Outlyer and set it to run on specific servers, the script itself outputs the correct status to Dataloop but if I then remote into a server running the script and check my log file, it is logging all error messages even though the commands ran successfully.

Has anyone seen issues like this where a script runs and reports correctly locally but when triggered by an agent, it logs all the wrong info? I've reached out to Dataloop's support as well but figured I'd check here as well.

Community
  • 1
  • 1
Niag Ntawv
  • 197
  • 1
  • 4
  • 16
  • 1
    Could it be a permissions isssue on the server? Which user context is the script being executed in when it fails? – Bassie Apr 10 '17 at 20:47
  • not sure if you're referring to the execution policy but dataloop's KB articles suggests that for powershell plugins we have to execute the script using powershell.exe -executionpolicy bypass -file. I've also tried adding the -noprofile and -noninteractive switches which does not seem to make a difference. Current executionpolicy on one of the servers is set to unrestricted. – Niag Ntawv Apr 10 '17 at 20:54
  • Maybe post what error details are being written to your logs, it could give some useful clues – Bassie Apr 10 '17 at 20:59
  • @Bassie looks like your initial assumption was correct. The dataloop agent by default runs as the built-in dataloop-agent user which doesn't have admin rights. I found a few posts showing how to invoke a ps script as admin but did a faster test by just having the dataloop-agent service run under the local system account. This fixed the problem. Thanks! – Niag Ntawv Apr 10 '17 at 21:16
  • That's great! You should post that as an answer and accept it when you can - that way it will be more visible to others who also get this problem – Bassie Apr 10 '17 at 21:18

1 Answers1

0

For those interested, the dataloop-agent service runs under a built-in dataloop-agent user that gets created during installation and it does not have admin rights.

The fix was to simply change the service to run under the local system user. You can either do it manually through the GUI or more automated with a batch script.

Niag Ntawv
  • 197
  • 1
  • 4
  • 16