diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f2dc98b0..aa20d8f09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - No longer throws an exception when parameter `AgtSvcAccount` is not specified. - SqlAgReplica - Converted unit test to Pester 5. + - `Update-AvailabilityGroupReplica` to trigger once within `Set-TargetResource` + for all AvailabilityReplica changes. - Private function `Invoke-SetupAction` ([issue #1798](https://github.com/dsccommunity/SqlServerDsc/issues/1798)). - Was changed to support the SQL Server 2022 GA feature `AzureExtension` (that replaced the feature name `ARC`). @@ -2409,7 +2411,7 @@ in a future release. MSFT\_xSQLServerEndpointPermission.psm1, MSFT\_xSQLServerEndpointState.psm1, MSFT\_xSQLServerNetwork.psm1, MSFT\_xSQLServerPermission.psm1, MSFT\_xSQLServerReplication.psm1, MSFT\_xSQLServerScript.psm1, - SQLPSStub.psm1, SQLServerStub.psm1. + SQLPSStub.psm1, SqlServerStub.psm1. - Opt-in for script files common tests ([issue #707](https://github.com/dsccommunity/SqlServerDsc/issues/707)). - Removed Byte Order Mark (BOM) from the files; DSCClusterSqlBuild.ps1, DSCFCISqlBuild.ps1, DSCSqlBuild.ps1, DSCSQLBuildEncrypted.ps1, diff --git a/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 b/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 index 17f3bd7a6..d3573f5cd 100644 --- a/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 +++ b/source/DSCResources/DSC_SqlAGReplica/DSC_SqlAGReplica.psm1 @@ -319,33 +319,41 @@ function Set-TargetResource $availabilityGroupReplica = $availabilityGroup.AvailabilityReplicas[$Name] if ( $availabilityGroupReplica ) { + $availabilityGroupReplicaUpdatesRequired = $false + # Get the parameters that were submitted to the function [System.Array] $submittedParameters = $PSBoundParameters.Keys + if ( ( $submittedParameters -contains 'FailoverMode' ) -and ( $FailoverMode -ne $availabilityGroupReplica.FailoverMode ) ) + { + $availabilityGroupReplica.FailoverMode = $FailoverMode + $availabilityGroupReplicaUpdatesRequired = $true + } + if ( ( $submittedParameters -contains 'AvailabilityMode' ) -and ( $AvailabilityMode -ne $availabilityGroupReplica.AvailabilityMode ) ) { $availabilityGroupReplica.AvailabilityMode = $AvailabilityMode - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } if ( ( $submittedParameters -contains 'BackupPriority' ) -and ( $BackupPriority -ne $availabilityGroupReplica.BackupPriority ) ) { $availabilityGroupReplica.BackupPriority = $BackupPriority - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } # Make sure ConnectionModeInPrimaryRole has a value in order to avoid false positive matches when the parameter is not defined if ( ( -not [System.String]::IsNullOrEmpty($ConnectionModeInPrimaryRole) ) -and ( $ConnectionModeInPrimaryRole -ne $availabilityGroupReplica.ConnectionModeInPrimaryRole ) ) { $availabilityGroupReplica.ConnectionModeInPrimaryRole = $ConnectionModeInPrimaryRole - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } # Make sure ConnectionModeInSecondaryRole has a value in order to avoid false positive matches when the parameter is not defined if ( ( -not [System.String]::IsNullOrEmpty($ConnectionModeInSecondaryRole) ) -and ( $ConnectionModeInSecondaryRole -ne $availabilityGroupReplica.ConnectionModeInSecondaryRole ) ) { $availabilityGroupReplica.ConnectionModeInSecondaryRole = $ConnectionModeInSecondaryRole - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } # Break out the EndpointUrl properties @@ -355,33 +363,27 @@ function Set-TargetResource { $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointPort, $endpoint.Protocol.Tcp.ListenerPort) $availabilityGroupReplica.EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } if ( ( $submittedParameters -contains 'EndpointHostName' ) -and ( $EndpointHostName -ne $currentEndpointHostName ) ) { $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointHostName, $EndpointHostName) $availabilityGroupReplica.EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } if ( $currentEndpointProtocol -ne 'TCP' ) { $newEndpointUrl = $availabilityGroupReplica.EndpointUrl.Replace($currentEndpointProtocol, 'TCP') $availabilityGroupReplica.EndpointUrl = $newEndpointUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica - } - - if ( ( $submittedParameters -contains 'FailoverMode' ) -and ( $FailoverMode -ne $availabilityGroupReplica.FailoverMode ) ) - { - $availabilityGroupReplica.FailoverMode = $FailoverMode - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } if ( ( $submittedParameters -contains 'ReadOnlyRoutingConnectionUrl' ) -and ( $ReadOnlyRoutingConnectionUrl -ne $availabilityGroupReplica.ReadOnlyRoutingConnectionUrl ) ) { $availabilityGroupReplica.ReadOnlyRoutingConnectionUrl = $ReadOnlyRoutingConnectionUrl - Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica + $availabilityGroupReplicaUpdatesRequired = $true } if ( ( $submittedParameters -contains 'ReadOnlyRoutingList' ) -and ( ( $ReadOnlyRoutingList -join ',' ) -ne ( $availabilityGroupReplica.ReadOnlyRoutingList -join ',' ) ) ) @@ -391,6 +393,11 @@ function Set-TargetResource { $availabilityGroupReplica.ReadOnlyRoutingList.Add($readOnlyRoutingListEntry) | Out-Null } + $availabilityGroupReplicaUpdatesRequired = $true + } + + if ( $availabilityGroupReplicaUpdatesRequired ) + { Update-AvailabilityGroupReplica -AvailabilityGroupReplica $availabilityGroupReplica } } diff --git a/tests/TestHelpers/CommonTestHelper.psm1 b/tests/TestHelpers/CommonTestHelper.psm1 index 9b2e6afb8..e1aae11e7 100644 --- a/tests/TestHelpers/CommonTestHelper.psm1 +++ b/tests/TestHelpers/CommonTestHelper.psm1 @@ -39,7 +39,7 @@ function Import-SqlModuleStub #> $modulesAndStubs = @{ SQLPS = 'SQLPSStub' - SqlServer = 'SQLServerStub' + SqlServer = 'SqlServerStub' } # Determine which module to ensure is loaded based on the parameters passed diff --git a/tests/Unit/DSC_SqlAGReplica.Tests.ps1 b/tests/Unit/DSC_SqlAGReplica.Tests.ps1 index 53cd1ea98..e0f3a397e 100644 --- a/tests/Unit/DSC_SqlAGReplica.Tests.ps1 +++ b/tests/Unit/DSC_SqlAGReplica.Tests.ps1 @@ -1220,6 +1220,70 @@ Describe 'SqlAGReplica\Set-TargetResource' { } } + Context 'When multiple properties are not in desired state' { + BeforeAll { + Mock -CommandName Update-AvailabilityGroupReplica + } + + It 'Should set all properties with one update' { + InModuleScope -Parameters $_ -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'SynchronousCommit' + BackupPriority = 60 + ConnectionModeInPrimaryRole = 'AllowReadWriteConnections' + ConnectionModeInSecondaryRole = 'AllowReadIntentConnectionsOnly' + EndpointHostName = 'AnotherEndpointHostName' + FailoverMode = 'Automatic' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server2', 'Server1') + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + } + + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Exactly -Times 1 -Scope It + } + } + + Context 'When AvailabilityMode and FailoverMode properties are not in desired state' { + BeforeAll { + Mock -CommandName Update-AvailabilityGroupReplica + } + + It 'Should set both properties with one update' { + InModuleScope -Parameters $_ -ScriptBlock { + $setTargetResourceParameters = @{ + Name = 'Server1' + AvailabilityGroupName = 'AG_AllServers' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + PrimaryReplicaServerName = 'Server2' + PrimaryReplicaInstanceName = 'MSSQLSERVER' + Ensure = 'Present' + AvailabilityMode = 'SynchronousCommit' + BackupPriority = 50 + ConnectionModeInPrimaryRole = 'AllowAllConnections' + ConnectionModeInSecondaryRole = 'AllowNoConnections' + EndpointHostName = 'Server1' + FailoverMode = 'Automatic' + ReadOnlyRoutingConnectionUrl = 'TCP://Server1.domain.com:1433' + ReadOnlyRoutingList = @('Server1', 'Server2') + } + + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + } + + Should -Invoke -CommandName Update-AvailabilityGroupReplica -Exactly -Times 1 -Scope It + } + } + Context 'When the endpoint port differ from the port in the replica''s endpoint URL' { BeforeAll { Mock -CommandName Update-AvailabilityGroupReplica