Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

xSQLServerAlwaysOnAvailabilityGroupDatabaseMembership: Make Cluster Aware #895

Merged
merged 3 commits into from
Oct 30, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
- Made the resource cluster aware. When ProcessOnlyOnActiveNode is specified,
the resource will only determine if a change is needed if the target node
is the active host of the SQL Server instance ([issue #868](https://github.com/PowerShell/xSQLServer/issues/868)).
- Changes to xSQLServerAlwaysOnAvailabilityGroupDatabaseMembership
- Made the resource cluster aware. When ProcessOnlyOnActiveNode is specified,
the resource will only determine if a change is needed if the target node
is the active host of the SQL Server instance ([issue #869](https://github.com/PowerShell/xSQLServer/issues/869)).
- Changes to xSQLServerAlwaysOnAvailabilityGroupReplica
- Made the resource cluster aware. When ProcessOnlyOnActiveNode is specified,
the resource will only determine if a change is needed if the target node is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,15 @@ function Get-TargetResource
Ensure = ''
Force = $false
MatchDatabaseOwner = $false
IsActiveNode = $false
}

# Connect to the instance
$serverObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName

# Is this node actively hosting the SQL instance?
$currentConfiguration.IsActiveNode = Test-ActiveNode -ServerObject $serverObject

# Get the Availability group object
$availabilityGroup = $serverObject.AvailabilityGroups[$AvailabilityGroupName]

Expand Down Expand Up @@ -139,6 +143,10 @@ function Get-TargetResource
If set to $false, the owner of the database will be the PSDscRunAsCredential.

The default is '$true'.

.PARAMETER ProcessOnlyOnActiveNode
Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.
Not used in Set-TargetResource.
#>
function Set-TargetResource
{
Expand Down Expand Up @@ -176,7 +184,11 @@ function Set-TargetResource

[Parameter()]
[Boolean]
$MatchDatabaseOwner
$MatchDatabaseOwner,

[Parameter()]
[Boolean]
$ProcessOnlyOnActiveNode
)

Import-SQLPSModule
Expand Down Expand Up @@ -597,6 +609,9 @@ function Set-TargetResource
If set to $false, the owner of the database will be the PSDscRunAsCredential.

The default is '$true'.

.PARAMETER ProcessOnlyOnActiveNode
Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.
#>
function Test-TargetResource
{
Expand Down Expand Up @@ -635,7 +650,11 @@ function Test-TargetResource

[Parameter()]
[Boolean]
$MatchDatabaseOwner
$MatchDatabaseOwner,

[Parameter()]
[Boolean]
$ProcessOnlyOnActiveNode
)

$configurationInDesiredState = $true
Expand All @@ -649,6 +668,16 @@ function Test-TargetResource
}
$currentConfiguration = Get-TargetResource @getTargetResourceParameters

<#
If this is supposed to process only the active node, and this is not the
active node, don't bother evaluating the test.
#>
if ( $ProcessOnlyOnActiveNode -and -not $currentConfiguration.IsActiveNode )
{
Write-Verbose -Message ( $script:localizedData.NotActiveNode -f $env:COMPUTERNAME,$SQLInstanceName )
return $configurationInDesiredState
}

# Connect to the defined instance
$serverObject = Connect-SQL -SQLServer $SQLServer -SQLInstanceName $SQLInstanceName

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ class MSFT_xSQLServerAlwaysOnAvailabilityGroupDatabaseMembership : OMI_BaseResou
[Write, Description("Specifies the membership of the database(s) in the availability group. The options are: Present: The defined database(s) are added to the availability group. All other databases that may be a member of the availability group are ignored. Absent: The defined database(s) are removed from the availability group. All other databases that may be a member of the availability group are ignored. The default is 'Present'."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure;
[Write, Description("When used with 'Ensure = 'Present'' it ensures the specified database(s) are the only databases that are a member of the specified Availability Group. This parameter is ignored when 'Ensure' is 'Absent'.")] Boolean Force;
[Write, Description("If set to $true, this ensures the database owner of the database on the primary replica is the owner of the database on all secondary replicas. This requires the database owner is available as a login on all replicas and that the PSDscRunAsAccount has impersonate permissions. If set to $false, the owner of the database will be the PSDscRunAsAccount. The default is '$true'")] Boolean MatchDatabaseOwner;
[Write, Description("Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.")] Boolean ProcessOnlyOnActiveNode;
[Read, Description("Determines if the current node is actively hosting the SQL Server instance.")] Boolean IsActiveNode;
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ConvertFrom-StringData @'
DatabaseShouldNotBeMember = The following databases should not be a member of the availability group '{0}': {1}.
DatabasesNotFound = The following databases were not found in the instance: {0}.
ImpersonatePermissionsMissing = The login '{0}' is missing impersonate permissions in the instances '{1}'.
NotActiveNode = The node '{0}' is not actively hosting the instance '{1}'. Exiting the test.
ParameterNotOfType = The parameter '{0}' is not of the type '{1}'.
ParameterNullOrEmpty = The parameter '{0}' is NULL or empty.
RemovingDatabasesToAvailabilityGroup = Removing the following databases from the '{0}' availability group: {1}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,6 @@ PARAMETER MatchDatabaseOwner
If set to $false, the owner of the database will be the PSDscRunAsCredential.

The default is '$true'.

.PARAMETER ProcessOnlyOnActiveNode
Specifies that the resource will only determine if a change is needed if the target node is the active host of the SQL Server Instance.
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<#
.EXAMPLE
This example shows how to ensure that the databases 'DB*' and 'AdventureWorks' are members in the Availability Group 'TestAG'.

In the event this is applied to a Failover Cluster Instance (FCI), the
ProcessOnlyOnActiveNode property will tell the Test-TargetResource function
to evaluate if any changes are needed if the node is actively hosting the
SQL Server Instance.
#>

$ConfigurationData = @{
Expand Down Expand Up @@ -101,13 +106,14 @@ Configuration Example
{
xSQLServerAlwaysOnAvailabilityGroupDatabaseMembership 'TestAGDatabaseMemberships'
{
AvailabilityGroupName = $Node.AvailabilityGroupName
BackupPath = '\\SQL1\AgInitialize'
DatabaseName = 'DB*', 'AdventureWorks'
SQLInstanceName = $Node.SQLInstanceName
SQLServer = $Node.NodeName
Ensure = 'Present'
PsDscRunAsCredential = $SysAdminAccount
AvailabilityGroupName = $Node.AvailabilityGroupName
BackupPath = '\\SQL1\AgInitialize'
DatabaseName = 'DB*', 'AdventureWorks'
SQLInstanceName = $Node.SQLInstanceName
SQLServer = $Node.NodeName
Ensure = 'Present'
ProcessOnlyOnActiveNode = $true
PsDscRunAsCredential = $SysAdminAccount
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,14 @@ group.
login on all replicas and that the PSDscRunAsAccount has impersonate permissions.
If set to $false, the owner of the database will be the PSDscRunAsAccount.
The default is '$true'.
* **`[Boolean]` ProcessOnlyOnActiveNode** _(Write)_: Specifies that the resource
will only determine if a change is needed if the target node is the active
host of the SQL Server Instance.

#### Read-Only Properties from Get-TargetResource

* **`[Boolean]` IsActiveNode** _(Read)_: Determines if the current node is
actively hosting the SQL Server instance.

### xSQLServerAlwaysOnAvailabilityGroupReplica

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ try

$mockBackupPath = 'X:\Backup'

$mockProcessOnlyOnActiveNode = $false

#endregion Parameter Mocks

#region mock names
Expand Down Expand Up @@ -1140,6 +1142,9 @@ WITH NORECOVERY'
Mock -CommandName Connect-SQL -MockWith { return $mockServerObject } -Verifiable
Mock -CommandName Get-PrimaryReplicaServerObject -MockWith { return $mockServerObject } -Verifiable -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Mock -CommandName Get-PrimaryReplicaServerObject -MockWith { return $mockServer2Object } -Verifiable -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Mock -CommandName Test-ActiveNode -MockWith {
return -not $mockProcessOnlyOnActiveNode
} -Verifiable
}

BeforeEach {
Expand All @@ -1152,6 +1157,7 @@ WITH NORECOVERY'
Ensure = 'Present'
Force = $false
MatchDatabaseOwner = $false
ProcessOnlyOnActiveNode = $false
}
}

Expand All @@ -1164,6 +1170,7 @@ WITH NORECOVERY'
Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 2 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}

It 'Should return $false when the specified availability group is not found' {
Expand All @@ -1174,6 +1181,7 @@ WITH NORECOVERY'
Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 2 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}

It 'Should return $false when no matching databases are found' {
Expand All @@ -1184,6 +1192,7 @@ WITH NORECOVERY'
Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 2 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}

It 'Should return $false when databases are found to add to the availability group' {
Expand All @@ -1192,6 +1201,7 @@ WITH NORECOVERY'
Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 2 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}

It 'Should return $true when the configuration is in the desired state and the primary replica is on another server' {
Expand All @@ -1203,6 +1213,21 @@ WITH NORECOVERY'
Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 2 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 1 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}

It 'Should return $true when ProcessOnlyOnActiveNode is "$true" and the current node is not actively hosting the instance' {
$mockProcessOnlyOnActiveNode = $true

$mockTestTargetResourceParameters.DatabaseName = $mockAvailabilityDatabaseNames.Clone()
$mockTestTargetResourceParameters.ProcessOnlyOnActiveNode = $mockProcessOnlyOnActiveNode

Test-TargetResource @mockTestTargetResourceParameters | Should -Be $true

Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server1' }
Assert-MockCalled -CommandName Get-PrimaryReplicaServerObject -Scope It -Times 0 -Exactly -ParameterFilter { $AvailabilityGroup.PrimaryReplicaServerName -eq 'Server2' }
Assert-MockCalled -CommandName Test-ActiveNode -Scope It -Times 1 -Exactly
}
}

Expand Down