DSCResources/MSFT_xClusterPreferredOwner/MSFT_xClusterPreferredOwner.psm1

<#
    .SYNOPSIS
        Returns the current state of the failover cluster group and cluster
        resource preferred owners.
 
    .PARAMETER ClusterGroup
        Name of the cluster group.
 
    .PARAMETER ClusterName
        Name of the cluster.
 
    .PARAMETER Nodes
        The nodes to set as owners.
 
    .PARAMETER ClusterResources
        The resources to set preferred owners on.
 
    .PARAMETER Ensure
        If the preferred owners should be present or absent. Default value is
        'Present'.
#>

function Get-TargetResource
{
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterGroup,

        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterName,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $Nodes,

        [Parameter()]
        [System.String[]]
        $ClusterResources,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present'
    )

    Write-Verbose -Message "Retrieving Owner information for cluster $ClusterName..."

    $ownerNodes = @(

        Write-Verbose -Message "Retrieving Owner information for Cluster Group $ClusterGroup"
        (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.Name -like $ClusterGroup
        } | Get-ClusterOwnerNode).OwnerNodes).Name

        if ($ClusterResources)
        {
            foreach ($resource in $ClusterResources)
            {
                Write-Verbose -Message "Retrieving Owner information for Cluster Resource $resource"
                (((Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript {
                    $_.Name -like $resource
                } | Get-ClusterOwnerNode).OwnerNodes).Name
            }
        }
    )

    $ownerNodes = $ownerNodes | Select-Object -Unique

    @{
        ClusterGroup = $ClusterGroup
        Clustername = $ClusterName
        Nodes = $ownerNodes
        ClusterResources = $ClusterResources
        Ensure = $Ensure
    }
}

<#
    .SYNOPSIS
        Configures the desired preferred owners on the failover cluster group and cluster
        resource.
 
    .PARAMETER ClusterGroup
        Name of the cluster group.
 
    .PARAMETER ClusterName
        Name of the cluster.
 
    .PARAMETER Nodes
        The nodes to set as owners.
 
    .PARAMETER ClusterResources
        The resources to set preferred owners on.
 
    .PARAMETER Ensure
        If the preferred owners should be present or absent. Default value is
        'Present'.
#>

function Set-TargetResource
{
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterGroup,

        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterName,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $Nodes,

        [Parameter()]
        [System.String[]]
        $ClusterResources,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present'
    )

    Write-Verbose -Message "Retrieving all owners from cluster $ClusterName"
    $allNodes = (Get-ClusterNode -Cluster $ClusterName).Name

    if ($Ensure -eq 'Present')
    {
        Write-Verbose -Message "Setting Cluster owners for Group $ClusterGroup to $Nodes"
        $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.Name -like $ClusterGroup
        } | Set-ClusterOwnerNode -Owners $Nodes

        $null = (Get-ClusterResource) | Where-Object {
            $_.OwnerGroup -like $ClusterGroup
        } | Set-ClusterOwnerNode -Owners $allNodes

        Write-Verbose -Message "Moving Cluster Group $ClusterGroup to node $($Nodes[0])"
        $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.name -like $ClusterGroup
        } | Move-ClusterGroup -Node $Nodes[0]

        foreach ($resource in $ClusterResources)
        {
            Write-Verbose -Message "Setting Cluster owners for Resource $resource to $Nodes"
            $null = (Get-ClusterResource -Cluster $ClusterName) | Where-Object -FilterScript {
                $_.Name -like $resource
            } | Set-ClusterOwnerNode -Owners $Nodes
        }
    }

    if ($Ensure -eq 'Absent')
    {
        Write-Verbose -Message "Retrieving current cluster owners for group $ClusterGroup"
        $currentOwners = (((Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.Name -like $ClusterGroup
        } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique

        $newOwners = @(
            foreach ($currentOwner in $currentOwners)
            {
                if ($Nodes -notcontains $currentOwner)
                {
                    $currentOwner
                }
            }
        )

        Write-Verbose -Message "Removing owners from group $($ClusterGroup): $Nodes"
        $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.Name -like $ClusterGroup
        } | Set-ClusterOwnerNode $newOwners

        Write-Verbose -Message "Setting Cluster owners for Group $ClusterGroup to $newOwners"
        $null = (Get-ClusterResource) | Where-Object -FilterScript {
            $_.OwnerGroup -like $ClusterGroup
        } | Set-ClusterOwnerNode $allNodes

        Write-Verbose -Message "Moving Cluster Group $ClusterGroup to node $($newOwners[0])"
        $null = (Get-ClusterGroup -Cluster $ClusterName) | Where-Object -FilterScript {
            $_.Name -like $ClusterGroup
        } | Move-ClusterGroup -Node $newOwners[0]

        foreach ($resource in $ClusterResources)
        {
            Write-Verbose -Message "Retrieving current cluster owners for resource $resource"
            $currentOwners = ((Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript {
                $_.Name -like $resource
            } | Get-ClusterOwnerNode).OwnerNodes).Name | Sort-Object -Unique

            $newOwners = @(
                foreach ($currentOwner in $currentOwners)
                {
                    if ($Nodes -notcontains $currentOwner)
                    {
                        $currentOwner
                    }
                }
            )

            Write-Verbose -Message "Setting Cluster owners for Resource $resource to $newOwners"
            $null = Get-ClusterResource -Cluster $ClusterName | Where-Object -FilterScript {
                $_.Name -like $resource
            } | Set-ClusterOwnerNode -Owners $newOwners
        }
    }
}

<#
    .SYNOPSIS
        Tests so that the desired preferred owners on the failover cluster group
        and cluster resource are in desired state.
 
    .PARAMETER ClusterGroup
        Name of the cluster group.
 
    .PARAMETER ClusterName
        Name of the cluster.
 
    .PARAMETER Nodes
        The nodes to set as owners.
 
    .PARAMETER ClusterResources
        The resources to set preferred owners on.
 
    .PARAMETER Ensure
        If the preferred owners should be present or absent. Default value is
        'Present'.
#>


function Test-TargetResource
{
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterGroup,

        [Parameter(Mandatory = $true)]
        [System.String]
        $ClusterName,

        [Parameter(Mandatory = $true)]
        [System.String[]]
        $Nodes,

        [Parameter()]
        [System.String[]]
        $ClusterResources,

        [Parameter()]
        [ValidateSet('Present', 'Absent')]
        [System.String]
        $Ensure = 'Present'
    )

    Write-Verbose -Message "Testing Owner information for cluster $ClusterName..."

    $getTargetResourceResult = (Get-TargetResource @PSBoundParameters).Nodes
    $result = $true

    if ($Ensure -eq 'Present')
    {
        foreach ($object in $getTargetResourceResult)
        {
            if ($Nodes -notcontains $object)
            {
                Write-Verbose -Message "$object was NOT found as possible owner"
                $result = $false
            }
        }

        foreach ($object in $Nodes)
        {
            if ($getTargetResourceResult -notcontains $object)
            {
                Write-Verbose -Message "$object was NOT found as possible owner"
                $result = $false
            }
        }
    }

    if ($Ensure -eq 'Absent')
    {
        foreach ($object in $getTargetResourceResult)
        {
            if ($Nodes -contains $object)
            {
                Write-Verbose -Message "$object WAS found as possible owner"
                $result = $false
            }
        }

        foreach ($object in $Nodes)
        {
            if ($getTargetResourceResult -contains $object)
            {
                Write-Verbose -Message "$object WAS found as possible owner"
                $result = $false
            }
        }
    }

    $result
}