Preferred Owner in Failover Cluster. Part 2

Preffered Owner

  • Part 1 covers some theory and GUI configuration.
  • Part 2 will focus on setting Preferred Owners with Powershell.
  • Part 3 will explain logic behind Possible Owners in PowerShell function.
  • Part 4 will cover the last step – setting anti-affinity groups.
  • Part 5 will describe reporting of current configuration in the cluster.

    In first I’ve covered some basic information about different advanced assignment policies for VM in a Failover cluster. This time I’d like to use some PowerShell to set ‘preferred owner’ for my VMs.

    General concept

    As before – I like my functions to run with alternative Credentials. If Credential parameter is passed, then PSSession will use it. Else, it will use current user context. Thanks to this I can code, run and test using VSCode on my workstation without the need to run with scissors admin rights. This does complicate code a bit but gives me flexibility. I choose to use Invoke-Command, get necessary data and parse/transform/check in my main session. This way I don’t need all RSAT cmdlets on my workstation and this code is portable. It should also work from any system (Linux/MacOS/Windows)!

    If it’s not possible to create PSSession – no code will do anything so I’ll error here:


    [Parameter(Mandatory, HelpMessage = 'Provide Cluster Name',
    ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [ValidateNotNullOrEmpty()]
    [System.String]
    $Cluster,
    [Parameter(Mandatory = $false, HelpMessage = 'Provide Credentials for Cluster',
    ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [System.Management.Automation.PSCredential]
    $Credential,
    #region PSSession parameters
    $connectionParams = @{
    ComputerName = $Cluster
    }
    if ($PSBoundParameters.ContainsKey('Credential')) {
    $connectionParams.Credential = $Credential
    Write-Verbose Message "Processing with provided credentials {$($Credential.UserName)}"
    }
    else {
    Write-Verbose Message "Processing with default credentials of user {$($env:USERNAME)}"
    }
    $ClusterSession = New-PSSession @connectionParams ErrorAction Stop

    To reset Preferred Owners to defaults (no preferred owner ) I’ll need to pass empty string or $null.

    If $PreferredOwner is provided, I need to be able to verify if provided Nodes are correct – if $Cluster does have members with those names. To do this I’ll use Compare-Object with -IncludeEqual. It will compare each node ($preferredOwner) with all cluster members ($nodesInCluster) and give proper verbose information:


    #region Validate Preferred Owner
    $nodesInCluster = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterNode | Select-Object ExpandProperty Name
    }
    #null or empty string – reset to defaults
    if (-not $PreferredOwner) {
    Write-Verbose Message "Provided no Preferred Owner. Will reset to default setting"
    $PreferredOwner = ''
    }
    #check if given hosts given in $PreferredOwner are members of $Cluster
    else {
    switch (Compare-Object ReferenceObject @($nodesInCluster) DifferenceObject @($PreferredOwner) IncludeEqual ) {
    {$PSItem.SideIndicator -eq '=>'} {
    Write-Error Message "Given preferred owner {$($PSItem.InputObject)} not found in nodes of cluster {$Cluster}. Aborting" ErrorAction Stop
    break
    }
    {$PSItem.SideIndicator -eq '=='} {
    Write-Verbose Message "Given preferred owner {$($PSItem.InputObject)} was found in nodes of cluster {$Cluster}"
    }
    }
    }

    When this is done I can loop through $VMName, read current status, set proper owners and read new status again. If -Verbose I will see what has changed from state A to state B.


    foreach ($VM in $VMName) {
    Write-Verbose Message "Processing VM {$VM}"
    $ClusterVM = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM ErrorAction SilentlyContinue | Select-Object *
    }
    if (-not $ClusterVM) {
    Write-Error Message "VM {$VM} not found on cluster {$Cluster}"
    }
    else {
    $currentPreferredOwners = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes
    }
    Write-Verbose Message "VM {$VM} current preferred owner: {$($currentPreferredOwners -join ',')}"
    Write-Verbose Message "Setting preferred owner: {$($PreferredOwner -join ',')} for VM {$VM} "
    Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Set-ClusterOwnerNode Owners $USING:PreferredOwner
    }
    $currentPreferredOwners = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes
    }
    Write-Verbose Message "VM {$VM} current preferred owner: {$($currentPreferredOwners -join ',')}"
    }
    }

    Full Script

    This is the full script


    function Set-ClusterVMPreferredOwner {
    <#
    .SYNOPSIS
    Will configure Preferred Owners for Virtual Machine running on Failover Cluster
    .DESCRIPTION
    Uses Invoke-Command to allow for PSCredential
    If Preferred Owner is provided will verify if it matches cluster Owner Nodes.
    If yes – will set Preferred Owners for given VMs.
    If no – will abort.
    If $null, or '' is provided will reset Preferred Owners to defaults for given VM
    .PARAMETER Cluster
    Cluster Name
    .PARAMETER Credential
    Optional PSCredential used to connect to cluster
    .PARAMETER VMName
    Virtual Machine Name or array of names to process
    .PARAMETER PreferredOwner
    Preferred Owner to set for given VM
    .EXAMPLE
    Set-ClusterVMPreferredOwner -Cluster 'Cluster' -VMName 'VM1' -PreferredOwner 'Node1' -Verbose
    Will set Preferred Owner on VM1 to Node1
    VERBOSE: Processing with default credentials of user {mczerniawski_admin}
    VERBOSE: Given preferred owner {Node1} was found in nodes of cluster {Cluster}
    VERBOSE: Processing VM {VM1}
    VERBOSE: VM {VM1} current preferred owner: {}
    VERBOSE: Setting preferred owner: {Node1} for VM {VM1}
    VERBOSE: VM {VM1} current preferred owner: {Node1}
    .EXAMPLE
    Set-ClusterVMPreferredOwner -Cluster 'Cluster' -VMName 'VM1','VM2' -Credential (Get-Credential) -Verbose
    Will set Preferred Owner on VM1 to Node1
    VERBOSE: Processing with provided credentials {contoso\mczerniawski_admin}
    VERBOSE: Provided no Preferred Owner. Will reset to default setting
    VERBOSE: Processing VM {VM1}
    VERBOSE: VM {VM1} current preferred owner: {Node1}
    VERBOSE: Setting preferred owner: {} for VM {VM1}
    VERBOSE: VM {VM1} current preferred owner: {}
    VERBOSE: Processing VM {VM2}
    VERBOSE: VM {VM2} current preferred owner: {}
    VERBOSE: Setting preferred owner: {} for VM {VM2}
    VERBOSE: VM {VM2} current preferred owner: {}
    #>
    [CmdletBinding()]
    param(
    [Parameter(Mandatory, HelpMessage = 'Provide Cluster Name',
    ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [ValidateNotNullOrEmpty()]
    [System.String]
    $Cluster,
    [Parameter(Mandatory = $false, HelpMessage = 'Provide Credentials for Cluster',
    ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [System.Management.Automation.PSCredential]
    $Credential,
    [Parameter(Mandatory, HelpMessage = 'Provide VMName',
    ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [ValidateNotNullOrEmpty()]
    [System.String[]]
    $VMName,
    [Parameter(Mandatory = $false, HelpMessage = 'Provide Preferred Owners Nodes to restrict VM to failover to',
    ValueFromPipeline,ValueFromPipelineByPropertyName)]
    [System.String[]]
    $PreferredOwner
    )
    begin {
    #region PSSession parameters
    $connectionParams = @{
    ComputerName = $Cluster
    }
    if ($PSBoundParameters.ContainsKey('Credential')) {
    $connectionParams.Credential = $Credential
    Write-Verbose Message "Processing with provided credentials {$($Credential.UserName)}"
    }
    else {
    Write-Verbose Message "Processing with default credentials of user {$($env:USERNAME)}"
    }
    $ClusterSession = New-PSSession @connectionParams ErrorAction Stop
    #endregion
    #region Validate Preferred Owner
    $nodesInCluster = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterNode | Select-Object ExpandProperty Name
    }
    #null or empty string – reset to defaults
    if (-not $PreferredOwner) {
    Write-Verbose Message "Provided no Preferred Owner. Will reset to default setting"
    $PreferredOwner = ''
    }
    #check if given hosts given in $PreferredOwner are members of $Cluster
    else {
    switch (Compare-Object ReferenceObject @($nodesInCluster) DifferenceObject @($PreferredOwner) IncludeEqual ) {
    {$PSItem.SideIndicator -eq '=>'} {
    Write-Error Message "Given preferred owner {$($PSItem.InputObject)} not found in nodes of cluster {$Cluster}. Aborting" ErrorAction Stop
    break
    }
    {$PSItem.SideIndicator -eq '=='} {
    Write-Verbose Message "Given preferred owner {$($PSItem.InputObject)} was found in nodes of cluster {$Cluster}"
    }
    }
    }
    #endregion
    }
    process {
    foreach ($VM in $VMName) {
    Write-Verbose Message "Processing VM {$VM}"
    $ClusterVM = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM ErrorAction SilentlyContinue | Select-Object *
    }
    if (-not $ClusterVM) {
    Write-Error Message "VM {$VM} not found on cluster {$Cluster}"
    }
    else {
    $currentPreferredOwners = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes
    }
    Write-Verbose Message "VM {$VM} current preferred owner: {$($currentPreferredOwners -join ',')}"
    Write-Verbose Message "Setting preferred owner: {$($PreferredOwner -join ',')} for VM {$VM} "
    Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Set-ClusterOwnerNode Owners $USING:PreferredOwner
    }
    $currentPreferredOwners = Invoke-Command Session $ClusterSession ScriptBlock {
    Get-ClusterGroup Name $USING:VM | Get-ClusterOwnerNode | Select-Object ExpandProperty OwnerNodes
    }
    Write-Verbose Message "VM {$VM} current preferred owner: {$($currentPreferredOwners -join ',')}"
    }
    }
    }
    end {
    $ClusterSession | Remove-PSSession ErrorAction SilentlyContinue
    }
    }

    view raw

    Preferred.ps1

    hosted with ❤ by GitHub

    And this is an example output:

  • setting preferred owner

  • Clearing to defaults

  • Same from PowerShell Core 6.12018-10-10_12h49_15
     

    Summary

This one is rather simple.

  • Create connection properties based on Credential parameter
  • Use Invoke-Command (manage anything from anywhere)
  • Validate provided nodes against cluster
  • Set proper value if no $PreferredOwner is provided
  • Verify if given VM exists and if so – Set Preferred Owner

2 thoughts on “Preferred Owner in Failover Cluster. Part 2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s