Param ( [string]$ScriptVersion = "5.3.2", #Reporting script version [string]$OldSNIP, #The old ServiceNet IP [string]$DpDevice, #The Datapipe device number if any, if not none [array]$BackupNetworks #The datapipe backup networks ) ############################################################################################################################# Function ParseLine ($Line, $StartPosition, $PrimaryBreakCharacter, $AlternateBreakCharacter) { #*********************************************************************************************************** #This function is passed a string, a position in the string to start and two characters on which if #encountered the processing of the string will stop. This is used to extract information from a messy #and/or inconsistent string. #Essentially you pass it a string and tell it where you want it to stop and on what character (up to two) #you want it to stop. It will return all the characters between where you told it to start and the stop #character, not including the stop character. It also returns the position in the string where it #encountered the stop character. #*********************************************************************************************************** $ResultArray=@("string",2) #Array for the parse results $PrimaryValue=@() #Array to hold individual characters until we join it as a string $CurrentPosition = 0 #Placeholder for the position in the line being processed $LineLength = $Line.Length - 1 #Array designated length of the line being processed #Processing the line character by character for ($ParseLoop = $StartPosition; $ParseLoop -le $LineLength; $ParseLoop++) { #If the character we're looking at matches either break character we're done If ($Line[$ParseLoop] -eq $PrimaryBreakCharacter -or $Line[$ParseLoop] -eq $AlternateBreakCharacter) { $CurrentPosition = $ParseLoop $ParseLoop = $LineLength } Else { #Otherwise add to the string array $PrimaryValue += $Line[$ParseLoop] } } #Push the character array into a string #$PrimaryValue = -join $PrimaryValue #Can't use -join because PS1 doesn't have it foreach ($Character in $PrimaryValue) {$EndString+=$Character} #Set the values for the returned array $ResultArray[0] = $EndString $ResultArray[1] = $CurrentPosition Return $ResultArray } ########################################################################################################### function Check-rspkgs { #*********************************************************************************************************** #This function determines whether the C:\rs-pkgs file exists and is writable. #*********************************************************************************************************** $ReturnValue = $False #Simple test to see if the directory exists if (!(test-path 'C:\rs-pkgs')) { #Create it if it doesn't new-item 'C:\rs-pkgs' -type directory |out-null } #Test write into the rs-pkgs directory new-item 'C:\rs-pkgs\MigTest.txt' -type file |out-null #If the file exists then the directory is good to proceed if (test-path 'C:\rs-pkgs\MigTest.txt') { #Setting the value we're going to pass and removing the test file $ReturnValue = $True remove-item 'C:\rs-pkgs\MigTest.txt' } #If the error logging directory doesn't exist, create it if (!(test-path "C:\rs-pkgs\Errors")) { new-item 'C:\rs-pkgs\Errors' -type directory |out-null } return $ReturnValue } ########################################################################################################### function Get-OS { #*********************************************************************************************************** #This function gathers the uptime in days and returns it. #*********************************************************************************************************** $OS = Get-WmiObject win32_operatingsystem Return $OS.Caption } ########################################################################################################### function Get-UAC { #*********************************************************************************************************** #This function determines whether UAC is enabled returns true or false. #*********************************************************************************************************** $Return = $False $UACStatus = (Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System) if ($UACStatus.EnableLUA -ne 0) {$Return = $True} #if ($UACStatus.ConsentPromptBehaviorAdmin -ne 0) {$Return = $True} Return $Return } ########################################################################################################### function Get-ORSNUsage { #*********************************************************************************************************** #This function determines if there is a route to the Object Rocket subnet and returns true/false. If true #it's likely that the customer is using Object Rocket over ServiceNet #*********************************************************************************************************** $Routes = route print #Gathering the routes configured on the device $LinesInRoutes = $Routes.length-1 #Total array designated length of the route table $CurrentLine = 0 #Marker to indicate where we are $LineInProcess = $Routes[$CurrentLine] #Variable will contain each line of the route table $ObjectRocketUse = $False #Assuming customer is not using Object Rocket over ServiceNet #Going through the lines of the route table while ($CurrentLine -le $LinesInRoutes) { if ($LineInProcess -like '*10.188.0.0*') { $ObjectRocketUse = $True } $CurrentLine++ $LineInProcess = $Routes[$CurrentLine] } return $ObjectRocketUse } ########################################################################################################### function Get-ServiceNetNICName ($OldIP) { #*********************************************************************************************************** #This function will determine NIC name of the NIC being used for ServiceNet communication. #*********************************************************************************************************** #Gathering the NIC configs $NICConfig = netsh interface ip show config #Getting the array designated length of the config $NICConfigLength = $NICConfig.length-1 #Setting the position within the NIC config. $CurrentLine = 0 $ServiceNetNICName = $Null #Proceeding through the cache While ($CurrentLine -le $NICConfigLength) { #Script would throw errors with a null line so we skip here if (!($NICConfig[$CurrentLine] -eq $null)) { #This line contains a NIC name if ($NICConfig[$CurrentLine][0] -eq "C") { #Scrape the line up to the NIC name $Yield,$Position = ParseLine $NICConfig[$CurrentLine] 0 "`"" #Advance a character and scrape the NIC name $Position++ $NICName,$Position = ParseLine $NICConfig[$CurrentLine] $Position "`"" "*" $CurrentLine++ } #Line contains an IP address if ($NICConfig[$CurrentLine][4] -eq "I" -and $NICConfig[$CurrentLine][5] -eq "P" ) { #Scrape to the beginning of the IP address $IPAddress,$Position = ParseLine $NICConfig[$CurrentLine] 4 "1" "*" #Scrape the IP address based on the $Position parameter returned $IPAddress,$Position = ParseLine $NICConfig[$CurrentLine] $Position " " "*" #If the old IP is identified we've got our NIC if ($IPAddress -eq $OldIP) { #Assign the current NIC name to the one we're going to return and set the line to the end of the loop control variable so we can break the loop $ServiceNetNICName = $NICName $CurrentLine = $NICConfigLength } } } #Advancing to the next line $CurrentLine++ } return $ServiceNetNICName } ########################################################################################################### function Log-Local ($LogFile,$Message) { #*********************************************************************************************************** #This function takes an array and logs locally to file. #*********************************************************************************************************** $OutFile = "C:\rs-pkgs\"+$LogFile add-content $OutFile $Message } ########################################################################################################### function IsServer2003 { #*********************************************************************************************************** #This function determines if the subject server is running Server 2003 or not. #*********************************************************************************************************** $OSTest = wmic os list status if ($OSTest -like "*2003*") { return $True } else { Return $False } } ########################################################################################################### function IsServer2008R1 { #*********************************************************************************************************** #This function determines if the subject server is running Server 2008 R1 or not. #*********************************************************************************************************** $OSTest = wmic os list status $ReturnValue = $False if ($OSTest -like "*2008*") { if (!($OSTest -like "*R2*")) { $ReturnValue = $True } } Return $ReturnValue } ########################################################################################################### function IsClusterInstalled { #*********************************************************************************************************** #This function determines whether or not this node has cluster service installed. #*********************************************************************************************************** $Services = get-service #Gathering the services on the local device $IsCluster = $False #Variable for determining if there's a cluster or not. Starting it as false first and we'll change it if we find a valid cluster service #Iterating through the services and if we find a running cluster service then this is a cluster member foreach ($Service in $Services) { if ($Service.name -eq "ClusSvc") #if service is installed, mark cluster iscluster true { $IsCluster = $True } } return $IsCluster } ########################################################################################################### function Get-ClusterResources { #*********************************************************************************************************** #This function gathers cluster resources into an array and returns it for use in multiple places later #*********************************************************************************************************** Import-Module failoverclusters $Resources = @() #holds the list of resources on the cluster $Resources = get-clusterresource #holds the list of resources on the cluster return $Resources } ########################################################################################################### function Get-ClusterDetect ($Server2003,$Server2008R1) { #*********************************************************************************************************** #This function determines whether or not this node is part of a cluster. Also checks if cluster is a type we can easily query with powershell or not. #*********************************************************************************************************** $ServiceStatus = (Get-Service -name clussvc).Status $ClusterDetection = [PSCustomObject]@{ # Text block containing a human-readible status of the cluster. Status = "Installed, WARNING: Manual discovery required."; # Is the cluster queryable via powershell? (Default to no and change to yes if PS command completes successfully) PSQueryable = $false; # Default these to 0, we will detect them later with another service call, once we know powershell works against the cluster. TotalResources = 0; UpResources = 0; DownResources = 0 } # if the node has the service in any state other than running, set the status accordingly,and skip anything further if ($ServiceStatus -ne "Running") { $ClusterDetection.Status = "Installed, Service not running, WARNING: Manual discovery required." } else { #If the node is 2003 or 2008R1, manual cluster intervention is required to retrieve the information If ($Server2003 -or $Server2008R1) { $ClusterDetection.Status = "Installed, Running, WARNING: Pre-2008R2 OS Detected, manual discovery required." } else { # by this time we should have proven we have a good running cluster that is 2008R2 or greater, so we can go ahead with throwing some powerhell commands at it. If the commands fail, we should have an alternative human-readable warning for manual intervention already set. Import-Module failoverclusters $a = Get-Cluster if ($a -ne $null) { $ClusterDetection.Status = "Installed, Running, Test powershell query successful" $ClusterDetection.PSQueryable = $true } } } return $ClusterDetection } ########################################################################################################### function Get-ClusterIPResources ($Resources) { #*********************************************************************************************************** #This function returns the status and information about cluster IPs for a given list of resources #*********************************************************************************************************** $ClusterIPs = @() #Array to hold the IP resources foreach ($r in $Resources) # Step through each resource found { if ($r.ResourceType -match "IP Address") #If a resource is of type IP Address, we need to log it to the $ClusterIPs variable, for later use { $add = ($r| Get-ClusterParameter |where-object Name -eq "Address").value $sub = ($r| Get-ClusterParameter |where-object Name -eq "SubnetMask").value $net = ($r| Get-ClusterParameter |where-object Name -eq "Network").value $ClusterIPs += [PSCustomObject]@{ Name=$r.Name; Group=$r.OwnerGroup; Address=$add; SubnetMask=$sub; Network=$net } } } return $ClusterIPs } ########################################################################################################### function Get-ClusterOfflineResources ($Resources) { #*********************************************************************************************************** #This function returns an array of any resources that are not in state "online" #*********************************************************************************************************** $OfflineResources = @() # Array holding the names and status of offline resources (used for count also) foreach ($r in $Resources) # Step through each resource found { if ($r.State -ne "Online") # If an non-online resource is found, addit to the array { $OfflineResources += [PSCustomObject]@{ Name=$r.name; Group=$r.OwnerGroup; State=$r.State; Type=$r.ResourceType; Cluster=$r.Cluster} } } return $OfflineResources } ########################################################################################################### #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#- #BEGIN SCRIPT #To those who come later: we've had to code for the lowest common denominator, which is PSv1, which doesn't have all the features of later versions. Judge not, lest ye be judged. ########################################################################################################### #Initializing variables or setting them to purposefully erroneous values to catch failures $RspkgsExists = $Null #Placeholder variable for the existence of C:\rs-pkgs $UAC = $Null #This is set to null and we should have a true/false value if the check is successful. This will flag an automation failure if it's still null on evaluation $DaysUp = -1 #Obviously we wouldn't have an uptime of -1 so if this gets checked later and we don't have a valid uptime we'll flag an automation failure $OSVersion = $Null #We should get a value of some sort for OS version so flag an automation failure if this is still null $UptimeFailure = $Null #This is whether the device has been up for 300+ days. It should have some value so we set it to null and can flag automation failures if it doesn't. $PendingUpdates = -1 #The minimum number of pending updates should be zero so if this is evaluated later and is -1 we know we've had an automation failure $ObjectRocketPossible = $Null #Have we detected a static route using the Object Rocket subnet? If so, flag for analysisand if no value flag an automation failure. $ServiceNetComms = $Null #Does it appear that the cusotmer is using ServiceNet for communication. Flag if so and if no value flag an automation failure. $Hard = 0 #Number of hard errors $Soft = 0 #Number of soft errors $Success = 0 #Number of successes $LogSource = $Null #Does the log source exist? $ServiceNetNICName = $Null #Placeholder for the ServiceNet NIC name # Following variables used for cluster detection routines $Cluster = $Null #Is this device a cluster? $ClusterStatus = @() #Cluster status - includes running and type, # of total/running/nonrunning resources. see function for full description $ClusterResources = @() # Array to hold ALL of the cluster resources, for use by several other modules $ClusterIPResources = @() #Array to hold IP resources and information about them $ClusterOfflineResources = @() #Array to hold offline cluster resources and information about them $ClusterNodes = @() #Array to hold offline cluster nodes and information about them $ServerIs2003 = IsServer2003 #Is the server 2003 $ServerIs2008R1 = IsServer2008R1 #Is the server 2008 R1 ########################################################################################################### #CSV variables - These variables are what we use to create comma separated values for inclusion in our cluster detection JSON creation $CSVNodes = "" #List of nodes $CSVStatuses = "" #Node statuses (up/down) $CSVOfflineNames = "" #List of offline resources $CSVOfflineStates #Types of offline resources $CSVIPs = "" #List of IPs configured on the cluster $CSIPSubnets = "" #Subnet masks for the IPs configured on the cluster $CSIPNetNames = "" #The network names associated with the IPs configured on the cluster $CSVIPOwners = "" #Current owners of the cluster IPs $CSVIPResNames = "" #The names of the offline resources #******************************************************************************* #Testing for the existence of rs-pkgs and creating it if it's not there. If this fails we'll log to the failure array $RspkgsExists = Check-rspkgs if (!($RspkgsExists)) { $RspkgsSeverity = "hard" $RspkgsVisibility = "public" $RspkgsContext = "auditing" $RspkgsMessage = "Unable to verify C rs-pkgs. Directory still does not exist." $Hard++ } else { $RspkgsSeverity = "success" $RspkgsVisibility = "public" $RspkgsContext = "auditing" $RspkgsMessage = "C rs-pkgs exists and is writable." $Success++ } #Local logging Log-local "ScriptLog - 2-4-WeekRun.txt" "Script Version:`t$ScriptVersion" #******************************************************************************* #Logging received variables Log-local "ScriptLog - 2-4-WeekRun.txt" "Parameters received." Log-local "ScriptLog - 2-4-WeekRun.txt" "Script Version:`t`t$ScriptVersion" Log-local "ScriptLog - 2-4-WeekRun.txt" "Old SNIP:`t$OldSNIP" Log-local "ScriptLog - 2-4-WeekRun.txt" "DP Device:`t$DpDevice" Log-local "ScriptLog - 2-4-WeekRun.txt" "Backup Networks:`t$BackupNetworks" #******************************************************************************* #If the device is a Datapipe device and no source backup IP exists #Identify the network address for each IP enabled adapter #Check that network against the list of Backup Networks sent via aric #If there is a match write that adapters IP to the source backup IP variable if (($DpDevice -ne 0) -and ($DpDevice -ne "none")) { if ($OldSNIP -eq "none") { $IpNetworks = get-wmiobject win32_networkadapterconfiguration -filter ipenabled=true foreach ($IpNetwork in $IpNetworks) { $IpAddress = $IpNetwork.ipaddress[0] $IpAddress = [ipaddress]$IpAddress.trim() $IpSubnet = $IpNetwork.IpSubnet[0] $IpSubnet = [ipaddress]$IpSubnet.trim() $NetworkAddress = ([ipaddress]($IpAddress.Address -band $IpSubnet.Address)).IPAddressToString foreach ($BackupNetwork in $BackupNetworks) { if ($BackupNetwork -eq $NetworkAddress) { $OldSNIP = $IpAddress.IPAddressToString } } } } } #******************************************************************************* #Gathering UAC status $UAC = Get-UAC #If UAC is enabled then send to the PSOGenerator function to be collected if ($UAC) { $UACSeverity = "hard" $UACVisibility = "public" $UACContext = "qc" $UACMessage = "Device has User Access Control enabled. This prevents our automated shutdown script from running and will require the device to be manually shut down ahead of the scheduled shut down for automation." $Hard++ } else { $UACSeverity = "success" $UACVisibility = "public" $UACContext = "qc" $UACMessage = "UAC is deactivated." $Success++ } #Flag automation as failed if there is no value and send to our PSOGenerator if ($UAC -eq $Null) { $UACSeverity = "hard" $UACVisibility = "public" $UACContext = "qc" $UACMessage = "UAC status check failed." $Hard++ } #******************************************************************************* #Gathering OS version $OSVersion = Get-OS #******************************************************************************* #Attempting to determine if the customer is using Object Rocket over ServiceNet $ObjectRocketPossible = Get-ORSNUsage #If the device has a route to the Object Rocket infrastructure send to the PSOGenerator function as a soft failure if ($ObjectRocketPossible) { $ORSeverity = "soft" $ORVisibility = "public" $ORContext = "qc" $ORMessage = "It appears that this device may be set up for communication to Object Rocket services over ServiceNet. Please verify this with the customer and work with them to whitelist the new ServiceNet IPs prior to migration. You can obtain the new ServiceNet IPs from the migration team (using ticket queue for DC Migrations). If this does not occur prior to the customer's scheduled migration date, there will be no connectivity to Object Rocket once the device is online in the destination DC." $Soft++ } else { $ORSeverity = "success" $ORVisibility = "public" $ORContext = "qc" $ORMessage = "Device is not using Object Rocket services over ServiceNet." $Success++ } #Flag automation as failed if there is no value and send to our PSOGenerator if ($ObjectRocketPossible -eq $Null) { $ORSeverity = "hard" $ORVisibility = "public" $ORContext = "qc" $ORMessage = "Object Rocket route check failed." $Hard++ } #If the old ServiceNet IP has been submitted we'll do some additional checks to make sure that the NSI database is accurate if ($OldSNIP) { #If we've been submitted an value of "none" that means there's no currently configured ServiceNet IP on the device and we don't need to audit any further if ($OldSNIP -eq "none") { $SNIPSeverity = "success" $SNIPVisibility = "public" $SNIPContext = "audit" $SNIPMessage = "No source ServiceNet IP allocated to this server." $Success++ } else { #If we get a value then we'll make sure that the value we're expecting for the currently configured ServiceNet IP is actually on the server #Gathering the NIC name $ServiceNetNICName = Get-ServiceNetNICName $OldSNIP #Error checking if ($ServiceNetNICName -eq $Null) { #Re-checking if we have a problem the first time $ServiceNetNICName = Get-ServiceNetNICName $OldSNIP if ($ServiceNetNICName -eq $Null) { #Erroring if we fail at this twice $SNIPSeverity = "hard" $SNIPVisibility = "public" $SNIPContext = "audit" $SNIPMessage = "ServiceNet IP $OldSNIP was not found on this server." $Hard++ } else { #if good this time, log success $SNIPSeverity = "success" $SNIPVisibility = "public" $SNIPContext = "audit" $SNIPMessage = "ServiceNet IP $OldSNIP found on this server." $Success++ } } else { #if good this time, log success $SNIPSeverity = "success" $SNIPVisibility = "public" $SNIPContext = "audit" $SNIPMessage = "ServiceNet IP $OldSNIP found on this server." $Success++ } } } else { #Logging success $SNIPSeverity = "success" $SNIPVisibility = "public" $SNIPContext = "qc" $SNIPMessage = "No ServiceNet IP was submitted." $Hard++ } #******************************************************************************* #Checking cluster status and running cluster-specific items if this device is in a cluster. $Cluster = IsClusterInstalled # Determine if cluster service is installed if ($Cluster) # Only run the rest of cluster stuff if it the node has cluster service (otherwise leave completely blank) { $ClusterStatus= Get-ClusterDetect $ServerIs2003 $ServerIs2008R1 # Determine if this cluster is 2008R2 or newer and some quick info on # of resources up/down if ($ClusterStatus.PSQueryable) # if PSQueryable is true, assume the cluster is queryable via powershell. If test query failed for any reason, step out of the rest of the process. { $ClusterResources = Get-ClusterResources # Returns an array of all cluster resources $ClusterIPResources = Get-ClusterIPResources $ClusterResources # Should return an array of all cluster IPs and information about each $ClusterNodes = Get-ClusterNode # Let's also get an array of the node names just for visual comparison if needed, direct from powershell $ClusterOfflineResources = Get-ClusterOfflineResources $ClusterResources $ClusterStatus.TotalResources = $ClusterResources.count # Total # of resources listed $ClusterStatus.UpResources = ($ClusterResources | Where-Object state -eq "Online").count # total online resources $ClusterStatus.DownResources = ($ClusterOfflineResources|Measure-Object).Count # total # of logged offline resources if ($ClusterResources.count -gt 0) # if any resources were found, let's verify they're counted correctly and change the clusterstatus text accordingly { if ($ClusterStatus.DownResources -lt 1 -and $ClusterResources.count -eq $ClusterStatus.UpResources) # If there aren't any offline resources listed, and the online count matches the # of resources (which is greater than 0), assume detection is successful and everything is up { $ClusterStatus.Status = "Installed, Running, Detection successful, No offline resources found" } else # By this point we have discovered some resources but not all are online { $ClusterStatus.Status = "Installed, Running, Detection Successful, Offline resources found and recorded" # Lets compare the total number of resources found to the # that state online and the # that we calculated that are NOT online, and error if there is a discrepancy to have someone look at it if ($ClusterStatus.TotalResources -ne ($ClusterStatus.UpResources + $ClusterStatus.DownResources)) { $ClusterStatus.Status = "Installed, Running, Detection Successful, WARNING: Resources counts did not verify successfully. Please manually verify any any down resources for the cluster." } } } else # if no resources were found (resources.count not greater than 0), assume there was an error gathering the information and log as such { $ClusterStatus.Status = "Installed, Running, WARNING: Error during detection, manual discovery required" } } } #******************************************************************************* # Cluster status output #******************************************************************************* #Writing RBA output write-output "RBA START STATUS:" write-output "{" write-output "`"success`": $Success, " write-output "`"soft`": $Soft, " write-output "`"hard`": $Hard , " write-output "`"scriptversion`": $ScriptVersion " write-output "}" write-output "RBA END STATUS:" write-output "RBA START DATA:" write-output "[" write-output "`t{" write-output "`t`t`"scriptversion`": `"$ScriptVersion`" " write-output "`t}," #rs-pkgs Output write-output "`t{" write-output "`t`t`"testname`": `"rspkgs`"," write-output "`t`t`"severity`": `"$RspkgsSeverity`"," write-output "`t`t`"visibility`": `"$RspkgsVisibility`"," write-output "`t`t`"context`": `"$RspkgsContext`"," write-output "`t`t`"message`": `"$RspkgsMessage`"" write-output "`t}," #UAC Output write-output "`t{" write-output "`t`t`"testname`": `"UAC`"," write-output "`t`t`"severity`": `"$UACSeverity`"," write-output "`t`t`"visibility`": `"$UACVisibility`"," write-output "`t`t`"context`": `"$UACContext`"," write-output "`t`t`"message`": `"$UACMessage`"" write-output "`t}," #ServiceNet IP detection output write-output "`t{" write-output "`t`t`"testname`": `"ServiceNet`"," write-output "`t`t`"severity`": `"$SNIPSeverity`"," write-output "`t`t`"visibility`": `"$SNIPVisibility`"," write-output "`t`t`"context`": `"$SNIPContext`"," write-output "`t`t`"message`": `"$SNIPMessage`"" write-output "`t}," #Object Rocket Output write-output "`t{" write-output "`t`t`"testname`": `"ObjectRocket`"," write-output "`t`t`"severity`": `"$ORSeverity`"," write-output "`t`t`"visibility`": `"$ORVisibility`"," write-output "`t`t`"context`": `"$ORContext`"," write-output "`t`t`"message`": `"$ORMessage`"" #******************************************************************************* #Checking cluster status and running cluster-specific items if this device is in a cluster. if ($Cluster) # if cluster service is installed, output this section. otherwise, output NOTHING AT ALL { #Writing Cluster Information headers write-output "`t}," write-output "`t{" write-output "`t`t`"ClusterInformation`":" write-output "`t`t{" #Assembling the list of cluster nodes if ($ClusterNodes -ne $Null) # only do this step if cluster nodes are not blank. { $IndexCounter = 0 # A counter to increment index for devices with multiple nodes/offline resources/IPs foreach ($Node In $ClusterNodes) #Compiling a CSV list of nodes { $IndexCounter++ # Increment IndexCounter each time $CSVNodes+=$Node.Name $CSVStatuses+=$Node.State if ($IndexCounter -ne $ClusterNodes.Length) #Determine if this is the last element or not. if not, keep writing with a comma. { $CSVNodes+="," $CSVStatuses+="," } } } #Output of nodes and node information write-output "`t`t`t`"NodeNames`": `"$CSVNodes`"," write-output "`t`t`t`"NodeStatuses`": `"$CSVStatuses`"," write-output "`t`t`t`"ClusterStatus`": `"$($ClusterStatus.Status)`"," write-output "`t`t`t`"ClusterPSQueryable`": `"$($ClusterStatus.PSQueryable)`"," write-output "`t`t`t`"ClusterResTotal`": `"$($ClusterStatus.TotalResources)`"," write-output "`t`t`t`"ClusterResUp`": `"$($ClusterStatus.UpResources)`"," write-output "`t`t`t`"ClusterResDown`": `"$($ClusterStatus.DownResources)`"," #Assembling the list of down cluster resources if ($ClusterOfflineResources -ne $Null) # only do this step if there are any offline resources. { $IndexCounter = 0 # A counter to increment index for devices with multiple nodes/offline resources/IPs foreach ($Offline In $ClusterOfflineResources) # step through each offline resource found { $IndexCounter++ # Increment IndexCounter each time $CSVOfflineNames+=[string]$Offline.Name $CSVOfflineStates+=[string]$Offline.State $CSVOfflineTypes+=[string]$Offline.Type if ($IndexCounter -ne $ClusterOfflineResources.Length) #Determine if this is the last element or not. if not, keep writing with a comma. { $CSVOfflineNames+="," $CSVOfflineStates+="," $CSVOfflineTypes+="," } } } #Output of offline resource information write-output "`t`t`t`"ClusterOfflineNames`": `"$CSVOfflineNames`"," write-output "`t`t`t`"ClusterOfflineStates`": `"$CSVOfflineStates`"," write-output "`t`t`t`"ClusterOfflineTypes`": `"$CSVOfflineTypes`"," #Assembling a list of cluster IPs if ($ClusterIPResources -ne $Null) # only do this step if cluster IPs are not blank. if blank, should write nothing at all. { $IndexCounter = 0 # A counter to increment index for devices with multiple nodes/offline resources/IPs foreach ($IP in $ClusterIPResources) # step thru each Cluster IP resource found { $IndexCounter++ # Increment IndexCounter each time $CSVIPs+=$IP.Address $CSVIPSubnets+=$IP.SubnetMask $CSVIPNetNames+=$IP.Network $CSVIPOwners+=$IP.Group -replace "\\","\\" $CSVIPResNames+=$IP.Name if ($IndexCounter -ne $ClusterIPResources.Length) #Determine if this is the last element or not. if not, keep writing with a comma. { $CSVIPs+="," $CSVIPSubnets+="," $CSVIPNetNames+="," $CSVIPOwners+="," $CSVIPResNames+="," } } } #Output of IP resource information write-output "`t`t`t`"ClusterIPs`": `"$CSVIPs`"," write-output "`t`t`t`"ClusterIPSubnets`": `"$CSVIPSubnets`"," write-output "`t`t`t`"ClusterIPNetNames`": `"$CSVIPNetNames`"," write-output "`t`t`t`"ClusterIPOwners`": `"$CSVIPOwners`"," write-output "`t`t`t`"ClusterIPResNames`": `"$CSVIPResNames`"" #Finishing off JSON write-output "`t`t}" write-output "`t}" } else { write-output "`t}" } #Writing footer write-output "]" write-output "RBA END DATA:" Log-local "ScriptLog - 2-4-WeekRun.txt" "ClusterDetect:`t$(Get-Date -format R)"