diff --git a/CHANGELOG.md b/CHANGELOG.md index 64844af0e..b860c97aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,11 @@ - Updated the integration test to stop the named instance while installing the other instances to mitigate [issue #1260](https://github.com/PowerShell/SqlServerDsc/issues/1260). +- Changes to SqlServerEndpoint + - Add the optional parameter Owner. The default owner remains the login used + for the creation of the endpoint + ([issue #1251](https://github.com/PowerShell/SqlServerDsc/issues/1251). + [Maxime Daniou (@mdaniou)](https://github.com/mdaniou) ## 12.2.0.0 diff --git a/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.psm1 b/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.psm1 index 1bc760540..c55f2d9f3 100644 --- a/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.psm1 +++ b/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.psm1 @@ -40,6 +40,7 @@ function Get-TargetResource EndpointName = '' Port = '' IpAddress = '' + Owner = '' } $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName @@ -61,6 +62,7 @@ function Get-TargetResource $getTargetResourceReturnValues.EndpointName = $endpointObject.Name $getTargetResourceReturnValues.Port = $endpointObject.Protocol.Tcp.ListenerPort $getTargetResourceReturnValues.IpAddress = $endpointObject.Protocol.Tcp.ListenerIPAddress + $getTargetResourceReturnValues.Owner = $endpointObject.Owner } else { @@ -68,6 +70,7 @@ function Get-TargetResource $getTargetResourceReturnValues.EndpointName = '' $getTargetResourceReturnValues.Port = '' $getTargetResourceReturnValues.IpAddress = '' + $getTargetResourceReturnValues.Owner = '' } } else @@ -101,6 +104,9 @@ function Get-TargetResource .PARAMETER IpAddress The network IP address the endpoint is listening on. Defaults to '0.0.0.0' which means listen on any valid IP address. + + .PARAMETER Owner + The owner of the endpoint. Default is the login used for the creation. #> function Set-TargetResource { @@ -130,7 +136,11 @@ function Set-TargetResource [Parameter()] [System.String] - $IpAddress = '0.0.0.0' + $IpAddress = '0.0.0.0', + + [Parameter()] + [System.String] + $Owner ) $getTargetResourceResult = Get-TargetResource -EndpointName $EndpointName -ServerName $ServerName -InstanceName $InstanceName @@ -147,6 +157,12 @@ function Set-TargetResource $endpointObject.ProtocolType = [Microsoft.SqlServer.Management.Smo.ProtocolType]::Tcp $endpointObject.Protocol.Tcp.ListenerPort = $Port $endpointObject.Protocol.Tcp.ListenerIPAddress = $IpAddress + + if ($PSBoundParameters.ContainsKey('Owner')) + { + $endpointObject.Owner = $Owner + } + $endpointObject.Payload.DatabaseMirroring.ServerMirroringRole = [Microsoft.SqlServer.Management.Smo.ServerMirroringRole]::All $endpointObject.Payload.DatabaseMirroring.EndpointEncryption = [Microsoft.SqlServer.Management.Smo.EndpointEncryption]::Required $endpointObject.Payload.DatabaseMirroring.EndpointEncryptionAlgorithm = [Microsoft.SqlServer.Management.Smo.EndpointEncryptionAlgorithm]::Aes @@ -172,6 +188,13 @@ function Set-TargetResource $endpointObject.Protocol.Tcp.ListenerPort = $Port $endpointObject.Alter() } + + if ($endpointObject.Owner -ne $Owner) + { + Write-Verbose -Message ('Updating endpoint {0} Owner to {1}.' -f $EndpointName, $Owner) + $endpointObject.Owner = $Owner + $endpointObject.Alter() + } } else { @@ -222,6 +245,9 @@ function Set-TargetResource .PARAMETER IpAddress The network IP address the endpoint is listening on. Defaults to '0.0.0.0' which means listen on any valid IP address. + + .PARAMETER Owner + The owner of the endpoint. Default is the login used for the creation. #> function Test-TargetResource { @@ -252,7 +278,11 @@ function Test-TargetResource [Parameter()] [System.String] - $IpAddress = '0.0.0.0' + $IpAddress = '0.0.0.0', + + [Parameter()] + [System.String] + $Owner ) $getTargetResourceResult = Get-TargetResource -EndpointName $EndpointName -ServerName $ServerName -InstanceName $InstanceName @@ -262,9 +292,15 @@ function Test-TargetResource if ($getTargetResourceResult.Ensure -eq 'Present' ` -and ( - $getTargetResourceResult.Port -ne $Port ` + $getTargetResourceResult.Port -ne $Port ` -or $getTargetResourceResult.IpAddress -ne $IpAddress - ) + ) + ) + { + $result = $false + } + elseif ($getTargetResourceResult.Ensure -eq 'Present' -and $Owner ` + -and $getTargetResourceResult.Owner -ne $Owner ) { $result = $false diff --git a/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.schema.mof b/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.schema.mof index 05c85cfab..9b421ae63 100644 --- a/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.schema.mof +++ b/DSCResources/MSFT_SqlServerEndpoint/MSFT_SqlServerEndpoint.schema.mof @@ -7,4 +7,5 @@ class MSFT_SqlServerEndpoint : OMI_BaseResource [Write, Description("The host name of the SQL Server to be configured. Default value is $env:COMPUTERNAME.")] String ServerName; [Key, Description("The name of the SQL instance to be configured.")] String InstanceName; [Write, Description("The network IP address the endpoint is listening on. Default the endpoint will listen on any valid IP address.")] String IpAddress; + [Write, Description("The owner of the endpoint. Default is the login used for the creation.")] String Owner; }; diff --git a/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortAndIPAddress.ps1 b/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortIPAddressOwner.ps1 similarity index 81% rename from Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortAndIPAddress.ps1 rename to Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortIPAddressOwner.ps1 index 973332ab9..e3e4c4d8c 100644 --- a/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortAndIPAddress.ps1 +++ b/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortIPAddressOwner.ps1 @@ -1,6 +1,6 @@ <# .EXAMPLE - This example will add a Database Mirror endpoint with a specific listener port and a specific listener IP address. + This example will add a Database Mirror endpoint with specific listener port, listener IP address and owner. #> Configuration Example { @@ -22,6 +22,7 @@ Configuration Example EndpointName = 'HADR' Port = 9001 IpAddress = '192.168.0.20' + Owner = 'sa' ServerName = 'server1.company.local' InstanceName = 'INST1' diff --git a/README.md b/README.md index 4c417e0ff..9501f2fb2 100644 --- a/README.md +++ b/README.md @@ -1041,11 +1041,13 @@ the resource [**SqlServerEndpointPermission**](#sqlserverendpointpermission). * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. * **`[String]` IpAddress** _(Write)_: The network IP address the endpoint is listening on. Defaults to '0.0.0.0' which means listen on any valid IP address. +* **`[String]` Owner** _(Write)_: The owner of the endpoint. Default is the + login used for the creation. #### Examples * [Create an endpoint with default values](/Examples/Resources/SqlServerEndpoint/1-CreateEndpointWithDefaultValues.ps1) -* [Create an endpoint with specific port and IP address](/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortAndIPAddress.ps1) +* [Create an endpoint with specific port and IP address](/Examples/Resources/SqlServerEndpoint/2-CreateEndpointWithSpecificPortIPAddressOwner.ps1) * [Remove an endpoint](/Examples/Resources/SqlServerEndpoint/3-RemoveEndpoint.ps1) #### Known issues diff --git a/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 b/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 index 1800e7be8..eabf97fa0 100644 --- a/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 @@ -59,11 +59,13 @@ try $mockEndpointType = 'DatabaseMirroring' $mockEndpointListenerPort = 5022 $mockEndpointListenerIpAddress = '0.0.0.0' # 0.0.0.0 means listen on all IP addresses. + $mockEndpointOwner = 'sa' $mockOtherEndpointName = 'UnknownEndpoint' $mockOtherEndpointType = 'UnknownType' $mockOtherEndpointListenerPort = 9001 $mockOtherEndpointListenerIpAddress = '192.168.0.20' + $mockOtherEndpointOwner = 'COMPANY\OtherAcct' $script:mockMethodAlterRan = $false $script:mockMethodCreateRan = $false @@ -74,6 +76,7 @@ try $mockDynamicEndpointType = $mockEndpointType $mockDynamicEndpointListenerPort = $mockEndpointListenerPort $mockDynamicEndpointListenerIpAddress = $mockEndpointListenerIpAddress + $mockDynamicEndpointOwner = $mockEndpointOwner $mockEndpointObject = { # TypeName: Microsoft.SqlServer.Management.Smo.Endpoint @@ -81,6 +84,7 @@ try Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockDynamicEndpointName -PassThru | Add-Member -MemberType NoteProperty -Name 'EndpointType' -Value $mockDynamicEndpointType -PassThru | Add-Member -MemberType NoteProperty -Name 'ProtocolType' -Value $null -PassThru | + Add-Member -MemberType NoteProperty -Name 'Owner' -Value $mockDynamicEndpointOwner -PassThru | Add-Member -MemberType ScriptProperty -Name 'Protocol' -Value { return New-Object -TypeName Object | Add-Member -MemberType ScriptProperty -Name 'Tcp' -Value { @@ -184,6 +188,7 @@ try $result.EndpointName | Should -Be '' $result.Port | Should -Be '' $result.IpAddress | Should -Be '' + $result.Owner | Should -Be '' } It 'Should call the mock function Connect-SQL' { @@ -208,6 +213,7 @@ try $result.EndpointName | Should -Be $testParameters.EndpointName $result.Port | Should -Be $mockEndpointListenerPort $result.IpAddress | Should -Be $mockEndpointListenerIpAddress + $result.Owner | Should -Be $mockEndpointOwner } It 'Should call the mock function Connect-SQL' { @@ -265,6 +271,7 @@ try $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockEndpointListenerPort) $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) $result = Test-TargetResource @testParameters $result | Should -Be $false @@ -322,6 +329,26 @@ try # Make sure the mock do return the correct endpoint listener IP address $mockDynamicEndpointListenerIpAddress = $mockEndpointListenerIpAddress + + # Make sure the mock do return the correct endpoint, but does not return the correct endpoint owner + $mockDynamicEndpointName = $mockEndpointName + $mockDynamicEndpointOwner = $mockOtherEndpointOwner + + Context 'When listener Owner is not in desired state' { + It 'Should return that desired state is absent' { + $testParameters.Add('Ensure', 'Present') + $testParameters.Add('Owner', $mockEndpointOwner) + + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + } + + # Make sure the mock do return the correct endpoint owner + $mockDynamicEndpointOwner = $mockEndpointOwner } Context 'When the system is in the desired state' { @@ -372,7 +399,7 @@ try # Set what the expected endpoint name should be when Create() method is called. $mockExpectedNameWhenCallingMethod = $mockEndpointName - It 'Should call the the method Create when desired state is to be Present (using default values)' { + It 'Should call the method Create when desired state is to be Present (using default values)' { Mock -CommandName Get-TargetResource -MockWith { return @{ Ensure = 'Absent' @@ -397,7 +424,7 @@ try # Set what the expected endpoint name should be when Create() method is called. $mockExpectedNameWhenCallingMethod = $mockEndpointName - It 'Should call the the method Create when desired state is to be Present (setting all parameters)' { + It 'Should call the method Create when desired state is to be Present (setting all parameters)' { Mock -CommandName Get-TargetResource -MockWith { return @{ Ensure = 'Absent' @@ -407,6 +434,7 @@ try $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockEndpointListenerPort) $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodCreateRan | Should -Be $true @@ -426,7 +454,7 @@ try # Set what the expected endpoint name should be when Drop() method is called. $mockExpectedNameWhenCallingMethod = $mockEndpointName - It 'Should call the the method Drop when desired state is to be Absent' { + It 'Should call the method Drop when desired state is to be Absent' { Mock -CommandName Get-TargetResource -MockWith { return @{ Ensure = 'Present' @@ -453,7 +481,7 @@ try # Set what the expected endpoint name should be when Alter() method is called. $mockExpectedNameWhenCallingMethod = $mockEndpointName - It 'Should not call Alter method when listener port is not in desired state' { + It 'Should call Alter method when listener port is not in desired state' { Mock -CommandName Get-TargetResource -MockWith { return @{ Ensure = 'Present' @@ -465,6 +493,7 @@ try $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockOtherEndpointListenerPort) $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodCreateRan | Should -Be $false @@ -484,7 +513,7 @@ try # Set what the expected endpoint name should be when Alter() method is called. $mockExpectedNameWhenCallingMethod = $mockEndpointName - It 'Should not call Alter method when listener IP address is not in desired state' { + It 'Should call Alter method when listener IP address is not in desired state' { Mock -CommandName Get-TargetResource -MockWith { return @{ Ensure = 'Present' @@ -496,6 +525,39 @@ try $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockEndpointListenerPort) $testParameters.Add('IpAddress', $mockOtherEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) + + { Set-TargetResource @testParameters } | Should -Not -Throw + $script:mockMethodCreateRan | Should -Be $false + $script:mockMethodStartRan | Should -Be $false + $script:mockMethodAlterRan | Should -Be $true + $script:mockMethodDropRan | Should -Be $false + + Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + } + + # Set all method call tests variables to $false + $script:mockMethodCreateRan = $false + $script:mockMethodStartRan = $false + $script:mockMethodAlterRan = $false + $script:mockMethodDropRan = $false + + # Set what the expected endpoint name should be when Alter() method is called. + $mockExpectedNameWhenCallingMethod = $mockEndpointName + + It 'Should call Alter method when Owner is not in desired state' { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Port = $mockEndpointListenerPort + IpAddress = $mockEndpointListenerIpAddress + } + } -Verifiable + + $testParameters.Add('Ensure', 'Present') + $testParameters.Add('Port', $mockEndpointListenerPort) + $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockOtherEndpointOwner) { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodCreateRan | Should -Be $false @@ -516,6 +578,7 @@ try Ensure = 'Present' Port = $mockEndpointListenerPort IpAddress = $mockEndpointListenerIpAddress + Owner = $mockEndpointOwner } } -Verifiable @@ -530,6 +593,7 @@ try Ensure = 'Present' Port = $mockEndpointListenerPort IpAddress = $mockEndpointListenerIpAddress + Owner = $mockEndpointOwner } } -Verifiable @@ -551,11 +615,12 @@ try } } - Context 'When the system is in the desired state' { + Context ' ' { # Make sure the mock do return the correct endpoint $mockDynamicEndpointName = $mockEndpointName $mockDynamicEndpointListenerPort = $mockEndpointListenerPort $mockDynamicEndpointListenerIpAddress = $mockEndpointListenerIpAddress + $mockDynamicEndpointOwner = $mockEndpointOwner # Set all method call tests variables to $false $script:mockMethodCreateRan = $false @@ -569,12 +634,14 @@ try Ensure = 'Present' Port = $mockEndpointListenerPort IpAddress = $mockEndpointListenerIpAddress + Owner = $mockEndpointOwner } } -Verifiable $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockEndpointListenerPort) $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodCreateRan | Should -Be $false @@ -657,6 +724,7 @@ try $testParameters.Add('Ensure', 'Present') $testParameters.Add('Port', $mockOtherEndpointListenerPort) $testParameters.Add('IpAddress', $mockEndpointListenerIpAddress) + $testParameters.Add('Owner', $mockEndpointOwner) { Set-TargetResource @testParameters } | Should -Throw 'Exception calling "Alter" with "0" argument(s): "Called mocked Alter() method on and endpoint with wrong name. Expected ''UnknownEndpoint''. But was ''DefaultEndpointMirror''."' }