A PowerShell script that scans a Windows Failover Cluster environment, checks whether cluster resources are running on their preferred node vs. a secondary node, attempts to move any that have drifted back to the preferred node, and emails a summary report.
- Pulls all cluster resources via a custom cluster-query function (see Dependency below).
- Splits resources into "checked" and "excluded" sets based on a configurable name pattern (useful for excluding clusters — like SQL Availability Groups — that manage their own failover separately and shouldn't be touched by this script).
- For any checked resource currently owned by a node ending in
-2, attempts to move it back to the corresponding-1node viaMove-ClusterGroup, but only if the cluster service is confirmed running on that node first. - Logs a full before/after resource table to a timestamped log file.
- Sends a summary email — success if everything ended up on the
-1node, critical alert if anything needed (or failed) a move.
This script calls Get-SITClusterResourceOwner from a custom internal module — that module isn't included here since it was written for a specific environment. You'll need to write your own equivalent that returns objects with at least these properties:
| Property | Description |
|---|---|
Cluster |
Cluster name |
ResourceName |
Cluster resource/group name |
OwnerNode |
Current owning node name |
State |
Resource state (e.g. Online) |
A straightforward way to build this yourself using the built-in FailoverClusters module:
function Get-YourClusterResourceOwner {
Get-ClusterGroup | Select-Object @{N='Cluster';E={$_.Cluster.Name}}, Name, OwnerNode, State
}Adjust property names in the script if yours differ (ResourceName maps to Name in the built-in cmdlet output, for example).
The script assumes a naming convention where cluster nodes end in -1 (preferred) or -2 (secondary) — e.g. CLUSTERNODE-1 and CLUSTERNODE-2. If your environment doesn't use this convention, you'll need to adjust the matching logic (-like "*-2" / -replace "-2", "-1") to fit your own node naming.
| What | Where | Change to |
|---|---|---|
| Cluster resource query module | Import-Module "C:\Scripts\YourModule\YourModule.psm1" |
Path to your own module (see Dependency above) |
| Exclusion pattern | -ExcludePattern parameter |
Regex matching any clusters/resources/nodes you want to exclude from the -1/-2 check |
| SMTP relay | $smtpServer |
Your organization's mail relay |
| Sender address | $from |
Your monitoring sender address |
| Recipient address | $to |
Your team's alert email |
| Log path | $logPath |
A UNC path or local path your account can write to |
.\ClusterResourceOwner.ps1
# or, to exclude a specific set of clusters from the -1/-2 check:
.\ClusterResourceOwner.ps1 -ExcludePattern "MYSQLCLUSTER|MYOTHERCLUSTER"Exit codes: 0 on success (everything on the preferred node), 1 if any resources were found on the secondary node (regardless of whether the move succeeded) — useful for wiring into a monitoring pipeline.
MIT License — feel free to use and modify for your environment.