Command History to Syslog for CDOT Version 2: Part 2/2 - The Script

#################################################################
## Command History to Syslog for CDOT (v2 - August 2014 by VC) ##
## =========================================================== ##
## CAVEAT UTILITOR!                                            ##
## This program comes with no warranty and no support          ##
#################################################################

Param(
[switch]$help,
[string]$workingDirectory,
[switch]$setup,
[string]$Cluster,
[string]$Node,
[string]$Batch,
[int]$RepeatMinutes,
[int]$RepeatIterations,
[switch]$setCredential
)

#########################
## Function Write-Help ##
#########################

Function Write-Help {
"SYNOPSIS
========

Command History to Syslog for Clustered Data ONTAP 8.2.X (V2 - August 2014 by DJC).

SYNTAX
======

.\CommandHistoryToSyslogForCDOT.ps1

.\CommandHistoryToSyslogForCDOT.ps1 -help

.\CommandHistoryToSyslogForCDOT.ps1 -setup -workingDirectory DIRECTORYPATH

.\CommandHistoryToSyslogForCDOT.ps1 -cluster CLUSTERNAME -node NODENAME -workingDirectory DIRECTORYPATH
{-repeatMinutes XXX -repeatIterations XXX}

.\CommandHistoryToSyslogForCDOT.ps1 -batch FILEPATH/FILENAME -workingDirectory DIRECTORYPATH
{-repeatMinutes XXX -repeatIterations XXX}

.\CommandHistoryToSyslogForCDOT.ps1 -setCredential -workingDirectory DIRECTORYPATH

DESCRIPTION
===========

In CDOT 8.2.1 it is not possible to send the contents of the command-history.log to a syslog server natively. This script gets around this by using a Windows Server 2008R2/7+ as a proxy, reading the command-history.log files over the SPI, and comparing a read (at say time T2) to a previously stored read from last time the script ran (at say time T1).

PARAMETERS
==========

PARAMETER -help
Displays the help output.

PARAMETER -workingDirectory
This is the working directory where the INI file is, and command-history.logs are saved to (also any batch instruction file can be in here too.) For everything but -help, this parameter is mandatory. The folder must be pre-created.

PARAMETER -setup
Runs setup which creates an ini file containing syslog server FQDN/IP and syslog server UDP port.

PARAMETER -cluster
The cluster FQDN/IP to connect to (must be used with -node).

PARAMETER -node
The nodename (CASE SENSITIVE - as from the output of '::> node show') (must be used with -cluster).

PARAMETER -batch
Runs from a batch instructions file containing clusters and their nodes.
The batch file is a simple txt file. Comments beginning with # are allowed. Clustername must come first on the line, then the nodes follow separated by commas. Example:
CLUSTER1,NODE1,NODE2,NODE3

PARAMETER -repeatMinutes
Runs every XXX minutes (requires the user running it is not logged off) {default = 5 minutes}. If repeatIterations is not specified, it will run until the user gets logged off.

PARAMETER -repeatIterations
The number of times to repeat running (requires the user running it is not logged off) {default = 0 or infinite repeats}. If RepeatMinutes is not specified, it will run every 5 minutes.

PARAMETER -setCredential
This allows pre-setting/re-setting of the credential required to log in via the SPI, or resetting of a previous entered credential. This is an optional switch, if the first time the script runs it detects no credential file, it will prompt for credentials and create one.

NOTES
=====

i) It handles when logs rotate, and can handle any number of log rotations between times it is run.
ii) The downloaded command-history.logs are saved locally as CLUSTERNAME.NODENAME.command-history.log.XXXXXXXXXX.
iii) This script uses all native PowerShell 3 commands. It doesn't require the DataONTAP PowerShell toolkit.
iv) The user connecting to the SPI needs only HTTP application, but requires admin role
- ::> sec login cre -user syslogger -app http -auth password -role admin
v) The repeat parameters were devised as a workaround to environments where there is the GPO 'Network access: Do not allow storage of credentials'.
vi) Thrown Exceptions are recorded along with some additional output in CommandHistoryToSyslogForCDOT.log
"}

#####################################################
## FUNCTION: TrapOut - Processes Thrown Exceptions ##
#####################################################

Function TrapOut{
$timeOfError = (get-date).DateTime
$lineNumber = $_.InvocationInfo.ScriptLineNumber
$offsetInLine = $_.InvocationInfo.OffsetInLine
$errorMessage = $_.Exception.Message
$timeOfError + ": " + $lineNumber + "." + $offsetInLine + " :" + $errorMessage >> $exceptionLogFile}

##############################################
## FUNCTION: sout - Special OUTput Function ##
##############################################

Function sout {
# Simply writes to screen and to log at the same time - only used to add context to the Thrown Exceptions log file!
$args[0]
$args[0] >> $exceptionLogFile}

###################################################################
## A Fix for Invoke-Web Request not liking some SSL Certificates ##
###################################################################

# This add-type is to get around SSL certificate errors with Invoke-WebRequest
# From: http://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
        public bool CheckValidationResult(
            ServicePoint srvPoint, X509Certificate certificate,
            WebRequest request, int certificateProblem) {
            return true;
        }
    }
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy

#######################################################################################################
## FUNCTION: Send-SyslogMessage                                                                      ##
## courtesy of: http://aperturescience.su/blog/2014/7/1/sending-syslog-messages-from-powershell.html ##
#######################################################################################################

Add-Type -TypeDefinition @"
public enum Syslog_Facility
{
kern,
user,
mail,
daemon,
auth,
syslog,
lpr,
news,
uucp,
clock,
authpriv,
ftp,
ntp,
logaudit,
logalert,
cron, 
local0,
local1,
local2,
local3,
local4,
local5,
local6,
local7,
}
"@

Add-Type -TypeDefinition @"
public enum Syslog_Severity
{
Emergency,
Alert,
Critical,
Error,
Warning,
Notice,
Informational,
Debug
}
"@

function Send-SyslogMessage {

<#
.SYNOPSIS: Sends a SYSLOG message to a server running the SYSLOG daemon
.DESCRIPTION: Sends a message to a SYSLOG server as defined in RFC 5424. A SYSLOG message contains not only raw message text,
but also a severity level and application/system within the host that has generated the message.
.PARAMETER Server: Destination SYSLOG server that message is to be sent to
.PARAMETER Message: Our message
.PARAMETER Severity: Severity level as defined in SYSLOG specification, must be of ENUM type Syslog_Severity
.PARAMETER Facility: Facility of message as defined in SYSLOG specification, must be of ENUM type Syslog_Facility
.PARAMETER Hostname: Hostname of machine the mssage is about, if not specified, local hostname will be used
.PARAMETER Timestamp: Timestamp, myst be of format, "yyyy:MM:dd:-HH:mm:ss zzz", if not specified, current date & time will be used
.PARAMETER UDPPort: SYSLOG UDP port to send message to
.AUTHOR: Kieran Jacobsen (2014 07 01)
.LINK: https://github.com/kjacobsen/PowershellSyslog
.LINK: http://aperturescience.su
#>

[CMDLetBinding()]
Param
(
[Parameter(mandatory=$true)] [String] $Server,
[Parameter(mandatory=$true)] [String] $Message,
[Parameter(mandatory=$true)] [Syslog_Severity] $Severity,
[Parameter(mandatory=$true)] [Syslog_Facility] $Facility,
[String] $Hostname,
[String] $Timestamp,
[int] $UDPPort = 514
)

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS SCOPE

# Create a UDP Client Object
$UDPCLient = New-Object System.Net.Sockets.UdpClient
$UDPCLient.Connect($Server, $UDPPort)

# Evaluate the facility and severity based on the enum types
$Facility_Number = $Facility.value__
$Severity_Number = $Severity.value__
Write-Verbose "Syslog Facility, $Facility_Number, Severity is $Severity_Number"

# Calculate the priority
$Priority = ($Facility_Number * 8) + $Severity_Number
Write-Verbose "Priority is $Priority"

# If no hostname parameter specified, then set it
if (($Hostname -eq "") -or ($Hostname -eq $null)){$Hostname = Hostname}

# If the hostname hasn't been specified, then we will use the current date and time
if (($Timestamp -eq "") -or ($Timestamp -eq $null)){$Timestamp = Get-Date -Format "yyyy:MM:dd:-HH:mm:ss zzz"}

# Assemble the full syslog formatted message
$FullSyslogMessage = "<{0}>{1} {2} {3}" -f $Priority, $Timestamp, $Hostname, $Message

# create an ASCII Encoding object
$Encoding = [System.Text.Encoding]::ASCII

# Convert into byte array representation
$ByteSyslogMessage = $Encoding.GetBytes($FullSyslogMessage)

# If the message is too long, shorten it
if ($ByteSyslogMessage.Length -gt 1024){$ByteSyslogMessage = $ByteSyslogMessage[0..1024]}

# Send the Message
$UDPCLient.Send($ByteSyslogMessage, $ByteSyslogMessage.Length)

}

# Note: I've made no changes to the above function except compacting it a bit and adding Trap{TrapOut}

###################################
## FUNCTION: FormAndSendToSyslog ##
###################################

function FormAndSendToSyslog {

Param(
[Parameter(mandatory=$true)] [String] $inputLine,
[Parameter(mandatory=$true)] [String] $nodeFrom
)

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS SCOPE
# We remove the  "00000046.00000b3c 00001b27 " and date from the command-history lines
$cdotDateTime = $inputLine.substring(27,31)
$outputLine   = $inputLine.remove(0,59)
"$cdotDateTime Debug local7 $nodeFrom $outputLine" 
$Sent2Syslog  = Send-SyslogMessage $syslogServer $outputLine Debug local7 $nodeFrom $cdotDateTime $syslogServerUDP

}

#####################################
## FUNCTION Send-CmdHistory2Syslog ##
#####################################

Function Send-CmdHistory2Syslog {

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS SCOPE

$clusterName               = $args[0]
$nodeName                  = $args[1]
$URLtoSPI                  = "https://" + $clusterName + "/spi/"
$URLtoMlog                 = $URLtoSPI + $nodeName + "/etc/log/mlog/"
$lastSavedCmdHist          = $null

sout "FN:SCH2S: Reading the contents of $workingDirectory ..."
$items                     = Get-ChildItem -Path $workingDirectory
$searchString              = $clusterName + "_" + $nodeName + "*"
sout "FN:SCH2S: ... and looking for items like $searchstring"
$matches                   = $items -like $searchString
$noPreviousSavedCmdHistLog = !$matches
If ($matches){$lastSavedCmdHist = $matches[0]} # There should be only 2 matches, a .log.XXXXXXXXXX file [0] and a .previous file [1] (This is used in CONDITION 3)

sout "FN:SCH2S: Download links at $URLtoMLog"
$linksInMlogPage           = invoke-webrequest -uri $URLtoMlog -credential $credential
If (!$linksInMlogPage){sout "FN:SCH2S: Failed to connect to $URLtoMLog. Returning to main program!";return}

$linksInMlog               = $linksInMlogPage.links | foreach {$_.href}
$commandHistoryLogLinks    = $linksInMlog | where-object {$_ -match 'command-history'}
$countCommandHistoryLogs   = $commandHistoryLogLinks.count
$finalCmdHistFile          = $commandHistoryLogLinks[$countCommandHistoryLogs-1]
$finalCmdHistFileURL       = $URLtoMlog + $finalCmdHistFile

# Saved File Locations
$savedFilepathLatest       = $workingDirectory + "\" + $clusterName + "_" + $nodename + "_" + $finalCmdHistFile
$savedFilepathCurrent      = $workingDirectory + "\" + $lastSavedCmdHist # Implemented for CONDITION 3
$savedFilepathPrevious     = $workingDirectory + "\" + $clusterName + "_" + $nodename + "_" + "command-history.log.previous"

##################################################################
# CONDITION 1 - no previous saved command-history.log.XXXXXXXXXX #
##################################################################

If ($noPreviousSavedCmdHistLog){
sout "FN:SCH2S: CONDITION 1 - we have no previous saved command-history.log.XXXXXXXXXX"
sout "FN:SCH2S: Download file from $finalCmdHistFileURL and save as $savedFilepathLatest"
$logGatherer = invoke-webrequest -uri $finalCmdHistFileURL -credential $credential -OutFile $savedFilepathLatest
sout "FN:SCH2S: Return to main program."
return}

# Remove any previously saved ...command-history.log.previous
$isThereAPreviousSavedCommandHistoryFile = Test-Path $savedFilepathPrevious
if ($isThereAPreviousSavedCommandHistoryFile){
Remove-Item $savedFilepathPrevious
sout "FN:SCH2S: Removed the file $savedFilepathPrevious"}

# Then Rename the previously saved to ...command-history.log.previous             
If ($savedFilepathLatest -eq $savedFilepathCurrent) {                             
Rename-Item $savedFilepathLatest $savedFilepathPrevious                       
sout "FN:SCH2S: Renamed the file $savedFilepathLatest  to $savedFilepathPrevious"} 
If ($savedFilepathLatest -ne $savedFilepathCurrent) {                             
Rename-Item $savedFilepathCurrent $savedFilepathPrevious                      
sout "FN:SCH2S: Renamed the file $savedFilepathCurrent  to $savedFilepathPrevious"}

# Do we have the latest command-history.log?
$searchString          = $clusterName + "_" + $nodename + "_" + $finalCmdHistFile
$latestCmdHistoryCheck = $items -like $searchString

#######################################################################
# CONDITION 2 - we have the latest command-history, just send updates #
#######################################################################

If ($latestCmdHistoryCheck){

sout "FN:SCH2S: CONDITION 2 - We have the latest command-history.log and just need to send updates"
sout "FN:SCH2S: Download file from $finalCmdHistFileURL and save as $savedFilepathLatest"
$logGatherer = invoke-webrequest -uri $finalCmdHistFileURL -credential $credential -OutFile $savedFilepathLatest
$sizeOfLatestFile   = (Get-Item $savedFilepathLatest).length
$sizeOfPreviousFile = (Get-Item $savedFilepathPrevious).length

if ($sizeOfLatestFile -eq $sizeOfPreviousFile){
sout "FN:SCH2S: File size of latest file and previous file is the same ($sizeOfLatestFile) - no updates."
sout "FN:SCH2S: Return to main program."
return}

# Code was added here and later in CONDITION 3, to catch an error when using Compare-Object, where, if the file downloaded previously was downloaded just after the log cycled it might have no content. Also the circumstance where both the latest and previous files were downloaded close together after the log cycled, and have no content.

$checkDifferences = "true"
$contentInLogPrevious = Get-Content $savedFilepathPrevious
$contentInLatestLog   = Get-Content $savedFilepathLatest
If (!$contentInLogPrevious){$checkDifferences = $null}
If (!$contentInLatestLog)  {
sout "FN:SCH2S: No content in latest log"
sout "FN:SCH2S: Return to main program."
return}

If ($checkDifferences){
$differences = Compare-Object $contentInLatestLog $contentInLogPrevious
$countOfMessagesSent    = $differences.count
foreach ($difference in $differences){
Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP
$message = $difference.InputObject
If ($message){FormAndSendToSyslog $message $nodeName}}}

If (!$checkDifferences){
$countOfMessagesSent = $contentInLatestLog.count
foreach ($message in $contentInLatestLog){
Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP
If ($message){FormAndSendToSyslog $message $nodeName}}}

sout "FN:SCH2S: Sent $countOfMessagesSent messages!"
$textCombineClusterNode = $clusterName + ":" + $nodeName
sout "FN:SCH2S: Completed sending syslog updates for $textCombineClusterNode"
sout "FN:SCH2S: Return to main program."
return

}

####################################################
# CONDITION 3 - the command-history log has cycled #
####################################################

sout "FN:SCH2S: CONDITION 3: The command-history.log has cycled. We need to send what was not sent in the last saved command-history.log, then the contents of any newer log files."

# We split the last saved command history filename to get the command-history.log.XXXXXXXXXX bit
$splitString               = $lastSavedCmdHist.Split("_")
$lastCheckedCmdHistFile    = $splitString[2]
$lastCheckedCmdHistFileURL = $URLtoMlog + $lastCheckedCmdHistFile
$savedFilepathLastChecked  = $workingDirectory + "\" + $lastSavedCmdHist

sout "FN:SCH2S: Download file from $lastCheckedCmdHistFileURL and save as $savedFilepathLatest"
$logGatherer = invoke-webrequest -uri $lastCheckedCmdHistFileURL -credential $credential -OutFile $savedFilepathLastChecked

sout "FN:SCH2S: Send all the differences from the last checked log file."
$differences = Compare-Object $(Get-Content $savedFilepathLastChecked) $(Get-Content $savedFilepathPrevious)
If ($differences){ # Only send differences if there are any.
foreach ($difference in $differences){
Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP
$message = $difference.InputObject
If ($message){FormAndSendToSyslog $message $nodeName}}
$countOfMessagesSent = $differences.count
sout "FN:SCH2S: Sent $countOfMessagesSent messages!"}

sout "FN:SCH2S: Completed sending syslog updates from $savedFilepathLastChecked"
sout "FN:SCH2S: Removing the file $savedFilepathLastChecked"
Remove-Item $savedFilepathLastChecked

$splitString                = $lastCheckedCmdHistFile.Split(".")
$lastCheckedLogNumberString = $splitString[2] # 2 since we have two dots and want the end XXXXXXXXXX bit
[int]$lastCheckedLogNumber  = $lastCheckedLogNumberString.TrimStart("0")
sout "FN:SCH2S: The last log number checked was $lastCheckedLogNumber"
$splitString                = $finalCmdHistFile.Split(".")
$finalLogNumberString       = $splitString[2] # 2 since we have two dots and want the end XXXXXXXXXX bit
[int]$finalLogNumber        = $finalLogNumberString.TrimStart("0")
sout "FN:SCH2S: The final log number available is $finalLogNumber"

$cycledLogs = $finalLogNumber - $lastCheckedLogNumber # Most likely this number will be 1

$i=0
do {

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP

# Log Numbers, URLs, Save Paths
$logNumber               = $lastCheckedLogNumber + $i + 1
[string]$stringLogNumber = $logNumber
$paddedStringLogNumber   = $stringLogNumber.PadLeft(10,"0")
$logFileToRead           = "command-history.log." + $paddedStringLogNumber
$logFileURL              = $URLtoMlog + $logFileToRead
$logFileSaveFilePath     = $workingDirectory + "\" + $clusterName + "_" + $nodename + "_" + $logFileToRead
sout "FN:SCH2S: Download file from $logFileURL and save as $logFileSaveFilePath"
$logGatherer             = invoke-webrequest -uri $logFileURL -credential $credential -OutFile $logFileSaveFilePath
$cmdHistoryFileContents  = Get-Content $logFileSaveFilePath

foreach ($message in $cmdHistoryFileContents){
Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP
If ($message){FormAndSendToSyslog $message $nodeName}}

$countOfMessagesSent     = $cmdHistoryFileContents.count
sout "FN:SCH2S: Sent $countOfMessagesSent messages!"
sout "FN:SCH2S: Completed sending syslog updates from $logFileSaveFilePath"

If($logNumber -ne $finalLogNumber){
sout "FN:SCH2S: Removing the file $logFileSaveFilePath (since it is not the latest one which will be kept)"
Remove-Item $logFileSaveFilePath}

$i++

} while ($i -lt $cycledLogs)

}

###########################################
###########################################
### THE MAIN PROGRAM - .\{filename}.ps1 ###
###########################################
###########################################

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS SCOPE

"
Command History to Syslog for CDOT (v2 - August 2014)
=====================================================
"

###################################
# Check PowerShell 3 is installed #
###################################

if($PSVersionTable.PSVersion.Major -lt 3){"MAIN PROGRAM: This program needs PowerShell 3";exit}

##############################
# Handle the -help parameter #
##############################

If ($help) {Write-Help;exit}

##########################################
# Handle the -workingDirectory parameter #
##########################################

If (!$workingDirectory){ "MAIN PROGRAM: No -workingDirectory parameter detected. The working directory parameter is manadatory for everything bar -help.";"";exit}
$iniFile      = $workingDirectory + "\CommandHistoryToSyslogForCDOT.ini"
$doesIniExist = Test-Path $iniFile

##########################################
# Initiate/Test for Script Logging Files #
##########################################

$exceptionLogFile    = $workingDirectory + "\CommandHistoryToSyslogForCDOT.log"
$oldExceptionLogFile = $workingDirectory + "\CommandHistoryToSyslogForCDOT.log.old"
$doesLogExist        = Test-Path $exceptionLogFile
$doesOldLogExist     = Test-Path $oldExceptionLogFile
If (!$doesLogExist)   {$HideOutput = New-Item $exceptionLogFile -type file -force}
If (!$doesOldLogExist){$HideOutput = New-Item $oldExceptionLogFile -type file -force}

###############################
# Handle the -setup parameter #
###############################

If ($setCredential) {$skipSetup = "true"} # Skip Setup if -setcredential is set

If ($setup -and !$skipSetup) {

"MAIN PROGRAM: -setup parameter detected, running setup."
If ($doesIniExist -eq $True ) {
"INI file detected! Current INI file contents =";""
$contents = Get-Content $iniFile
$contents
""
$ReadInput = Read-Host "Do you want to overwrite (Y/N)?"
If ($ReadInput -eq "N"){"";exit}
} # END of If ($doesIniExist -eq $True )
""
"Command History to Syslog Setup"
"==============================="
""
If ($doesIniExist) {"This setup script will (re)create the INI file $iniFile with the following details:"}
If (!$doesIniExist){"This setup script will create an INI file in the filepath $iniFile, with the following details:"}
""
"1) Syslog Server FQDN / IP Address."
"2) Syslog Server UDP Port (Default = 514)."
""
$HideOutput = New-Item $iniFile -type file -force
$ReadInput = Read-Host "Enter Syslog Server FQDN / IP Address?"
Add-Content $iniFile "Syslog Server FQDN/IP:$ReadInput"
$ReadInput = Read-Host "Enter Syslog Server UDP Port (Default = 514)?"
Add-Content $iniFile "Syslog Server UDP Port:$ReadInput"
""
exit

}

#######################
# Handle the INI File #
#######################

If (!$doesIniExist -and !$skipSetup){"MAIN PROGRAM: The script needs an INI file. Please rerun with -setup.";"";exit}
If ($doesIniExist -and !$skipSetup) {
$contents = Get-Content $iniFile
$syslogServer    = $contents[0].remove(0,22)
$syslogServerUDP = $contents[1].remove(0,23)
"MAIN PROGRAM: Got contents of INI file from $iniFile"}

######################
# HANDLE CREDENTIALS #
######################

$whoAmI                  = $env:username
$credentialsFile         = $workingDirectory + "\CommandHistoryToSyslogForCDOT_" + $whoAmI + ".creds"
$isThereACredentialsFile = Test-Path $credentialsFile
If ($setCredential -and $isThereACredentialsFile){Remove-Item $credentialsFile;$isThereACredentialsFile = $null}

If(!$isThereACredentialsFile){
"MAIN PROGRAM: The program needs to store credentials for connecting to the SPI. The credential is stored as an encrypted string - at $credentialsFile - that only $whoAmI can use."
$ReadInput              = Read-Host "MAIN PROGRAM: Enter user with access to the SPI (The username must be entered with CASE SENSITIVITY as displayed via CDOT::> security login show)?"
$getCredential          = Get-Credential -credential $ReadInput
$username               = $ReadInput
$securePassword         = $getCredential.password
$credential             = New-Object System.Management.Automation.PsCredential($username, $securePassword)
$securePasswordString   = $securePassword | ConvertFrom-SecureString
$credentialsFileContent = @($username,$securePasswordString)
$credentialsFileContent | Set-Content $credentialsFile
}

if($isThereACredentialsFile){
"MAIN PROGRAM: Detected credentials file at $credentialsFile. Getting content."
$fileContent          = Get-Content $credentialsFile
$username             = $fileContent[0]
$securePasswordString = $fileContent[1]
$securePassword       = $securePasswordString | ConvertTo-SecureString
$credential           = New-Object System.Management.Automation.PsCredential($username, $securePassword)
}

If ($setCredential){"Finished setting credential. Exiting now.";"";exit}

#############################################
# Handle RepeatMinutes and RepeatIterations #
#############################################

$sleepTimer        = 300    # Default sleep is 5 minutes
$sleep             = $null  # By default, don't sleep
$repeats           = 1      # By default, just run once
$infiniteLoop      = $null  # By default, not an infinite loop

If ($RepeatMinutes) {
$sleepTimer    = $RepeatMinutes*60
$sleep         = "true"
"MAIN PROGRAM: Script will repeat every $RepeatMinutes minutes."}

If (!$RepeatMinutes -and $RepeatIterations) {
$sleep         = "true"
$RepeatMinutes = 5
"MAIN PROGRAM: Script will repeat every 5 minutes."}

If ($RepeatIterations) {
$sleep        = "true"
$repeats      = $RepeatIterations
"MAIN PROGRAM: Script will repeat $repeatIterations times."}

If ($RepeatMinutes -and !$RepeatIterations) {
$sleep         = "true"
$infiniteLoop  = "true"
"MAIN PROGRAM: Script will repeat infinitely."}

$k=0
$notFinalIteration="true"
do {

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP

if ($repeats -ne 1){
$currentIteration = $k + 1
sout ""
sout "MAIN PROGRAM: Current iteration $currentIteration"
sout "================================================="}
if ($infiniteLoop) {
sout ""
sout "MAIN PROGRAM: New iteration in infinite loop"
sout "============================================"}

###############################################
# The Script Logging File rotates once at 5MB #
###############################################

$sizeOfExceptionLogFile = (Get-Item $exceptionLogFile).length
If($sizeOfExceptionLogFile -gt 5242880){
Remove-Item $oldExceptionLogFile
Rename-Item $exceptionLogFile $oldExceptionLogFile
$HideOutput = New-Item $exceptionLogFile -type file -force}

###########################
# Handle Cluster and Node #
###########################

if ($cluster -and $node) {
$combineClusterNode = $cluster + ":" + $node
sout "MAIN PROGRAM: Send command history to syslog for cluster $combineClusterNode"
Send-CmdHistory2Syslog $cluster $node}

################
# Handle Batch #
################

if ($batch){

$splitFile = $batch.Split("\") # Test if FILEPATH or FILENAME for -batch
If ($splitFile.Count -eq 1){$batchPath = $workingDirectory + "\" + $batch} # The string did not split, it's a filename!
If ($splitFile.Count -ne 1){$batchPath = $batch} # The string did split, it's a filepath!

$doesBatchExist = Test-Path $batchPath
if(!$doesBatchExist){"MAIN PROGRAM: No batch file at $batchPath. Exiting!";"";exit}
$batchFileContents = Get-Content $batchPath

foreach ($line in $batchFileContents){

Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP

if (!($line[0] -eq "#")) { # Not a comment line

$lineItems            = $line.Split(",")
$batchFileClusterName = $lineItems[0]
$numberOfNodes        = ($lineItems.count)-1

$j = 0
do {
Trap{TrapOut} # HANDLES THROWN EXCEPTIONS IN THIS LOOP
$batchFileNodeName       = $lineItems[$j+1]
$batchCombineClusterNode = $batchFileClusterName + ":" + $batchFileNodeName
sout "MAIN PROGRAM: BATCH: Send command history to syslog for $batchCombineClusterNode"
Send-CmdHistory2Syslog $batchFileClusterName $batchFileNodeName
$j++
} while ($j -lt $numberOfNodes)
}
}
} # END of of ($batch)

if(!$infiniteLoop){$k++}
if($k -eq $repeats){$notFinalIteration = $null}
if($Sleep -and $notFinalIteration){
$currentDateTime = Get-Date
sout "MAIN PROGRAM: $currentDateTime"
sout "MAIN PROGRAM: Sleeping for $RepeatMinutes minutes."
Start-Sleep -s $sleepTimer}

} while ($k -lt $repeats)

"" # END OF PROGRAM

Comments