Param ( [string]$ScriptVersion = "4.3" #Reporting script version ) ############################################################################################################################# 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 #Rewritten for PS1 foreach ($Character in $PrimaryValue) {$EndString+=$Character} #Set the values for the returned array #$ResultArray[0] = $PrimaryValue $ResultArray[0] = $EndString $ResultArray[1] = $CurrentLine Return $ResultArray } ########################################################################################################### function IsThisACluster ($Server2003,$Server2008R1) { #*********************************************************************************************************** #This function determines whether or not this node is part of a cluster. If it is we'll recod some data #about the cluster node members #*********************************************************************************************************** $Test = $Null $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 write-host write-host "Verifying that this device is part of a cluster..." -foregroundcolor "cyan" #Iterating through the services and if we find a running cluster service then this is a cluster foreach ($Service in $Services) { if ($Service.name -eq "ClusSvc" -and $Service.StartType -ne "Disabled") { $IsCluster = $True } } if ($IsCluster) { write-host "Device is part of a cluster." -foregroundcolor "green" } else { write-host "This device is not part of a cluster." -foregroundcolor "yellow" } return $IsCluster } ########################################################################################################### function Set-AutomaticClusterServices { #*********************************************************************************************************** #This function sets the cluster service to 'automatic' startup #*********************************************************************************************************** write-host write-host "Setting cluster service start mode to automatic..." -foregroundcolor "cyan" wmic service where "Name= 'ClusSvc'" call ChangeStartmode Automatic |out-null } ########################################################################################################### function Check-AutomaticClusterServices { #*********************************************************************************************************** #This function checks if the cluster service is set to 'automatic' startup #*********************************************************************************************************** $ClusterService = (Get-WmiObject -Class Win32_Service -Filter "Name = 'ClusSvc'" | Select-Object -Property *).StartMode write-host "Verifying cluster start mode..." -foregroundcolor "cyan" if ($ClusterService -eq "Auto") { write-host "Cluster service will start automatically." -foregroundcolor "green" return $True } else { write-host "Cluster service will not start automatically. Start mode set to $ClusterService." -foregroundcolor "red" return $False } } ############################################################################################################################# function Start-ClusterService { #*********************************************************************************************************** #This function checks the status of the cluster service and starts it if it is not set to disabled and is #not currently running #*********************************************************************************************************** $ClusterService = $Null $ClusterService = (get-service -name "ClusSvc").StartType #Gathering the start type the local device if ($ClusterService -eq "Disabled") { write-host "Cluster service is disabled. Ensure this cluster was up prior to migration." -foregroundcolor "red" } else { Start-Service clussvc write-host write-host "Starting cluster service..." -foregroundcolor "cyan" Start-Sleep -s 15 } } ########################################################################################################### function Check-ClusterServiceRunning { #*********************************************************************************************************** #This function tests to see if the cluster service is started and starts it if it exists #*********************************************************************************************************** $IsRunning = $False #Checking if the cluster service actually exists and starting if it does #Iterating through the services and if we find a running cluster service then this is a cluster $ClusterService = (get-service -name "ClusSvc").Status if ($ClusterService -eq "Running") { $IsRunning = $True } #Adding in a wait [System.Threading.Thread]::Sleep(5000) #Checking if the cluster service actually exists and starting if it does if ($IsRunning) { write-host "Cluster service is running." -foregroundcolor "green" return $True } else { write-host "Cluster service state is: "$ClusterService -foregroundcolor "red" return $False } } ########################################################################################################### function Restore-ClusterOwners { #*********************************************************************************************************** #This function pulls the previous resource owners and moves the groups back to their pre-migration state. #*********************************************************************************************************** $PreMigGroups = import-csv 'c:\rs-pkgs\MR-ClusterGroups-Premigration.csv' write-host write-host "Returning resources to their pre-migration owners..." -foregroundcolor "cyan" foreach ($resource in $PreMigGroups) { $Current = Get-ClusterGroup $Resource.Name if ($Current.OwnerNode -ne $Resource.OwnerNode) { write-host "Moving"$Resource.Name"to node"$Resource.OwnerNode"..." -foregroundcolor "cyan" Move-ClusterGroup $Resource.name -node $Resource.OwnerNode |out-null } else { write-host $Resource.Name"is already on"$Resource.OwnerNode -foregroundcolor "green" } } } ########################################################################################################### function Restore-ClusterOnline { #*********************************************************************************************************** #This function pulls the previous resource owners and moves the groups back to their pre-migration state. #*********************************************************************************************************** $PreMigGroups = import-csv 'c:\rs-pkgs\MR-ClusterGroups-Premigration.csv' foreach ($resource in $PreMigGroups) { if ($resource.State -eq "Online") { Start-ClusterGroup $Resource.name } elseif ($Resource.State -eq "Offline") { Stop-ClusterGroup $Resource.name } } } ########################################################################################################### function Verify-ClusterOwner { #*********************************************************************************************************** #This function pulls the previous resource owners and moves the groups back to their pre-migration state. #*********************************************************************************************************** $PreMigGroups = import-csv 'c:\rs-pkgs\MR-ClusterGroups-Premigration.csv' $ClusterDisposition = Get-ClusterNode | Get-ClusterGroup $ClusterCorrect = $True $FailedResources = @() write-host write-host "Verifying resource group ownership..." -foregroundcolor "cyan" foreach ($PreResource in $PreMigGroups) { foreach ($PostResource in $ClusterDisposition) { if ($PreResource.Name -eq $PostResource.Name) { if ($PreResource.OwnerNode -ne $PostResource.OwnerNode) { $ClusterCorrect =$false } } } } if ($ClusterCorrect) { write-host "All resources are verified as running on the nodes they ran on pre-migration." -foregroundcolor "green" } else { write-host "Resources are not running on the nodes they ran on pre-migration." -foregroundcolor "red" } return $ClusterCorrect } ########################################################################################################### function Check-ClusterComplete { #*********************************************************************************************************** #This function pulls the previous resource owners and moves the groups back to their pre-migration state. #*********************************************************************************************************** $AllNodes = Get-ClusterNode $RemainingNodes = "" $Count = 0 $ReturnValues = @() write-host write-host "Verifying cluster node status..." -foregroundcolor "cyan" foreach ($Node in $AllNodes) { if ($Node.State -eq "Down") { $Count++ if ($Count -gt 1) { $RemainingNodes+=", " } $RemainingNodes+=$Node.Name } } if ($Count -gt 0) { write-host "All cluster nodes are not online." -foregroundcolor "red" $ReturnValues+=$False } else { write-host "All cluster nodes are online." -foregroundcolor "green" $ReturnValues+=$True } $ReturnValues+=$RemainingNodes return ($ReturnValues) } ########################################################################################################### function PSOGenerator ($Severity, $Visibility, $Context, $Message) { #*********************************************************************************************************** #This function takes parameters and returns a PSO for collection into an end-report. #*********************************************************************************************************** #Building the PSO. $FailurePSO = [PSCustomObject]@{ severity = $Severity; visibility = $Visibility; context = $Context; message = $Message } return $FailurePSO } ########################################################################################################### function JSON-Write ($Items,$Version,$Hard,$Soft,$Success) { #*********************************************************************************************************** #This function takes an array and outputs the values in JSON format. #*********************************************************************************************************** if ($Items.Count -gt 0) { write-host "[" write-host "`t{" Write-Output "`"success`": $Success, " Write-Output "`"soft`": $Soft, " Write-Output "`"hard`": $Hard , " Write-Output "`"scriptversion`": $ScriptVersion " write-host "`t}," for ($i=0; $i -lt $Items.count; $i++) { write-host "`t{" write-host "`t`t`"severity`": `"" -nonewline write-host $Items[$i].Severity -nonewline write-host "`"," write-host "`t`t`"visibility`": `"" -nonewline write-host $Items[$i].visibility -nonewline write-host "`"," write-host "`t`t`"context`": `"" -nonewline write-host $Items[$i].context -nonewline write-host "`"," write-host "`t`t`"message`": `"" -nonewline write-host $ITems[$i].message -nonewline write-host "`"" write-host "`t}" -nonewline #If we haven't hit zero failure count then we'll output a comma to take care of the next dictionary. Otherwise we terminate without a comma to meet the specification. if ($i -gt 0) {write-host ","} else {write-host} } write-host "]" } } ########################################################################################################### $ResponseArray = @() #Array to carry all check responses $Cluster = $Null #Is this device a cluster? $ClusterServiceRunning = $False #Is the cluster service running on the local node $ClusterComplete = $False #Are all nodes of the cluster running? $DownNodes = "" #List of nodes that are still down $Hard = 0 #Number of hard failure operations $Soft = 0 #Number of soft failure operations $Success = 0 #Number of successful operations #******************************************************************************* #Checking cluster status and running cluster-specific items if this device is in a cluster. If this fails we'll log to the failure array $Cluster = IsThisACluster if ($Cluster -eq $Null) { $Cluster = IsThisACluster if ($Cluster -eq $Null) { $ResponseArray+= PSOGenerator "hard" "public" "qc" "This device does not appear to be a cluster." $Hard++ } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster check successful. $Cluster" $Success++ } } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster check successful. $Cluster" $Success++ } #******************************************************************************* #If this is a cluster, attempt to restore online status if ($Cluster) { Import-Module failoverclusters #******************************************************************************* #Start the cluster service if it is not already started Start-ClusterService $ClusterServiceRunning = Check-ClusterServiceRunning if (!($ClusterServiceRunning)) { write-host "Retrying..." -foregroundcolor "yellow" Start-ClusterService $ClusterServiceRunning = Check-ClusterServiceRunning if (!($ClusterServiceRunning)) { $ResponseArray+= PSOGenerator "hard" "public" "qc" "This device is a cluster but the cluster service is not starting." $Hard++ } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster service started." $Success++ } } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster service started." $Success++ } Set-AutomaticClusterServices #Checking if this was successful $ClusterServiceGood = Check-AutomaticClusterServices if (!($ClusterServiceGood)) { #First time through failure, retrying Set-AutomaticClusterServices $ClusterServiceGood = Check-AutomaticClusterServices #Checking if our retry was successful if (!($ClusterServiceGood)) { #Logging final failure to set cluster services $ResponseArray+= PSOGenerator "hard" "public" "qc" "Unable to set cluster services to automatic." $Hard++ } else { #Cluster service change successful. Logging. $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster services set to automatic." $Success++ } } else { #Cluster service change successful. Logging. $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster services set to automatic." $Success++ } #******************************************************************************* #Verify that all cluster nodes are up $ClusterComplete,$DownNodes = Check-ClusterComplete if (!($ClusterComplete)) { write-host "Retrying..." -foregroundcolor "yellow" $ClusterComplete,$DownNodes = Check-ClusterComplete if (!($ClusterComplete)) { $ResponseArray+= PSOGenerator "hard" "public" "qc" "Not all nodes are up. Remaining nodes are: $DownNodes" $Hard++ } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "All nodes are up." $Success++ } } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "All nodes are up." $Success++ #******************************************************************************* #Check to see if the cluster resources are already running where they need to be. If they're not then move them appropriately them Restore-ClusterOwners $ClusterGroupsRestored = Verify-ClusterOwner if (!($ClusterGroupsRestored)) { write-host "Retrying..." -foregroundcolor "yellow" Restore-ClusterOwners $ClusterGroupsRestored = Verify-ClusterOwner if (!($ClusterGroupsRestored)) { $ResponseArray+= PSOGenerator "hard" "public" "qc" "Unable to restore cluster resource group ownership." $Hard++ } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster resource group ownership restored." $Success++ } } else { $ResponseArray+= PSOGenerator "success" "public" "qc" "Cluster resource group ownership restored." $Success++ } } } JSON-Write $ResponseArray $Hard $Soft $Success