Powershell Log file - powershell

Firstly I want to point out that I'm a PowerShell ScrapBooker and not really terribly knowledgeable about PowerShell.
I've been working on a script that installs BGInfo ... I have the actual install and uninstall working perfectly, and now I'm moving on to getting the logging sorted out nicely.
I found this article "Create Log File in Powershell" which was wonderful and have incorporated this Function into my script.
Function Write-Log {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False)]
[ValidateSet("LABEL","INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message,
[Parameter(Mandatory=$False)]
[string]
$logfile
)
$Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss")
IF ($Level -eq "LABEL") {
$Line = "$Message"
}
ELSE {
$Line = "$Stamp $Level $Message"
}
If($logfile) {
Add-Content $logfile -Value $Line
}
Else {
Write-Output $Line
}
}
What I need to know is how to use this to log output from commands.
For Example:
In My script I have this command:
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run' -Name BgInfo -Value """$InstPath\Bginfo.exe"" $InstPath\$BGTemplateFile $InstOptions" -PropertyType 'String' -Force
or this one:
Copy $SourcePath\Bginfo.exe $InstPath
What I would like to know is how I can use my function to capture ANY output from that command and log it in my log file.
I guess that I would also like to use this information and apply it to any other commands where I want to log something.
Hopefully that's all clear and makes sense and that someone can help me out.
Cheers,
Dave.
:)

I believe you are referring to this function:
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
Usage:
Write-Log -Message 'My log Error Message' -Path c:\Logs\Script.log -Level Error
Write-Log -Message 'My log Warning message' -Level Warn
Note: By default its storing in C:\logs\PowershellLog.log. You can change it on the fly during using also and you can hardcode it in function itself.
Hope it helps.

Related

Powershell not creating log file

Ive got a powershell script where I am updating alot of stuff and I am wanting to write everything to a log file. I've used a similar method as this to write a CSV file but I can't get this to work. Can anyone help me out on this? Everything works except the log file does not get created.
$path = Split-Path -parent "\\srv02file\Public\Information Technology\IT Reports\*.*"
$LogDate = Get-Date -f yyyyMMddhhmm
$LogFile = $path + "Remove_Formatting_$LogDate.log"
#Log function with levels of logging and time entry. Default level is INFO when not set
#LogWrite -LEVEL INFO -Message "Log entry"
#HH:MM:SS INFO Log entry
Function LogWrite
{
[CmdletBinding()]
Param (
[Parameter(Mandatory=$False)]
[ValidateSet("INFO","WARN","ERROR","FATAL","DEBUG")]
[String]
$Level = "INFO",
[Parameter(Mandatory=$True)]
[string]
$Message
#[Parameter(Mandatory=$False)]
#[string]
#$LogPath
)
$Stamp = (Get-Date).toString("hh:mm:ss")
$Line = "$Stamp $Level $Message"
Add-Content $LogFile -Value $Line
Write-Output $Line
}
You're missing a trailing slash after the folder in $LogFile
Use Join-Path to combine a folder and filename to avoid that issue in future.
You'll also need to uncomment the $LogPath parameter and use that, since $LogFile doesn't exist in the function scope.

Powershell bind argument path

When I execute a particular script, the Write-Log function throws an error. This appears to be before I call the function. It is loaded from an external file. This problem is new, it worked previously with no issues.
Error
There are two errors, split by a VERBOSE output statement. The 2nd VERBOSE line is the last script line I included. The format (not colours) is the best I can recreate
C:\Scripts>powershell -file tca_export_tt_ftps.ps1
Test-Path : Cannot bind argument to parameter 'Path' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:75 char:24
+ if ((Test-Path $Path) -AND $NoClobber) {
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.TestPathCommand
VERBOSE: Testing C:\Scripts\log\ exists
Out-File : Cannot bind argument to parameter 'FilePath' because it is an empty string.
At C:\scripts\Function-Write-Log.ps1:110 char:67
+ ... FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Out-File], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl lowed,Microsoft.PowerShell.Commands.OutFileCommand
VERBOSE: Logfile is: C:\Scripts\log\kh_tca_out_20210222.log
Start of my Script
Function Upload-TCA
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$false)]
[Alias('StartDate')]
[Alias('SeedDate')]
[DateTime]$dateSeed = (Get-Date),
#[string]$dateSeedStr = (Get-Date -format "yyyy-MM-dd"),
[Parameter(Mandatory=$false)]
[Alias('DisableFtp')]
[bool]$ftpDisabled=$false,
[Parameter(Mandatory=$false)]
[Alias('DisplayConfig')]
[bool]$emitConfig = $false,
[Parameter(Mandatory=$true)]
[string]$orgCode
)
#########################################################################
# Function Imports
#########################################################################
. c:\scripts\Function-Generate-Folder.ps1
. c:\scripts\Function-Write-Log.ps1
. c:\scripts\Function-Upload-WinSCP-FTPS.ps1
. c:\scripts\Function-Create-CredentialFromFile.ps1
#########################################################################
# Logging Setup
#########################################################################
$logDir = "C:\Scripts\log\"
Generate-Folder $logDir
$logName = "$orgCode_tca_out_$(Get-Date -Format "yyyyMMdd").log"
$logFile = Join-Path -Path $logDir -ChildPath $logName
Write-Log "Logfile is: $logFile" -Path $logFile
Write-Log
The Write-Log function is from the Microsoft Script Center Included here in case the link is broken
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.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.
.NOTES
Created by: Jason Wasser #wasserja
Modified: 11/24/2015 09:30:19 AM
Changelog:
* Code simplification and clarification - thanks to #juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to #JeffHicks
* Revised the Force switch to work as it should - thanks to #JeffHicks
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
I hadn't read the Generate-Folder method correctly. I changed the calls in there for Write-Log to Write-Host. It was trying to use the log before it was fully setup.
Function Generate-Folder()
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[string]$path='C:\scripts\logs'
)
Write-Host "Testing $path exists" # cant use write-log as not setup yet
$global:foldPath=$null
foreach($foldername in $path.split("\"))
{
$global:foldPath+=($foldername+"\")
if(!(Test-Path $global:foldPath))
{
New-Item -ItemType Directory -Path $global:foldPath
Write-Host "$global:foldPath Folder Created Successfully" #-Path $logFile
}
}
}

Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process

I have the following PowerShell script:
param([switch]$Elevated)
function Test-Admin
{
$currentUser = New-Object Security.Principal.WindowsPrincipal $([Security.Principal.WindowsIdentity]::GetCurrent())
$currentUser.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}
if ((Test-Admin) -eq $false) {
if ($elevated) {
# tried to elevate, did not work, aborting
} else {
Start-Process powershell.exe -Verb RunAs -ArgumentList ('-noprofile -noexit -file "{0}" -elevated ' -f ($myinvocation.MyCommand.Definition))
}
exit
}
function UpdateHosts {
param ($hostName)
Write-Host $hostName
try {
$strHosts = (Get-Content C:\WINDOWS\system32\drivers\etc\hosts -Raw)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
} catch {
Write-Error "Unable to read hosts file"
Write-Error $_
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+ $hostName","$ipAddress $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
} catch {
Write-Error "Unable to write hosts file"
Write-Error $_
exit
}
}
$ipAddress = "127.0.0.1"
UpdateHosts -hostName local.pap360.com
Sometimes, when I run it, I get the following error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by another process.
When I open up C:\WINDOWS\system32\drivers\etc\hosts in Notepad it's then blank. ie. all the data I had in it is wiped.
My question is... how can I prevent this from happening?
Like if Set-Content can't access the hosts file to write to it then how is it able to wipe it's contents? And why isn't the catch block working?
Here's the full error:
Set-Content : The process cannot access the file 'C:\WINDOWS\system32\drivers\etc\hosts' because it is being used by
another process.
At C:\path\to\test.ps1:36 char:92
+ ... $hostName" | Set-Content -Path C:\WINDOWS\system32\drivers\etc\hosts
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (C:\WINDOWS\system32\drivers\etc\hosts:String) [Set-Content], IOException
+ FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.SetContentCommand
I also don't understand why it's so intermittent. Is there some Windows process that opens the hosts file up for 1s once a minute or some such?
First of all, check if your Firewall or AV software isn't restricting access to the file.
If that is not the case and 'some' other process is currently locking the hosts file, perhaps add a test before reading or writing the file can help:
function Test-LockedFile {
param (
[parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[Alias('FullName', 'FilePath')]
[ValidateScript({Test-Path $_ -PathType Leaf})]
[string]$Path
)
$file = [System.IO.FileInfo]::new($Path)
# old PowerShell versions use:
# $file = New-Object System.IO.FileInfo $Path
try {
$stream = $file.Open([System.IO.FileMode]::Open,
[System.IO.FileAccess]::ReadWrite,
[System.IO.FileShare]::None)
if ($stream) { $stream.Close() }
return $false # file is not locked
}
catch {
return $true # file is locked
}
}
Then use like this:
function UpdateHosts {
param ($hostName)
Write-Host $hostName
$path = 'C:\WINDOWS\system32\drivers\etc\hosts'
# test if the file is readable/writable
# you can of course also put this in a loop to keep trying for X times
# until Test-LockedFile -Path $path returns $false.
if (Test-LockedFile -Path $path) {
Write-Error "The hosts file is currently locked"
}
else {
try {
$strHosts = (Get-Content $path -Raw -ErrorAction Stop)
if([string]::IsNullOrEmpty($strHosts)) {
Write-Error "Get-Content hosts empty"
exit
}
}
catch {
Write-Error "Unable to read hosts file:`r`n$($_.Exception.Message)"
exit
}
try {
$strHosts -replace "[\d]+\.[\d]+\.[\d]+\.[\d]+\s+$hostName", "$ipAddress $hostName" |
Set-Content -Path $path -Force -ErrorAction Stop
}
catch {
Write-Error "Unable to write hosts file:`r`n$($_.Exception.Message)"
exit
}
}
}

How to I apply the Write-Log function to a script

I have the latest Write-Log function from this module installed and wondered if it's possible to apply that function here rather than and or while using the verbose part of the transcript.
I want to now add in a try..catch where if it fails it using the Write-Log function to insert a record into the log file
$varfullpath = "C:\Users\Simon.Evans\Documents\ReferenceData__logfile.txt"
Start-Transcript -Path $varfullpath -Append
Write-Output $varfullpath
[string]$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\LNAT\Code_Mapping.txt"
[string]$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\LNAT\Code_Mapping.txt"
Copy-Item -Force -Verbose $sourceDirectory -Destination $destinationDirectory
Stop-Transcript
For info this is the Write-Log function installed which still not sussed how it works given I've never used functions.
<#
.Synopsis
Write-Log writes a message to a specified log file with the current time stamp.
.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.
.NOTES
Created by: Jason Wasser #wasserja
Modified: 11/24/2015 09:30:19 AM
Changelog:
* Code simplification and clarification - thanks to #juneb_get_help
* Added documentation.
* Renamed LogPath parameter to Path to keep it standard - thanks to #JeffHicks
* Revised the Force switch to work as it should - thanks to #JeffHicks
To Do:
* Add error handling if trying to create a log file in a inaccessible location.
* Add ability to write $Message to $Verbose or $Error pipelines to eliminate
duplicates.
.PARAMETER Message
Message is the content that you wish to add to the log file.
.PARAMETER Path
The path to the log file to which you would like to write. By default the function will
create the path and file if it does not exist.
.PARAMETER Level
Specify the criticality of the log information being written to the log (i.e. Error, Warning, Informational)
.PARAMETER NoClobber
Use NoClobber if you do not wish to overwrite an existing file.
.EXAMPLE
Write-Log -Message 'Log message'
Writes the message to c:\Logs\PowerShellLog.log.
.EXAMPLE
Write-Log -Message 'Restarting Server.' -Path c:\Logs\Scriptoutput.log
Writes the content to the specified log file and creates the path and file specified.
.EXAMPLE
Write-Log -Message 'Folder does not exist.' -Path c:\Logs\Script.log -Level Error
Writes the message to the specified log file as an error message, and writes the message to the error pipeline.
.LINK
https://gallery.technet.microsoft.com/scriptcenter/Write-Log-PowerShell-999c32d0
#>
function Write-Log
{
[CmdletBinding()]
Param
(
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[ValidateNotNullOrEmpty()]
[Alias("LogContent")]
[string]$Message,
[Parameter(Mandatory=$false)]
[Alias('LogPath')]
[string]$Path='C:\Logs\PowerShellLog.log',
[Parameter(Mandatory=$false)]
[ValidateSet("Error","Warn","Info")]
[string]$Level="Info",
[Parameter(Mandatory=$false)]
[switch]$NoClobber
)
Begin
{
# Set VerbosePreference to Continue so that verbose messages are displayed.
$VerbosePreference = 'Continue'
}
Process
{
# If the file already exists and NoClobber was specified, do not write to the log.
if ((Test-Path $Path) -AND $NoClobber) {
Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
Return
}
# If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
elseif (!(Test-Path $Path)) {
Write-Verbose "Creating $Path."
$NewLogFile = New-Item $Path -Force -ItemType File
}
else {
# Nothing to see here yet.
}
# Format Date for our Log File
$FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Write message to error, warning, or verbose pipeline and specify $LevelText
switch ($Level) {
'Error' {
Write-Error $Message
$LevelText = 'ERROR:'
}
'Warn' {
Write-Warning $Message
$LevelText = 'WARNING:'
}
'Info' {
Write-Verbose $Message
$LevelText = 'INFO:'
}
}
# Write log entry to $Path
"$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
}
End
{
}
}
Thanks for the input chaps finally got there in the end but made a few silly errors on the way.
$varfullpath = "C:\Users\Simon.Evans\Documents\ReferenceData__logfile.txt"
$sourceDirectory = "C:\Users\Simon.Evans\Documents\Source Data\LNAT\Code_Maping.txt"
$destinationDirectory = "I:\Dev\BI\Projects\Powershell\Test Area\Source Data\LNAT\Code_Mapping.txt"
try{
Copy-item -Force -Verbose $sourceDirectory -Destination $destinationDirectory -ErrorAction Stop
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory suceeded" -path $varfullpath
}
catch{
$Error[0] | Write-Log -path $varfullpath ##Example Error 1
Write-log -Message "Copy from $sourceDirectory to $destinationDirectory Failed" -Level Error -path $varfullpath ##Example Error 2
}

Powershell script for checking AD replication

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.
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
}