From 943c87801e68780a5981054a267c622c48b19bcd Mon Sep 17 00:00:00 2001 From: kungfu71186 Date: Sun, 30 Jun 2019 15:34:05 +0300 Subject: [PATCH 01/13] Invoke-query updated to allow smo object (#1380) - Changes to common module: Invoke-Query - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355) - Works together with Connect-SQL now - Parameters and Aliases now match that of Connect-SQL - Can now pass in credentials - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object - Can pipe Connect-SQL | Invoke-Query - Added default vaules to Invoke-Query --- CHANGELOG.md | 10 + .../SqlServerDsc.Common.psm1 | 89 +++++++-- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 177 +++++++++++++----- 3 files changed, 214 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b7d601fc..446e47e43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## Unreleased +- Changes to common module: Invoke-Query + - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355) + - Works together with Connect-SQL now + - Parameters and Aliases now match that of Connect-SQL + - Can now pass in credentials + - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object + - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object + - Can pipe Connect-SQL | Invoke-Query + - Added default vaules to Invoke-Query + ## 13.0.0.0 - Changes to SqlServerDsc diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index f3e8b5001..f780af7b6 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1569,47 +1569,69 @@ function Restart-ReportingServicesService } } + <# .SYNOPSIS - Executes a query on the specified database. + Executes a query on the specified database. .PARAMETER SQLServer - The hostname of the server that hosts the SQL instance. + The hostname of the server that hosts the SQL instance. .PARAMETER SQLInstanceName - The name of the SQL instance that hosts the database. + The name of the SQL instance that hosts the database. .PARAMETER Database - Specify the name of the database to execute the query on. + Specify the name of the database to execute the query on. .PARAMETER Query - The query string to execute. + The query string to execute. + + .PARAMETER DatabaseCredential + PSCredential object with the credentials to use to impersonate a user when connecting. + If this is not provided then the current user will be used to connect to the SQL Server Database Engine instance. + + .PARAMETER LoginType + Specifies which type of logon credential should be used. The valid types are + Integrated, WindowsUser, and SqlLogin. If WindowsUser or SqlLogin are specified + then the SetupCredential needs to be specified as well. + + .PARAMETER SqlServerObject + You can pass in an object type of 'Microsoft.SqlServer.Management.Smo.Server'. This can also be passed in + through the pipeline allowing you to use connect-sql | invoke-query if you wish. .PARAMETER WithResults - Specifies if the query should return results. + Specifies if the query should return results. .PARAMETER StatementTimeout - Set the query StatementTimeout in seconds. Default 600 seconds (10mins). + Set the query StatementTimeout in seconds. Default 600 seconds (10mins). + + .EXAMPLE + Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master ` + -Query 'SELECT name FROM sys.databases' -WithResults .EXAMPLE - Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master -Query 'SELECT name FROM sys.databases' -WithResults + Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master ` + -Query 'RESTORE DATABASE [NorthWinds] WITH RECOVERY' .EXAMPLE - Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master -Query 'RESTORE DATABASE [NorthWinds] WITH RECOVERY' + Connect-SQL @sqlConnectionParameters | Invoke-Query -Database master ` + -Query 'SELECT name FROM sys.databases' -WithResults #> function Invoke-Query { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName='SqlServer')] param ( - [Parameter(Mandatory = $true)] + [Alias("ServerName")] + [Parameter(ParameterSetName='SqlServer')] [ValidateNotNullOrEmpty()] [System.String] - $SQLServer, + $SQLServer = $env:COMPUTERNAME, - [Parameter(Mandatory = $true)] + [Alias("InstanceName")] + [Parameter(ParameterSetName='SqlServer')] [System.String] - $SQLInstanceName, + $SQLInstanceName = 'MSSQLSERVER', [Parameter(Mandatory = $true)] [System.String] @@ -1619,19 +1641,52 @@ function Invoke-Query [System.String] $Query, + [Alias("SetupCredential")] + [Parameter()] + [System.Management.Automation.PSCredential] + $DatabaseCredential, + + [Parameter()] + [ValidateSet('Integrated', 'WindowsUser', 'SqlLogin')] + [System.String] + $LoginType = 'Integrated', + + [Parameter(ValueFromPipeline, ParameterSetName='SqlObject', Mandatory = $true)] + [ValidateNotNull()] + [Microsoft.SqlServer.Management.Smo.Server] + $SqlServerObject, + [Parameter()] [Switch] $WithResults, - [Parameter()] [ValidateNotNull()] [System.Int32] $StatementTimeout = 600 ) - $serverObject = Connect-SQL -ServerName $SQLServer -InstanceName $SQLInstanceName -StatementTimeout $StatementTimeout + if ($PSCmdlet.ParameterSetName -eq 'SqlObject') + { + $serverObject = $SqlServerObject + } + elseif ($PSCmdlet.ParameterSetName -eq 'SqlServer') + { + $connectSQLParameters = @{ + ServerName = $SQLServer + InstanceName = $SQLInstanceName + LoginType = $LoginType + StatementTimeout = $StatementTimeout + } + + if ($PSBoundParameters.ContainsKey('DatabaseCredential')) + { + $connectSQLParameters.SetupCredential = $DatabaseCredential + } + + $serverObject = Connect-SQL @connectSQLParameters + } - if ( $WithResults ) + if ($WithResults) { try { diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 9da6ad020..3ca5efd7a 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1390,46 +1390,55 @@ InModuleScope 'SqlServerDsc.Common' { BeforeAll { $mockExpectedQuery = '' - $mockConnectSql = { - return @( - ( - New-Object -TypeName PSObject -Property @{ - Databases = @{ - 'master' = ( - New-Object -TypeName PSObject -Property @{ Name = 'master' } | - Add-Member -MemberType ScriptMethod -Name ExecuteNonQuery -Value { - param - ( - [Parameter()] - [System.String] - $sqlCommand - ) - - if ( $sqlCommand -ne $mockExpectedQuery ) - { - throw - } - } -PassThru | - Add-Member -MemberType ScriptMethod -Name ExecuteWithResults -Value { - param - ( - [Parameter()] - [System.String] - $sqlCommand - ) - - if ( $sqlCommand -ne $mockExpectedQuery ) - { - throw - } - - return New-Object -TypeName System.Data.DataSet - } -PassThru - ) - } - } - ) + $mockSetupCredentialUserName = 'TestUserName12345' + $mockSetupCredentialPassword = 'StrongOne7.' + $mockSetupCredentialSecurePassword = ConvertTo-SecureString -String $mockSetupCredentialPassword -AsPlainText -Force + $mockSetupCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSetupCredentialUserName, $mockSetupCredentialSecurePassword) + + $masterDatabaseObject = New-Object -TypeName PSObject + $masterDatabaseObject | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'master' + $masterDatabaseObject | Add-Member -MemberType ScriptMethod -Name 'ExecuteNonQuery' -Value { + param + ( + [Parameter()] + [System.String] + $sqlCommand ) + + if ( $sqlCommand -ne $mockExpectedQuery ) + { + throw + } + } + + $masterDatabaseObject | Add-Member -MemberType ScriptMethod -Name 'ExecuteWithResults' -Value { + param + ( + [Parameter()] + [System.String] + $sqlCommand + ) + + if ( $sqlCommand -ne $mockExpectedQuery ) + { + throw + } + + return New-Object -TypeName System.Data.DataSet + } + + $databasesObject = New-Object -TypeName PSObject + $databasesObject | Add-Member -MemberType NoteProperty -Name 'Databases' -Value @{ + 'master' = $masterDatabaseObject + } + + $mockSMOServer = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' + $mockSMOServer | Add-Member -MemberType NoteProperty -Name 'Databases' -Value @{ + 'master' = $masterDatabaseObject + } -Force + + $mockConnectSql = { + return @($databasesObject) } $mockThrowLocalizedMessage = { @@ -1443,10 +1452,17 @@ InModuleScope 'SqlServerDsc.Common' { } $queryParams = @{ - SQLServer = 'Server1' - SQLInstanceName = 'MSSQLSERVER' - Database = 'master' - Query = '' + SQLServer = 'Server1' + SQLInstanceName = 'MSSQLSERVER' + Database = 'master' + Query = '' + DatabaseCredential = $mockSetupCredential + } + + $queryParametersWithSMO = @{ + Query = '' + SqlServerObject = $mockSMOServer + Database = 'master' } Context 'Execute a query with no results' { @@ -1462,7 +1478,9 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should throw the correct error, ExecuteNonQueryFailed, when executing the query fails' { $queryParams.Query = 'BadQuery' - { Invoke-Query @queryParams } | Should -Throw ($script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database) + { Invoke-Query @queryParams } | Should -Throw ( + $script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database + ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } @@ -1481,11 +1499,80 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should throw the correct error, ExecuteQueryWithResultsFailed, when executing the query fails' { $queryParams.Query = 'BadQuery' - { Invoke-Query @queryParams -WithResults } | Should -Throw ($script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database) + { Invoke-Query @queryParams -WithResults } | Should -Throw ( + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } } + + Context 'Pass in an SMO Server Object' { + Context 'Execute a query with no results' { + It 'Should execute the query silently' { + $queryParametersWithSMO.Query = "EXEC sp_configure 'show advanced option', '1'" + $mockExpectedQuery = $queryParametersWithSMO.Query.Clone() + + { Invoke-Query @queryParametersWithSMO } | Should -Not -Throw + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + + It 'Should throw the correct error, ExecuteNonQueryFailed, when executing the query fails' { + $queryParametersWithSMO.Query = 'BadQuery' + + { Invoke-Query @queryParametersWithSMO } | Should -Throw ( + $script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database + ) + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + } + + Context 'Execute a query with results' { + It 'Should execute the query and return a result set' { + $queryParametersWithSMO.Query = 'SELECT name FROM sys.databases' + $mockExpectedQuery = $queryParametersWithSMO.Query.Clone() + + Invoke-Query @queryParametersWithSMO -WithResults | Should -Not -BeNullOrEmpty + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + + It 'Should throw the correct error, ExecuteQueryWithResultsFailed, when executing the query fails' { + $queryParametersWithSMO.Query = 'BadQuery' + + { Invoke-Query @queryParametersWithSMO -WithResults } | Should -Throw ( + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + ) + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + } + + Context 'Execute a query with piped SMO server object' { + It 'Should execute the query and return a result set' { + $mockQuery = 'SELECT name FROM sys.databases' + $mockExpectedQuery = $mockQuery + + $mockSMOServer | Invoke-Query -Query $mockQuery -Database master -WithResults | + Should -Not -BeNullOrEmpty + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + + It 'Should throw the correct error, ExecuteQueryWithResultsFailed, when executing the query fails' { + $mockQuery = 'BadQuery' + + { $mockSMOServer | Invoke-Query -Query $mockQuery -Database master -WithResults } | + Should -Throw ( + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + ) + + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly + } + } + } } Describe 'SqlServerDsc.Common\Update-AvailabilityGroupReplica' -Tag 'UpdateAvailabilityGroupReplica' { From 728a1cf4dc6363e2959eb8b85f38c20199c0bf2e Mon Sep 17 00:00:00 2001 From: Wayne Teutenberg Date: Wed, 3 Jul 2019 03:00:53 +1200 Subject: [PATCH 02/13] SqlServerSecureConnection: Fix issue #1350 (#1376) - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix issue #1350 --- CHANGELOG.md | 4 ++- .../MSFT_SqlServerSecureConnection.psm1 | 12 +++++++ .../SqlServerDsc.Common.psm1 | 17 ++++++---- .../MSFT_SqlServerSecureConnection.Tests.ps1 | 33 +++++++++++++++---- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 446e47e43..5d8f9a437 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,7 +86,9 @@ - Fix issue where calling Get would return an error because the database name list may have been returned as a string instead of as a string array ([issue #1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). - +- Changes to SqlServerSecureConnection + - Forced $Thumbprint to lowercase to fix issue#1350 + ## 12.5.0.0 - Changes to SqlServerSecureConnection diff --git a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 index 287f1e92a..5a881a95c 100644 --- a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 +++ b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 @@ -68,6 +68,12 @@ function Get-TargetResource if ($Ensure -eq 'Present') { + # Configuration manager requires thumbprint to be lowercase or it won't display the configured certificate. + if (-not [string]::IsNullOrEmpty($Thumbprint)) + { + $Thumbprint = $Thumbprint.ToLower() + } + $ensureValue = 'Present' $certificateSettings = Test-CertificatePermission -Thumbprint $Thumbprint -ServiceAccount $ServiceAccount if ($encryptionSettings.Certificate -ine $Thumbprint) @@ -199,6 +205,12 @@ function Set-TargetResource $ServiceAccount ) + # Configuration manager requires thumbprint to be lowercase or it won't display the configured certificate. + if (-not [string]::IsNullOrEmpty($Thumbprint)) + { + $Thumbprint = $Thumbprint.ToLower() + } + $parameters = @{ InstanceName = $InstanceName Thumbprint = $Thumbprint diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index f780af7b6..95aaf2c92 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1465,15 +1465,18 @@ function Restart-SqlService { # This call, if it fails, will take between ~9-10 seconds to return. $testConnectionServerObject = Connect-SQL -ServerName $SQLServer -InstanceName $SQLInstanceName -ErrorAction SilentlyContinue - if ($testConnectionServerObject -and $testConnectionServerObject.Status -ne 'Online') - { - # Waiting 2 seconds to not hammer the SQL Server instance. - Start-Sleep -Seconds 2 - } - else + + # Make sure we have an SMO object to test Status + if ($testConnectionServerObject) { - break + if ($testConnectionServerObject.Status -eq 'Online') + { + break + } } + + # Waiting 2 seconds to not hammer the SQL Server instance. + Start-Sleep -Seconds 2 } until ($connectTimer.Elapsed.Seconds -ge $Timeout) $connectTimer.Stop() diff --git a/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 b/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 index bf1d90e51..16d901879 100644 --- a/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 @@ -65,13 +65,12 @@ try [void] AddAccessRule([System.Security.AccessControl.FileSystemAccessRule] $object) { - } } class MockedGetItem { - [string] $Thumbprint = '12345678' + [string] $Thumbprint = '1a11ab1ab1a11111a1111ab111111ab11abcdefa' [string] $PSPath = 'PathToItem' [string] $Path = 'PathToItem' [MockedAccessControl]$ACL = [MockedAccessControl]::new() @@ -89,7 +88,7 @@ try $mockNamedInstanceName = 'INSTANCE' $mockDefaultInstanceName = 'MSSQLSERVER' - $mockThumbprint = '123456789' + $mockThumbprint = '2A11AB1AB1A11111A1111AB111111AB11ABCDEFB' $mockServiceAccount = 'SqlSvc' Describe 'SqlServerSecureConnection\Get-TargetResource' -Tag 'Get' { @@ -117,14 +116,14 @@ try It 'Should return the the state of present' { $resultGetTargetResource = Get-TargetResource @defaultParameters $resultGetTargetResource.InstanceName | Should -Be $mockNamedInstanceName - $resultGetTargetResource.Thumbprint | Should -Be $mockThumbprint + $resultGetTargetResource.Thumbprint | Should -BeExactly $mockThumbprint $resultGetTargetResource.ServiceAccount | Should -Be $mockServiceAccount $resultGetTargetResource.ForceEncryption | Should -Be $true $resultGetTargetResource.Ensure | Should -Be 'Present' Assert-MockCalled -CommandName Get-EncryptedConnectionSetting -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Test-CertificatePermission -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } } - } Context 'When the system is not in the desired state and Ensure is Present' { @@ -302,11 +301,32 @@ try Context 'When the system is not in the desired state' { + Context 'When Thumbprint contain upper-case' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + InstanceName = $mockNamedInstanceName + Thumbprint = $mockThumbprint.ToUpper() + } + } -Verifiable + } + + It 'Should configure with lower-case' { + { Set-TargetResource @defaultParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-EncryptedConnectionSetting -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } + + Assert-MockCalled -CommandName Set-CertificatePermission -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } + + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 1 -Scope It + } + } + Context 'When only certificate permissions are set' { Mock -CommandName Get-TargetResource -MockWith { return @{ InstanceName = $mockNamedInstanceName - Thumbprint = '987654321' + Thumbprint = $mockThumbprint ServiceAccount = $mockServiceAccount ForceEncryption = $false Ensure = 'Present' @@ -336,6 +356,7 @@ try } } Mock -CommandName Test-CertificatePermission -MockWith { return $false } + It 'Should configure only certificate permissions' { { Set-TargetResource @defaultParameters } | Should -Not -Throw From cfd74ebb0cbdcc5498e1bbc6dba3b1a85dbb785a Mon Sep 17 00:00:00 2001 From: Wayne Teutenberg Date: Fri, 5 Jul 2019 19:25:21 +1200 Subject: [PATCH 03/13] Restart-SqlService: Regression test for issues #1306 (#1384) --- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 3ca5efd7a..e8357f3ff 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1132,11 +1132,11 @@ InModuleScope 'SqlServerDsc.Common' { $mockDynamicStatus = 'Offline' - It 'Should throw the correct error message' { - $errorMessage = $localizedData.FailedToConnectToInstanceTimeout -f $env:ComputerName, 'MSSQLSERVER', 1 + It 'Should wait for timeout before throwing error message' { + $errorMessage = $localizedData.FailedToConnectToInstanceTimeout -f $env:ComputerName, 'MSSQLSERVER', 4 { - Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'MSSQLSERVER' -Timeout 1 + Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'MSSQLSERVER' -Timeout 4 } | Should -Throw $errorMessage Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { @@ -1145,7 +1145,7 @@ InModuleScope 'SqlServerDsc.Common' { Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { $PSBoundParameters.ContainsKey('ErrorAction') -eq $true - } -Scope It -Exactly -Times 1 + } -Scope It -Exactly -Times 2 } } } From 0a91a406611d7fd6ee8a6d5a571d14d39b460d98 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Fri, 5 Jul 2019 11:41:39 +0200 Subject: [PATCH 04/13] Update CHANGELOG.md (#1385) * Update CHANGELOG.md Fix change log after a recent merge, and change log clean up * Merge branch 'dev' into johlju-patch-1 --- CHANGELOG.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d8f9a437..2ff11ab1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,19 @@ ## Unreleased -- Changes to common module: Invoke-Query - - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355) - - Works together with Connect-SQL now - - Parameters and Aliases now match that of Connect-SQL - - Can now pass in credentials - - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object - - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object - - Can pipe Connect-SQL | Invoke-Query - - Added default vaules to Invoke-Query - +- Changes to SqlServerDsc + - Changes to helper function Invoke-Query + - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355). + - Works together with Connect-SQL now. + - Parameters and Aliases now match that of Connect-SQL. + - Can now pass in credentials. + - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object. + - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object. + - Can pipe Connect-SQL | Invoke-Query. + - Added default vaules to Invoke-Query. +- Changes to SqlServerSecureConnection + - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). + ## 13.0.0.0 - Changes to SqlServerDsc @@ -86,9 +89,7 @@ - Fix issue where calling Get would return an error because the database name list may have been returned as a string instead of as a string array ([issue #1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). -- Changes to SqlServerSecureConnection - - Forced $Thumbprint to lowercase to fix issue#1350 - + ## 12.5.0.0 - Changes to SqlServerSecureConnection From 2d37a930d957d2bbe148d118a5065a2f6f33d29e Mon Sep 17 00:00:00 2001 From: Daniel Scott-Raynsford Date: Sat, 6 Jul 2019 22:35:16 +1200 Subject: [PATCH 05/13] SqlSetup: Fix minor style violation (#1388) - Changes to SqlSetup - Correct minor style violation (issue #1387). --- CHANGELOG.md | 4 +++- DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ff11ab1e..70e97f10d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ - Added default vaules to Invoke-Query. - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). - +- Changes to SqlSetup + - Correct minor style violation [issue #1387](https://github.com/PowerShell/SqlServerDsc/issues/1387). + ## 13.0.0.0 - Changes to SqlServerDsc diff --git a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 index 1ecc0340e..b50142baf 100644 --- a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 +++ b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 @@ -2388,7 +2388,7 @@ function ConvertTo-StartupType $StartMode ) - If ($StartMode -eq 'Auto') + if ($StartMode -eq 'Auto') { $StartMode = 'Automatic' } From bb95eb8794615c6a08eebefcaf6b79e5b23e1a16 Mon Sep 17 00:00:00 2001 From: Paul Shamus Date: Tue, 16 Jul 2019 22:39:29 -0700 Subject: [PATCH 06/13] SqlDatabaseUser: New DSC resource (#1391) - Changes to SqlServerDsc - New DSC resource SqlDatabaseUser (issue #846). - Adds ability to create database users with more fine-grained control, e.g. re-mapping of orphaned logins or a different login. Supports creating a user with or without login name, and database users mapped to a certificate or asymmetric key. - Minor style fixes in unit tests. - Changes to SqlDatabase - Get-TargetResource now correctly return `$null` for the collation property when the database does not exist (issue #1395). - No longer enforces the collation property if the Collation parameter is not part of the configuration (issue #1396). - Updated resource description in README.md - Fix examples to use `PsDscRunAsCredential` (issue #760). - Added integration tests (issue #739). - Updated unit tests to the latest template (issue #1068). --- CHANGELOG.md | 17 +- .../MSFT_SqlDatabase/MSFT_SqlDatabase.psm1 | 9 +- .../MSFT_SqlDatabaseUser.psm1 | 859 +++++++++++++ .../MSFT_SqlDatabaseUser.schema.mof | 16 + .../en-US/MSFT_SqlDatabaseUser.strings.psd1 | 30 + Examples/README.md | 1 + .../SqlDatabase/1-CreateDatabase.ps1 | 4 + .../SqlDatabase/2-DeleteDatabase.ps1 | 2 + .../1-AddDatabaseUserWithLogin.ps1 | 56 + .../2-AddDatabaseUserWithoutLogin.ps1 | 29 + ...3-AddDatabaseUserMappedToAsymmetricKey.ps1 | 32 + .../4-AddDatabaseUserMappedToCertificate.ps1 | 32 + .../SqlDatabaseUser/5-RemoveDatabaseUser.ps1 | 30 + .../SqlServerDsc.Common.psm1 | 7 +- README.md | 77 +- .../MSFT_SqlDatabase.Integration.Tests.ps1 | 199 +++ Tests/Integration/MSFT_SqlDatabase.config.ps1 | 107 ++ ...MSFT_SqlDatabaseUser.Integration.Tests.ps1 | 497 ++++++++ .../MSFT_SqlDatabaseUser.config.ps1 | 345 ++++++ .../MSFT_SqlServerDatabaseMail.config.ps1 | 2 - .../MSFT_SqlServiceAccount.config.ps1 | 4 +- Tests/Integration/README.md | 136 +- Tests/Unit/MSFT_SqlAGListener.Tests.ps1 | 124 +- Tests/Unit/MSFT_SqlAlias.Tests.ps1 | 122 +- Tests/Unit/MSFT_SqlDatabase.Tests.ps1 | 45 +- .../MSFT_SqlDatabaseDefaultLocation.Tests.ps1 | 18 +- Tests/Unit/MSFT_SqlDatabaseOwner.Tests.ps1 | 18 +- .../Unit/MSFT_SqlDatabasePermission.Tests.ps1 | 56 +- .../MSFT_SqlDatabaseRecoveryModel.Tests.ps1 | 16 +- Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 | 118 +- Tests/Unit/MSFT_SqlDatabaseUser.Tests.ps1 | 1098 +++++++++++++++++ .../Unit/MSFT_SqlServerDatabaseMail.Tests.ps1 | 36 +- Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 | 36 +- ...MSFT_SqlServerEndpointPermission.Tests.ps1 | 24 +- .../MSFT_SqlServerEndpointState.Tests.ps1 | 30 +- Tests/Unit/MSFT_SqlServerMaxDop.Tests.ps1 | 48 +- Tests/Unit/MSFT_SqlServerPermission.Tests.ps1 | 22 +- Tests/Unit/MSFT_SqlServerRole.Tests.ps1 | 118 +- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 6 +- 39 files changed, 3981 insertions(+), 445 deletions(-) create mode 100644 DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.psm1 create mode 100644 DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.schema.mof create mode 100644 DSCResources/MSFT_SqlDatabaseUser/en-US/MSFT_SqlDatabaseUser.strings.psd1 create mode 100644 Examples/Resources/SqlDatabaseUser/1-AddDatabaseUserWithLogin.ps1 create mode 100644 Examples/Resources/SqlDatabaseUser/2-AddDatabaseUserWithoutLogin.ps1 create mode 100644 Examples/Resources/SqlDatabaseUser/3-AddDatabaseUserMappedToAsymmetricKey.ps1 create mode 100644 Examples/Resources/SqlDatabaseUser/4-AddDatabaseUserMappedToCertificate.ps1 create mode 100644 Examples/Resources/SqlDatabaseUser/5-RemoveDatabaseUser.ps1 create mode 100644 Tests/Integration/MSFT_SqlDatabase.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_SqlDatabase.config.ps1 create mode 100644 Tests/Integration/MSFT_SqlDatabaseUser.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_SqlDatabaseUser.config.ps1 create mode 100644 Tests/Unit/MSFT_SqlDatabaseUser.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e97f10d..19b3953ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ## Unreleased - Changes to SqlServerDsc + - New DSC resource SqlDatabaseUser ([issue #846](https://github.com/PowerShell/SqlServerDsc/issues/846)). + - Adds ability to create database users with more fine-grained control, + e.g. re-mapping of orphaned logins or a different login. Supports + creating a user with or without login name, and database users mapped + to a certificate or asymmetric key. - Changes to helper function Invoke-Query - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355). - Works together with Connect-SQL now. @@ -11,11 +16,21 @@ - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object. - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object. - Can pipe Connect-SQL | Invoke-Query. - - Added default vaules to Invoke-Query. + - Added default values to Invoke-Query. + - Minor style fixes in unit tests. - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). - Changes to SqlSetup - Correct minor style violation [issue #1387](https://github.com/PowerShell/SqlServerDsc/issues/1387). +- Changes to SqlDatabase + - Get-TargetResource now correctly return `$null` for the collation property + when the database does not exist ([issue #1395](https://github.com/PowerShell/SqlServerDsc/issues/1395)). + - No longer enforces the collation property if the Collation parameter + is not part of the configuration ([issue #1396](https://github.com/PowerShell/SqlServerDsc/issues/1396)). + - Updated resource description in README.md + - Fix examples to use `PsDscRunAsCredential` ([issue #760](https://github.com/PowerShell/SqlServerDsc/issues/760)). + - Added integration tests ([issue #739](https://github.com/PowerShell/SqlServerDsc/issues/739)). + - Updated unit tests to the latest template ([issue #1068](https://github.com/PowerShell/SqlServerDsc/issues/1068)). ## 13.0.0.0 diff --git a/DSCResources/MSFT_SqlDatabase/MSFT_SqlDatabase.psm1 b/DSCResources/MSFT_SqlDatabase/MSFT_SqlDatabase.psm1 index 86e8adec7..9c1e74fb8 100644 --- a/DSCResources/MSFT_SqlDatabase/MSFT_SqlDatabase.psm1 +++ b/DSCResources/MSFT_SqlDatabase/MSFT_SqlDatabase.psm1 @@ -68,8 +68,6 @@ function Get-TargetResource $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName if ($sqlServerObject) { - $sqlDatabaseCollation = $sqlServerObject.Collation - # Check database exists $sqlDatabaseObject = $sqlServerObject.Databases[$Name] @@ -299,11 +297,6 @@ function Test-TargetResource $getTargetResourceResult = Get-TargetResource @PSBoundParameters $isDatabaseInDesiredState = $true - if (-not $PSBoundParameters.ContainsKey('Collation')) - { - $Collation = $getTargetResourceResult.Collation - } - switch ($Ensure) { 'Absent' @@ -328,7 +321,7 @@ function Test-TargetResource $isDatabaseInDesiredState = $false } - elseif ($getTargetResourceResult.Collation -ne $Collation) + elseif ($PSBoundParameters.ContainsKey('Collation') -and $getTargetResourceResult.Collation -ne $Collation) { Write-Verbose -Message ( $script:localizedData.CollationWrong -f $Name, $getTargetResourceResult.Collation, $Collation diff --git a/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.psm1 b/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.psm1 new file mode 100644 index 000000000..5bdb4c10e --- /dev/null +++ b/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.psm1 @@ -0,0 +1,859 @@ +$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' + +$script:resourceHelperModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'SqlServerDsc.Common' +Import-Module -Name (Join-Path -Path $script:resourceHelperModulePath -ChildPath 'SqlServerDsc.Common.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_SqlDatabaseUser' + +<# + .SYNOPSIS + Returns the current state of the database user in a database. + + .PARAMETER Name + Specifies the name of the database user to be added or removed. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the database exist. + + .PARAMETER DatabaseName + Specifies the name of the database in which to configure the database user. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $DatabaseName + ) + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + Write-Verbose -Message ( + $script:localizedData.RetrievingDatabaseUser -f $Name, $DatabaseName + ) + + $returnValue = @{ + Ensure = 'Absent' + Name = $Name + ServerName = $ServerName + InstanceName = $InstanceName + DatabaseName = $DatabaseName + LoginName = $null + AsymmetricKeyName = $null + CertificateName = $null + UserType = $null + AuthenticationType = $null + LoginType = $null + } + + # Check if database exists. + $sqlDatabaseObject = $sqlServerObject.Databases[$DatabaseName] + + if ($sqlDatabaseObject) + { + $sqlUserObject = $sqlDatabaseObject.Users[$Name] + + if ($sqlUserObject) + { + Write-Verbose -Message ( + $script:localizedData.DatabaseUserExist -f $Name, $DatabaseName + ) + + $returnValue['Ensure'] = 'Present' + $returnValue['LoginName'] = $sqlUserObject.Login + $returnValue['AsymmetricKeyName'] = $sqlUserObject.AsymmetricKey + $returnValue['CertificateName'] = $sqlUserObject.Certificate + $returnValue['AuthenticationType'] = $sqlUserObject.AuthenticationType + $returnValue['LoginType'] = $sqlUserObject.LoginType + $returnValue['UserType'] = ConvertTo-UserType -AuthenticationType $sqlUserObject.AuthenticationType -LoginType $sqlUserObject.LoginType + } + else + { + Write-Verbose -Message ( + $script:localizedData.DatabaseUserDoesNotExist -f $Name, $DatabaseName + ) + } + } + else + { + $errorMessage = $script:localizedData.DatabaseNotFound -f $DatabaseName + New-ObjectNotFoundException -Message $errorMessage + } + + return $returnValue +} + +<# + .SYNOPSIS + Creates, removes or updates a database user in a database. + + .PARAMETER Name + Specifies the name of the database user to be added or removed. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the database exist. + + .PARAMETER DatabaseName + Specifies the name of the database in which to configure the database user. + + .PARAMETER LoginName + Specifies the name of the SQL login to associate with the database user. + This must be specified if parameter UserType is set to 'Login'. + + .PARAMETER AsymmetricKeyName + Specifies the name of the asymmetric key to associate with the database + user. This must be specified if parameter UserType is set to 'AsymmetricKey'. + + .PARAMETER CertificateName + Specifies the name of the certificate to associate with the database + user. This must be specified if parameter UserType is set to 'Certificate'. + + .PARAMETER UserType + Specifies the type of the database user. Valid values are 'Login', + 'NoLogin', 'Certificate', or 'AsymmetricKey'. Defaults to 'NoLogin'. + + .PARAMETER Ensure + Specifies if the database user should be present or absent. If 'Present' + then the user will be added to the database and, if needed, the login + mapping will be updated. If 'Absent' then the user will be removed from + the database. Defaults to 'Present'. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $DatabaseName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $LoginName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $AsymmetricKeyName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $CertificateName, + + [Parameter()] + [ValidateSet( + 'Login', + 'NoLogin', + 'Certificate', + 'AsymmetricKey' + )] + [System.String] + $UserType = 'NoLogin', + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $Force = $false + ) + + Assert-Parameters @PSBoundParameters + + Write-Verbose -Message ( + $script:localizedData.SetDatabaseUser -f $Name, $DatabaseName + ) + + $getTargetResourceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Database = $DatabaseName + Name = $Name + } + + # Get-TargetResource will also help us to test if the database exist. + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + # Default parameters for the cmdlet Invoke-Query used throughout. + $invokeQueryParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Database = $DatabaseName + } + + $recreateDatabaseUser = $false + + if ($getTargetResourceResult.Ensure -eq $Ensure) + { + if ($Ensure -eq 'Present') + { + # Update database user properties, if needed. + if ($UserType -eq $getTargetResourceResult.UserType) + { + switch ($UserType) + { + 'Login' + { + if ($getTargetResourceResult.LoginName -ne $LoginName) + { + Write-Verbose -Message ( + $script:localizedData.ChangingLoginName -f $Name, $LoginName + ) + + # Assert that the login exist. + Assert-SqlLogin @PSBoundParameters + + try + { + <# + Must provide 'WITH NAME' and set to the same name, otherwise + the database user could be renamed in certain conditions. + See remarks section in this article: + https://docs.microsoft.com/en-us/sql/t-sql/statements/alter-user-transact-sql#remarks + #> + Invoke-Query @invokeQueryParameters -Query ( + 'ALTER USER [{0}] WITH NAME = [{1}], LOGIN = [{2}];' -f $Name, $Name, $LoginName + ) + } + catch + { + $errorMessage = $script:localizedData.FailedUpdateDatabaseUser -f $Name, $DatabaseName, $UserType + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + } + + 'AsymmetricKey' + { + if ($getTargetResourceResult.AsymmetricKeyName -ne $AsymmetricKeyName) + { + <# + Not allowed to alter a database user mapped to an asymmetric key, + the database user need to be re-created. + #> + Write-Verbose -Message ( + $script:localizedData.ChangingAsymmetricKey -f $Name, $getTargetResourceResult.AsymmetricKeyName, $AsymmetricKeyName, $DatabaseName + ) + + $recreateDatabaseUser = $true + } + } + + 'Certificate' + { + if ($getTargetResourceResult.CertificateName -ne $CertificateName) + { + <# + Not allowed to alter a database user mapped to a certificate, + the database user need to be re-created. + #> + Write-Verbose -Message ( + $script:localizedData.ChangingCertificate -f $Name, $getTargetResourceResult.CertificateName, $CertificateName, $DatabaseName + ) + + $recreateDatabaseUser = $true + } + } + } + } + else + { + <# + Current database user have a different user type, the + database user need to be re-created. + #> + Write-Verbose -Message ( + $script:localizedData.ChangingUserType -f $Name, $getTargetResourceResult.UserType, $UserType, $DatabaseName + ) + + $recreateDatabaseUser = $true + } + + } + } + + # Throw if not opt-in to re-create database user. + if ($recreateDatabaseUser -and -not $Force) + { + $errorMessage = $script:localizedData.ForceNotEnabled + New-InvalidOperationException -Message $errorMessage + } + + if (($Ensure -eq 'Absent' -and $getTargetResourceResult.Ensure -ne $Ensure) -or $recreateDatabaseUser) + { + # Drop the database user. + try + { + Write-Verbose -Message ( + $script:localizedData.DropDatabaseUser -f $Name, $DatabaseName + ) + + Invoke-Query @invokeQueryParameters -Query ( + 'DROP USER [{0}];' -f $Name + ) + } + catch + { + $errorMessage = $script:localizedData.FailedDropDatabaseUser -f $Name, $DatabaseName + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + + <# + This evaluation is made to handle creation and re-creation of a database + user to minimize the logic when the user has a different user type, or + when there are restrictions on altering an existing database user. + #> + if (($Ensure -eq 'Present' -and $getTargetResourceResult.Ensure -ne $Ensure) -or $recreateDatabaseUser) + { + # Create the database user. + try + { + Write-Verbose -Message ( + $script:localizedData.CreateDatabaseUser -f $Name, $DatabaseName, $UserType + ) + + switch ($UserType) + { + 'Login' + { + # Assert that the login exist. + Assert-SqlLogin @PSBoundParameters + + Invoke-Query @invokeQueryParameters -Query ( + 'CREATE USER [{0}] FOR LOGIN [{1}];' -f $Name, $LoginName + ) + } + + 'NoLogin' + { + Invoke-Query @invokeQueryParameters -Query ( + 'CREATE USER [{0}] WITHOUT LOGIN;' -f $Name + ) + } + + 'AsymmetricKey' + { + # Assert that the asymmetric key exist. + Assert-DatabaseAsymmetricKey @PSBoundParameters + + Invoke-Query @invokeQueryParameters -Query ( + 'CREATE USER [{0}] FOR ASYMMETRIC KEY [{1}];' -f $Name, $AsymmetricKeyName + ) + } + + 'Certificate' + { + # Assert that the certificate exist. + Assert-DatabaseCertificate @PSBoundParameters + + Invoke-Query @invokeQueryParameters -Query ( + 'CREATE USER [{0}] FOR CERTIFICATE [{1}];' -f $Name, $CertificateName + ) + } + } + } + catch + { + $errorMessage = $script:localizedData.FailedCreateDatabaseUser -f $Name, $DatabaseName, $UserType + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } +} + +<# + .SYNOPSIS + Determines if the database user in a database is in desired state. + + .PARAMETER Name + Specifies the name of the database user to be added or removed. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the database exist. + + .PARAMETER DatabaseName + Specifies the name of the database in which to configure the database user. + + .PARAMETER LoginName + Specifies the name of the SQL login to associate with the database user. + This must be specified if parameter UserType is set to 'Login'. + + .PARAMETER AsymmetricKeyName + Specifies the name of the asymmetric key to associate with the database + user. This must be specified if parameter UserType is set to 'AsymmetricKey'. + + .PARAMETER CertificateName + Specifies the name of the certificate to associate with the database + user. This must be specified if parameter UserType is set to 'Certificate'. + + .PARAMETER UserType + Specifies the type of the database user. Valid values are 'Login', + 'NoLogin', 'Certificate', or 'AsymmetricKey'. Defaults to 'NoLogin'. + + .PARAMETER Ensure + Specifies if the database user should be present or absent. If 'Present' + then the user will be added to the database and, if needed, the login + mapping will be updated. If 'Absent' then the user will be removed from + the database. Defaults to 'Present'. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $DatabaseName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $LoginName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $AsymmetricKeyName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $CertificateName, + + [Parameter()] + [ValidateSet( + 'Login', + 'NoLogin', + 'Certificate', + 'AsymmetricKey' + )] + [System.String] + $UserType = 'NoLogin', + + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter()] + [System.Boolean] + $Force = $false + ) + + Assert-Parameters @PSBoundParameters + + Write-Verbose -Message ( + $script:localizedData.EvaluateDatabaseUser -f $Name, $DatabaseName + ) + + $getTargetResourceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Database = $DatabaseName + Name = $Name + } + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + if ($getTargetResourceResult.Ensure -eq $Ensure) + { + if ($Ensure -eq 'Present') + { + <# + Make sure default values are part of desired values if the user did + not specify them in the configuration. + #> + $desiredValues = @{ } + $PSBoundParameters + $desiredValues['Ensure'] = $Ensure + $desiredValues['UserType'] = $UserType + + $testTargetResourceReturnValue = Test-DscParameterState -CurrentValues $getTargetResourceResult ` + -DesiredValues $desiredValues ` + -ValuesToCheck @( + 'LoginName' + 'AsymmetricKeyName' + 'CertificateName' + 'UserType' + ) + } + else + { + $testTargetResourceReturnValue = $true + } + } + else + { + $testTargetResourceReturnValue = $false + } + + if ($testTargetResourceReturnValue) + { + Write-Verbose -Message $script:localizedData.InDesiredState + } + else + { + Write-Verbose -Message $script:localizedData.NotInDesiredState + } + + return $testTargetResourceReturnValue +} + +<# + .SYNOPSIS + Convert a database user's authentication type property to the correct + user type. + + .PARAMETER AuthenticationType + The authentication type for the database user. + + .PARAMETER LoginType + The login type of the database user. +#> +function ConvertTo-UserType +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $AuthenticationType, + + [Parameter()] + [System.String] + $LoginType + ) + + $userType = switch ($AuthenticationType) + { + { $_ -eq 'Windows' -or $_ -eq 'Instance' } + { + 'Login' + } + + 'None' + { + switch ($LoginType) + { + 'SqlLogin' + { + 'NoLogin' + } + + Default + { + $LoginType + } + } + + } + + Default + { + $errorMessage = $script:localizedData.UnknownAuthenticationType -f $AuthenticationType, $LoginType + New-InvalidOperationException -Message $errorMessage + } + } + + return $userType +} + +<# + .SYNOPSIS + Test if a SQL login exist on the instance. Throws and error + if it does not exist. + + .PARAMETER LoginName + Specifies the name of the SQL login to associate with the database user. + + .PARAMETER AsymmetricKeyName + Specifies the name of the asymmetric key to associate with the database + user. + + .PARAMETER CertificateName + Specifies the name of the certificate to associate with the database + user. + + .PARAMETER UserType + Specifies the type of the database user. Defaults to 'NoLogin'. + + .PARAMETER RemainingArguments + Not used. +#> +function Assert-Parameters +{ + [CmdletBinding()] + param + ( + [Parameter()] + [System.String] + $LoginName, + + [Parameter()] + [System.String] + $AsymmetricKeyName, + + [Parameter()] + [System.String] + $CertificateName, + + [Parameter()] + [System.String] + $UserType = 'NoLogin', + + # Catch all other splatted parameters from $PSBoundParameters + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments + ) + + if ($UserType -ne 'Login' -and $PSBoundParameters.ContainsKey('LoginName')) + { + $errorMessage = $script:localizedData.LoginNameProvidedWithWrongUserType -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } + + if ($UserType -ne 'Certificate' -and $PSBoundParameters.ContainsKey('CertificateName')) + { + $errorMessage = $script:localizedData.CertificateNameProvidedWithWrongUserType -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } + + if ($UserType -ne 'AsymmetricKey' -and $PSBoundParameters.ContainsKey('AsymmetricKeyName')) + { + $errorMessage = $script:localizedData.AsymmetricKeyNameProvidedWithWrongUserType -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } + + if ($UserType -eq 'Login' -and -not $PSBoundParameters.ContainsKey('LoginName')) + { + $errorMessage = $script:localizedData.LoginUserTypeWithoutLoginName -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } + + if ($UserType -eq 'AsymmetricKey' -and -not $PSBoundParameters.ContainsKey('AsymmetricKeyName')) + { + $errorMessage = $script:localizedData.AsymmetricKeyUserTypeWithoutAsymmetricKeyName -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } + + if ($UserType -eq 'Certificate' -and -not $PSBoundParameters.ContainsKey('CertificateName')) + { + $errorMessage = $script:localizedData.CertificateUserTypeWithoutCertificateName -f $UserType + New-InvalidArgumentException -ArgumentName 'Action' -Message $errorMessage + } +} + +<# + .SYNOPSIS + Test if a SQL login exist on the instance. Throws and error + if it does not exist. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the SQL login should be evaluated. + + .PARAMETER LoginName + Specifies the name of the SQL login to be evaluated. + + .PARAMETER RemainingArguments + Not used. +#> +function Assert-SqlLogin +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $LoginName, + + # Catch all other splatted parameters from $PSBoundParameters + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments + ) + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + if (-not $sqlServerObject.Logins[$LoginName]) + { + $errorMessage = $script:localizedData.SqlLoginNotFound -f $LoginName + New-ObjectNotFoundException -Message $errorMessage + } +} + +<# + .SYNOPSIS + Test if a database certificate exist in the database. Throws and error + if it does not exist. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the database exist. + + .PARAMETER DatabaseName + Specifies the name of the database in which the certificate should be + evaluated. + + .PARAMETER CertificateName + Specifies the name of the certificate to be evaluated. + + .PARAMETER RemainingArguments + Not used. +#> +function Assert-DatabaseCertificate +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $DatabaseName, + + [Parameter(Mandatory = $true)] + [System.String] + $CertificateName, + + # Catch all other splatted parameters from $PSBoundParameters + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments + ) + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + if (-not $sqlServerObject.Databases[$DatabaseName].Certificates[$CertificateName]) + { + $errorMessage = $script:localizedData.CertificateNotFound -f $CertificateName, $DatabaseName + New-ObjectNotFoundException -Message $errorMessage + } +} + +<# + .SYNOPSIS + Test if a database asymmetric key exist in the database. Throws and error + if it does not exist. + + .PARAMETER ServerName + Specifies the host name of the SQL Server on which the instance exist. + + .PARAMETER InstanceName + Specifies the SQL instance in which the database exists. + + .PARAMETER DatabaseName + Specifies the name of the database in which the asymmetric key should be + evaluated. + + .PARAMETER AsymmetricKeyName + Specifies the name of the asymmetric key to be evaluated. + + .PARAMETER RemainingArguments + Not used. +#> +function Assert-DatabaseAsymmetricKey +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $ServerName, + + [Parameter(Mandatory = $true)] + [System.String] + $InstanceName, + + [Parameter(Mandatory = $true)] + [System.String] + $DatabaseName, + + [Parameter(Mandatory = $true)] + [System.String] + $AsymmetricKeyName, + + # Catch all other splatted parameters from $PSBoundParameters + [Parameter(ValueFromRemainingArguments = $true)] + $RemainingArguments + ) + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + if (-not $sqlServerObject.Databases[$DatabaseName].AsymmetricKeys[$AsymmetricKeyName]) + { + $errorMessage = $script:localizedData.AsymmetryKeyNotFound -f $AsymmetricKeyName, $DatabaseName + New-ObjectNotFoundException -Message $errorMessage + } +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.schema.mof b/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.schema.mof new file mode 100644 index 000000000..25b415d8f --- /dev/null +++ b/DSCResources/MSFT_SqlDatabaseUser/MSFT_SqlDatabaseUser.schema.mof @@ -0,0 +1,16 @@ +[ClassVersion("1.0.0.0"), FriendlyName("SqlDatabaseUser")] +class MSFT_SqlDatabaseUser : OMI_BaseResource +{ + [Key, Description("Specifies the name of the database user to be added or removed.")] String Name; + [Key, Description("Specifies the host name of the SQL Server on which the instance exist.")] String ServerName; + [Key, Description("Specifies the SQL instance in which the database exist.")] String InstanceName; + [Key, Description("Specifies the name of the database in which to configure the database user.")] String DatabaseName; + [Write, Description("Specifies the name of the SQL login to associate with the database user. This must be specified if parameter UserType is set to 'Login'.")] String LoginName; + [Write, Description("Specifies the name of the asymmetric key to associate with the database user. This must be specified if parameter UserType is set to 'AsymmetricKey'.")] String AsymmetricKeyName; + [Write, Description("Specifies the name of the certificate to associate with the database user. This must be specified if parameter UserType is set to 'Certificate'.")] String CertificateName; + [Write, Description("Specifies the type of the database user. Valid values are 'Login', 'NoLogin', 'Certificate', or 'AsymmetricKey'. Defaults to 'NoLogin'."), ValueMap{"Login", "NoLogin", "Certificate", "AsymmetricKey"}, Values{"Login", "NoLogin", "Certificate", "AsymmetricKey"}] String UserType; + [Write, Description("Specifies if the database user should be present or absent. If 'Present' then the database user will be added to the database and, if needed, the login mapping will be updated. If 'Absent' then the database user will be removed from the database. Defaults to 'Present'."), ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure; + [Write, Description("Specifies if it is allowed to re-create the database user if either the user type, the asymmetric key, or the certificate changes. Defaults to $false not allowing database users to be re-created.")] Boolean Force; + [Read, Description("Returns the authentication type of the SQL login connected to the database user. This will return either 'Windows', 'Instance' or 'None'. The value 'Windows' means the SQL login is using Windows Authentication, 'Instance' means that the SQL login is using SQL authentication, and 'None' means that the database user have no SQL login connected to it.")] String AuthenticationType; + [Read, Description("Returns the login type of the SQL login connected to the database user. If no SQL login is connected to the database user this returns $null.")] String LoginType; +}; diff --git a/DSCResources/MSFT_SqlDatabaseUser/en-US/MSFT_SqlDatabaseUser.strings.psd1 b/DSCResources/MSFT_SqlDatabaseUser/en-US/MSFT_SqlDatabaseUser.strings.psd1 new file mode 100644 index 000000000..370dca49f --- /dev/null +++ b/DSCResources/MSFT_SqlDatabaseUser/en-US/MSFT_SqlDatabaseUser.strings.psd1 @@ -0,0 +1,30 @@ +ConvertFrom-StringData @' + RetrievingDatabaseUser = Retrieving information about the database user '{0}' from the database '{1}'. (SDU0001) + DatabaseNotFound = The database '{0}' does not exist. (SDU0002) + EvaluateDatabaseUser = Determining if the database user '{0}' in the database '{1}' is in the desired state. (SDU0003) + DatabaseUserExist = The database user '{0}' exist in the database '{1}'. (SDU0004) + DatabaseUserDoesNotExist = The database user '{0}' does not exist in the database '{1}'. (SDU0005) + InDesiredState = The database user is in desired state. (SDU0006) + NotInDesiredState = The database user is not in desired state. (SDU0007) + UnknownAuthenticationType = The databaser user has an, by the resource, unsupported combination of authentication type '{0}' and login type '{1}'. (SDU0008) + LoginNameProvidedWithWrongUserType = A login name was provided but the user type is '{0}'. Change to the correct user type or remove the login name. (SDU0009) + AsymmetricKeyNameProvidedWithWrongUserType = A asymmetric key name was provided but the user type is '{0}'. Change to the correct user type or remove the asymmetric key name. (SDU0010) + CertificateNameProvidedWithWrongUserType = A certificate name was provided but the user type is '{0}'. Change to the correct user type or remove the certificate name. (SDU0011) + CreateDatabaseUser = Creating the database user '{0}' in the database '{1}' with the user type '{2}'. (SDU0012) + FailedCreateDatabaseUser = Failed creating the database user '{0}' in the database '{1}' with the user type '{2}'. (SDU0013) + DropDatabaseUser = Removing the database user '{0}' from the database '{1}'. (SDU0014) + FailedDropDatabaseUser = Failed removing the database user '{0}' from the database '{1}'. (SDU0015) + SqlLoginNotFound = The SQL login '{0}' does not exist in the SQL Server instance. Failed to create the database user. (SDU0016) + CertificateNotFound = The certificate '{0}' does not exist in the database '{1}'. Failed to create the database user. (SDU0017) + AsymmetryKeyNotFound = The asymmetry key '{0}' does not exist in the database '{1}'. Failed to create the database user. (SDU0018) + SetDatabaseUser = Setting the database user '{0}' in the database '{1}' to the desired state. (SDU0019) + FailedUpdateDatabaseUser = Failed updating the database user '{0}' in the database '{1}' with the user type '{2}'. (SDU0020) + ChangingLoginName = Changing the databaser user '{0}' to use the SQL login name '{1}'. (SDU0021) + ChangingUserType = The database user '{0}' currently have the user type '{1}', but expected it to be '{2}'. Re-creating the database user '{0}' in the database '{3}'. (SDU0022) + ChangingAsymmetricKey = The database user '{0}' currently have the asymmetric key '{1}', but expected it to be '{2}'. Re-creating the database user '{0}' in the database '{3}'. (SDU0023) + ChangingCertificate = The database user '{0}' currently have the certificate '{1}', but expected it to be '{2}'. Re-creating the database user '{0}' in the database '{3}'. (SDU0024) + LoginUserTypeWithoutLoginName = No login name was provided with the user type '{0}'. Add a login name to the configuration. (SDU0025) + AsymmetricKeyUserTypeWithoutAsymmetricKeyName = No asymmetric key name was provided with the user type '{0}'. Add a asymmetric key name to the configuration. (SDU0026) + CertificateUserTypeWithoutCertificateName = No certificate name was provided with the user type '{0}'. Add a certificate name to the configuration. (SDU0027) + ForceNotEnabled = Unable to re-create the database user. The database user needs to be re-created but the configuration has not opt-in to re-create the database user. To opt-in set the parameter Force to $true. +'@ diff --git a/Examples/README.md b/Examples/README.md index a60a20ccf..9e5225877 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -21,6 +21,7 @@ These are the links to the examples for each individual resource. - [SqlDatabasePermission](Resources/SqlDatabasePermission) - [SqlDatabaseRecoveryModel](Resources/SqlDatabaseRecoveryModel) - [SqlDatabaseRole](Resources/SqlDatabaseRole) +- [SqlDatabaseUser](Resources/SqlDatabaseUser) - [SqlRS](Resources/SqlRS) - [SqlRSSetup](Resources/SqlRSSetup) - [SqlScript](Resources/SqlScript) diff --git a/Examples/Resources/SqlDatabase/1-CreateDatabase.ps1 b/Examples/Resources/SqlDatabase/1-CreateDatabase.ps1 index e3ce99289..72fc8a8ce 100644 --- a/Examples/Resources/SqlDatabase/1-CreateDatabase.ps1 +++ b/Examples/Resources/SqlDatabase/1-CreateDatabase.ps1 @@ -25,6 +25,8 @@ Configuration Example ServerName = 'sqltest.company.local' InstanceName = 'DSC' Name = 'Contoso' + + PsDscRunAsCredential = $SqlAdministratorCredential } SqlDatabase Create_Database_with_different_collation @@ -34,6 +36,8 @@ Configuration Example InstanceName = 'DSC' Name = 'AdventureWorks' Collation = 'SQL_Latin1_General_Pref_CP850_CI_AS' + + PsDscRunAsCredential = $SqlAdministratorCredential } } } diff --git a/Examples/Resources/SqlDatabase/2-DeleteDatabase.ps1 b/Examples/Resources/SqlDatabase/2-DeleteDatabase.ps1 index 482c454f4..480a14e40 100644 --- a/Examples/Resources/SqlDatabase/2-DeleteDatabase.ps1 +++ b/Examples/Resources/SqlDatabase/2-DeleteDatabase.ps1 @@ -22,6 +22,8 @@ Configuration Example ServerName = 'sqltest.company.local' InstanceName = 'DSC' Name = 'AdventureWorks' + + PsDscRunAsCredential = $SqlAdministratorCredential } } } diff --git a/Examples/Resources/SqlDatabaseUser/1-AddDatabaseUserWithLogin.ps1 b/Examples/Resources/SqlDatabaseUser/1-AddDatabaseUserWithLogin.ps1 new file mode 100644 index 000000000..302b1df1b --- /dev/null +++ b/Examples/Resources/SqlDatabaseUser/1-AddDatabaseUserWithLogin.ps1 @@ -0,0 +1,56 @@ +<# + .EXAMPLE + This example shows how to ensure that the database users ReportAdmin, + CONTOSO\ReportEditors, and CONTOSO\ReportViewers are present in the + AdventureWorks database in the instance sqltest.company.local\DSC. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseUser ReportAdmin_AddUser + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'ReportAdmin' + UserType = 'Login' + LoginName = 'ReportAdmin' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabaseUser ContosoReportEditor_AddUser + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'CONTOSO\ReportEditor' + UserType = 'Login' + LoginName = 'CONTOSO\ReportEditor' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + + SqlDatabaseUser ContosoReportViewer_AddUser + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'CONTOSO\ReportViewer' + UserType = 'Login' + LoginName = 'CONTOSO\ReportViewer' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseUser/2-AddDatabaseUserWithoutLogin.ps1 b/Examples/Resources/SqlDatabaseUser/2-AddDatabaseUserWithoutLogin.ps1 new file mode 100644 index 000000000..129aeee24 --- /dev/null +++ b/Examples/Resources/SqlDatabaseUser/2-AddDatabaseUserWithoutLogin.ps1 @@ -0,0 +1,29 @@ +<# + .EXAMPLE + This example shows how to ensure that the database user User1 are present + in the AdventureWorks database in the instance sqltest.company.local\DSC. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseUser AddUser1 + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'User1' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseUser/3-AddDatabaseUserMappedToAsymmetricKey.ps1 b/Examples/Resources/SqlDatabaseUser/3-AddDatabaseUserMappedToAsymmetricKey.ps1 new file mode 100644 index 000000000..8c1a80ae5 --- /dev/null +++ b/Examples/Resources/SqlDatabaseUser/3-AddDatabaseUserMappedToAsymmetricKey.ps1 @@ -0,0 +1,32 @@ +<# + .EXAMPLE + This example shows how to ensure that the database user User1 are + mapped to the asymmetric key Key1 in the AdventureWorks database in + the instance sqltest.company.local\DSC. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseUser ReportAdmin_AddUser + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'ReportAdmin' + UserType = 'AsymmetricKey' + AsymmetricKeyName = 'Key1' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseUser/4-AddDatabaseUserMappedToCertificate.ps1 b/Examples/Resources/SqlDatabaseUser/4-AddDatabaseUserMappedToCertificate.ps1 new file mode 100644 index 000000000..a45939d35 --- /dev/null +++ b/Examples/Resources/SqlDatabaseUser/4-AddDatabaseUserMappedToCertificate.ps1 @@ -0,0 +1,32 @@ +<# + .EXAMPLE + This example shows how to ensure that the database user User1 are + mapped to the certificate Certificate1 in the AdventureWorks database in + the instance sqltest.company.local\DSC. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseUser ReportAdmin_AddUser + { + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'ReportAdmin' + UserType = 'Certificate' + CertificateName = 'Certificate1' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Examples/Resources/SqlDatabaseUser/5-RemoveDatabaseUser.ps1 b/Examples/Resources/SqlDatabaseUser/5-RemoveDatabaseUser.ps1 new file mode 100644 index 000000000..00aa9d325 --- /dev/null +++ b/Examples/Resources/SqlDatabaseUser/5-RemoveDatabaseUser.ps1 @@ -0,0 +1,30 @@ +<# + .EXAMPLE + This example shows how to ensure that the database user CONTOSO\ReportViewers + is absent from the AdventureWorks database in the instance sqltest.company.local\DSC. +#> +Configuration Example +{ + param + ( + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $SqlAdministratorCredential + ) + + Import-DscResource -ModuleName SqlServerDsc + + node localhost + { + SqlDatabaseUser ContosoReportViewer_RemoveUser + { + Ensure = 'Absent' + ServerName = 'sqltest.company.local' + InstanceName = 'DSC' + DatabaseName = 'AdventureWorks' + Name = 'CONTOSO\ReportViewer' + + PsDscRunAsCredential = $SqlAdministratorCredential + } + } +} diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index 95aaf2c92..df398371e 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1625,14 +1625,14 @@ function Invoke-Query [CmdletBinding(DefaultParameterSetName='SqlServer')] param ( - [Alias("ServerName")] [Parameter(ParameterSetName='SqlServer')] + [Alias('ServerName')] [ValidateNotNullOrEmpty()] [System.String] $SQLServer = $env:COMPUTERNAME, - [Alias("InstanceName")] [Parameter(ParameterSetName='SqlServer')] + [Alias('InstanceName')] [System.String] $SQLInstanceName = 'MSSQLSERVER', @@ -1644,8 +1644,8 @@ function Invoke-Query [System.String] $Query, - [Alias("SetupCredential")] [Parameter()] + [Alias('SetupCredential')] [System.Management.Automation.PSCredential] $DatabaseCredential, @@ -1663,6 +1663,7 @@ function Invoke-Query [Switch] $WithResults, + [Parameter()] [ValidateNotNull()] [System.Int32] $StatementTimeout = 600 diff --git a/README.md b/README.md index 3fedb632b..31b599324 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ A full list of changes in each version can be found in the [change log](CHANGELO * [**SqlDatabaseRecoveryModel**](#sqldatabaserecoverymodel) resource to manage database recovery model. * [**SqlDatabaseRole**](#sqldatabaserole) resource to manage SQL database roles. +* [**SqlDatabaseUser**](#sqldatabaseuser) resource to manage SQL database users. * [**SqlRS**](#sqlrs) configures SQL Server Reporting. Services to use a database engine in another instance. * [**SqlRSSetup**](#sqlrssetup) Installs the standalone @@ -575,10 +576,8 @@ All issues are not listed here, see [here for all open issues](https://github.co ### SqlDatabase This resource is used to create or delete a database. For more information about -database, please read: - -* [Create a Database](https://msdn.microsoft.com/en-us/library/ms186312.aspx). -* [Delete a Database](https://msdn.microsoft.com/en-us/library/ms177419.aspx). +SQL Server databases, please read the following articles [Create a Database](https://docs.microsoft.com/en-us/sql/relational-databases/databases/create-a-database) +and [Delete a Database](https://docs.microsoft.com/en-us/sql/relational-databases/databases/delete-a-database). #### Requirements @@ -794,6 +793,76 @@ manages members in both built-in and user created database roles. All issues are not listed here, see [here for all open issues](https://github.com/PowerShell/SqlServerDsc/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+SqlDatabaseRole). +### SqlDatabaseUser + +This resource is used to create a database users. A database user can be +created with or without a login, and a database users can be mapped to a +certificate or asymmetric key. The resource also allows re-mapping of the +SQL login. + +> **Note:** This resource does not yet support [Contained Databases](https://docs.microsoft.com/en-us/sql/relational-databases/databases/contained-databases). + +#### Requirements + +* Target machine must be running Windows Server 2008 R2 or later. +* Target machine must be running SQL Server Database Engine 2008 or later. + +#### Parameters + +* **`[String]` Name** _(Key)_: Specifies the name of the database user to + be added or removed. +* **`[String]` ServerName** _(Key)_: Specifies the host name of the SQL + Server on which the instance exist. +* **`[String]` InstanceName** _(Key)_: Specifies the SQL instance in which + the database exist. +* **`[String]` DatabaseName** _(Key)_: Specifies the name of the database in + which to configure the user. +* **`[String]` LoginName** _(Write)_: Specifies the name of the SQL login to + associate with the database user. This must be specified if parameter + UserType is set to 'Login'. +* **`[String]` AsymmetricKeyName** _(Write)_: Specifies the name of the + asymmetric key to associate with the database user. This must be + specified if parameter UserType is set to 'AsymmetricKey'. +* **`[String]` CertificateName** _(Write)_: Specifies the name of the + certificate to associate with the database user. This must be specified + if parameter UserType is set to 'Certificate'. +* **`[String]` UserType** _(Write)_: Specifies the type of the database + user. Valid values are 'Login', 'NoLogin', 'Certificate', or 'AsymmetricKey'. + Defaults to 'NoLogin'. { Login | *NoLogin* | Certificate | AsymmetricKey }. +* **`[String]` Ensure** _(Write)_: Specifies if the database user should + be present or absent. If 'Present' then the database user will be added + to the database and, if needed, the login mapping will be updated. If + 'Absent' then the database user will be removed from the database. + Defaults to 'Present'. { *Present* | Absent }. +* **`[Boolean]` Force** _(Write)_: Specifies if the resource is allowed + to re-create the database user if either the user type, the asymmetric + key, or the certificate changes. Defaults to $false not allowing + database users to be re-created. + +#### Read-Only Properties from Get-TargetResource + +* **`[String]` AuthenticationType** _(Read)_: Returns the authentication + type of the SQL login connected to the database user. This will return either 'Windows', + 'Instance' or 'None'. The value 'Windows' means the SQL login is using + Windows Authentication, 'Instance' means that the SQL login + is using SQL authentication, and 'None' means that the database user have + no SQL login connected to it. +* **`[String]` LoginType** _(Read)_: Returns the login type of the SQL + login connected to the database user. If no SQL login is connected to + the database user this returns `$null`. + +#### Examples + +* [Add a database user to a database, with a SQL login](/Examples/Resources/SqlDatabaseUser/1-AddDatabaseUserWithLogin.ps1) +* [Add a database user to a database, without a SQL login](/Examples/Resources/SqlDatabaseUser/2-AddDatabaseUserWithoutLogin.ps1) +* [Add a database user to a database, mapped to an asymmetric key](/Examples/Resources/SqlDatabaseUser/3-AddDatabaseUserMappedToAsymmetricKey.ps1) +* [Add a database user to a database, mapped to a certificate](/Examples/Resources/SqlDatabaseUser/4-AddDatabaseUserMappedToCertificate.ps1) +* [Remove database user from a database](/Examples/Resources/SqlDatabaseUser/5-RemoveDatabaseUser.ps1) + +#### Known issues + +All issues are not listed here, see [here for all open issues](https://github.com/PowerShell/SqlServerDsc/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+SqlDatabaseUser). + ### SqlRS Initializes and configures SQL Reporting Services server. diff --git a/Tests/Integration/MSFT_SqlDatabase.Integration.Tests.ps1 b/Tests/Integration/MSFT_SqlDatabase.Integration.Tests.ps1 new file mode 100644 index 000000000..239a6a185 --- /dev/null +++ b/Tests/Integration/MSFT_SqlDatabase.Integration.Tests.ps1 @@ -0,0 +1,199 @@ +# This is used to make sure the integration test run in the correct order. +[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 2)] +param() + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Integration' -Category @('Integration_SQL2016','Integration_SQL2017')) +{ + return +} + +$script:dscModuleName = 'SqlServerDsc' +$script:dscResourceFriendlyName = 'SqlDatabase' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region HEADER +# Integration Test Template Version: 1.3.2 +[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Integration +#endregion + +# Using try/finally to always cleanup. +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $configurationName = "$($script:dscResourceName)_AddDatabase1_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.DatabaseName1 + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.Collation | Should -Be 'Finnish_Swedish_CI_AS' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabase2_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.DatabaseName2 + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.Collation | Should -Be $ConfigurationData.AllNodes.Collation + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_RemoveDatabase2_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.DatabaseName2 + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.Collation | Should -BeNullOrEmpty + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + } +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/Tests/Integration/MSFT_SqlDatabase.config.ps1 b/Tests/Integration/MSFT_SqlDatabase.config.ps1 new file mode 100644 index 000000000..beba28a19 --- /dev/null +++ b/Tests/Integration/MSFT_SqlDatabase.config.ps1 @@ -0,0 +1,107 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file, + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + UserName = "$env:COMPUTERNAME\SqlAdmin" + Password = 'P@ssw0rd1' + + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQLTEST' + + DatabaseName1 = 'Database1' + DatabaseName2 = 'Database2' + Collation = 'SQL_Latin1_General_Pref_CP850_CI_AS' + } + ) + } +} + +<# + .SYNOPSIS + Creates a database with standard collation. +#> +Configuration MSFT_SqlDatabase_AddDatabase1_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabase 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.DatabaseName1 + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database with a specific collation. +#> +Configuration MSFT_SqlDatabase_AddDatabase2_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabase 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.DatabaseName2 + Collation = $Node.Collation + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database with a specific collation. +#> +Configuration MSFT_SqlDatabase_RemoveDatabase2_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabase 'Integration_Test' + { + Ensure = 'Absent' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.DatabaseName2 + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} diff --git a/Tests/Integration/MSFT_SqlDatabaseUser.Integration.Tests.ps1 b/Tests/Integration/MSFT_SqlDatabaseUser.Integration.Tests.ps1 new file mode 100644 index 000000000..fdec2f20b --- /dev/null +++ b/Tests/Integration/MSFT_SqlDatabaseUser.Integration.Tests.ps1 @@ -0,0 +1,497 @@ +# This is used to make sure the integration test run in the correct order. +[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 3)] +param() + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Integration' -Category @('Integration_SQL2016','Integration_SQL2017')) +{ + return +} + +$script:dscModuleName = 'SqlServerDsc' +$script:dscResourceFriendlyName = 'SqlDatabaseUser' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +#region HEADER +# Integration Test Template Version: 1.3.2 +[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Integration +#endregion + +# Using try/finally to always cleanup. +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser1_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User1_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User1_UserType + $resourceCurrentState.LoginName | Should -Be $ConfigurationData.AllNodes.User1_LoginName + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'Windows' + $resourceCurrentState.LoginType | Should -Be 'WindowsUser' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser2_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User2_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User2_UserType + $resourceCurrentState.LoginName | Should -Be $ConfigurationData.AllNodes.User2_LoginName + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'Instance' + $resourceCurrentState.LoginType | Should -Be 'SqlLogin' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser3_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User3_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User3_UserType + $resourceCurrentState.LoginName | Should -BeNullOrEmpty + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'None' + $resourceCurrentState.LoginType | Should -Be 'SqlLogin' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser4_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User4_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User4_UserType + $resourceCurrentState.LoginName | Should -Be $ConfigurationData.AllNodes.User4_LoginName + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'Windows' + $resourceCurrentState.LoginType | Should -Be 'WindowsGroup' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_RecreateDatabaseUser4_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User4_Name + $resourceCurrentState.UserType | Should -Be 'NoLogin' + $resourceCurrentState.LoginName | Should -BeNullOrEmpty + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'None' + $resourceCurrentState.LoginType | Should -Be 'SqlLogin' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_RemoveDatabaseUser4_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User4_Name + $resourceCurrentState.UserType | Should -BeNullOrEmpty + $resourceCurrentState.LoginName | Should -BeNullOrEmpty + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -BeNullOrEmpty + $resourceCurrentState.UserType | Should -BeNullOrEmpty + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser5_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User5_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User5_UserType + $resourceCurrentState.LoginName | Should -BeNullOrEmpty + $resourceCurrentState.AsymmetricKeyName | Should -BeNullOrEmpty + $resourceCurrentState.CertificateName | Should -Be $ConfigurationData.AllNodes.CertificateName + $resourceCurrentState.AuthenticationType | Should -Be 'None' + $resourceCurrentState.LoginType | Should -Be 'Certificate' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_AddDatabaseUser6_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.DatabaseName | Should -Be $ConfigurationData.AllNodes.DatabaseName + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.User6_Name + $resourceCurrentState.UserType | Should -Be $ConfigurationData.AllNodes.User6_UserType + $resourceCurrentState.LoginName | Should -BeNullOrEmpty + $resourceCurrentState.AsymmetricKeyName | Should -Be $ConfigurationData.AllNodes.AsymmetricKeyName + $resourceCurrentState.CertificateName | Should -BeNullOrEmpty + $resourceCurrentState.AuthenticationType | Should -Be 'None' + $resourceCurrentState.LoginType | Should -Be 'AsymmetricKey' + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + } +} +finally +{ + #region FOOTER + Restore-TestEnvironment -TestEnvironment $TestEnvironment + #endregion +} diff --git a/Tests/Integration/MSFT_SqlDatabaseUser.config.ps1 b/Tests/Integration/MSFT_SqlDatabaseUser.config.ps1 new file mode 100644 index 000000000..21c14a8f7 --- /dev/null +++ b/Tests/Integration/MSFT_SqlDatabaseUser.config.ps1 @@ -0,0 +1,345 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file, + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + UserName = "$env:COMPUTERNAME\SqlAdmin" + Password = 'P@ssw0rd1' + + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQLTEST' + + # This is created by the SqlDatabase integration tests. + DatabaseName = 'Database1' + + User1_Name = 'User1' + User1_UserType = 'Login' + User1_LoginName = "$env:COMPUTERNAME\DscUser1" # Windows User + + User2_Name = 'User2' + User2_UserType = 'Login' + User2_LoginName = 'DscUser4' # SQL login + + User3_Name = 'User3' + User3_UserType = 'NoLogin' + + User4_Name = 'User4' + User4_UserType = 'Login' + User4_LoginName = "$env:COMPUTERNAME\DscSqlUsers1" # Windows Group + + User5_Name = 'User5' + User5_UserType = 'Certificate' + CertificateName = 'Certificate1' + + User6_Name = 'User6' + User6_UserType = 'AsymmetricKey' + AsymmetricKeyName = 'AsymmetricKey1' + } + ) + } +} + +<# + .SYNOPSIS + Creates a database user with a login against a SQL login which is of type + Windows user. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser1_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User1_Name + UserType = $Node.User1_UserType + LoginName = $Node.User1_LoginName + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database user with a login against a SQL login which is of type SQL. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser2_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User2_Name + UserType = $Node.User2_UserType + LoginName = $Node.User2_LoginName + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database user without a login. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser3_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User3_Name + UserType = $Node.User3_UserType + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database user with a login against a SQL login which is of type + Windows Group. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser4_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User4_Name + UserType = $Node.User4_UserType + LoginName = $Node.User4_LoginName + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Re-creates a database user which had a login, to a user without login. +#> +Configuration MSFT_SqlDatabaseUser_RecreateDatabaseUser4_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User4_Name + UserType = 'NoLogin' + Force = $true + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Removes a database user. +#> +Configuration MSFT_SqlDatabaseUser_RemoveDatabaseUser4_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Absent' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User4_Name + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database user mapped to a certificate. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser5_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlScriptQuery 'CreateDatabaseCertificate' + { + ServerInstance = Join-Path -Path $Node.ServerName -ChildPath $Node.InstanceName + + GetQuery = @' +SELECT Name FROM [$(DatabaseName)].sys.certificates WHERE Name = '$(CertificateName)' FOR JSON AUTO +'@ + + TestQuery = @' +if (select count(name) from [$(DatabaseName)].sys.certificates where name = '$(CertificateName)') = 0 +BEGIN + RAISERROR ('Did not find the certificate [$(CertificateName)]', 16, 1) +END +ELSE +BEGIN + PRINT 'Found the certificate [$(CertificateName)]' +END +'@ + + SetQuery = @' +USE [$(DatabaseName)]; +CREATE CERTIFICATE [$(CertificateName)] + ENCRYPTION BY PASSWORD = 'P@ssw0rd1' + WITH SUBJECT = 'SqlServerDsc Integration Test'; +'@ + + QueryTimeout = 30 + Variable = @( + ('DatabaseName={0}' -f $Node.DatabaseName) + ('CertificateName={0}' -f $Node.CertificateName) + ) + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User5_Name + UserType = $Node.User5_UserType + CertificateName = $Node.CertificateName + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Creates a database user mapped to an asymmetric key. +#> +Configuration MSFT_SqlDatabaseUser_AddDatabaseUser6_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlScriptQuery 'CreateDatabaseAsymmetricKey' + { + ServerInstance = Join-Path -Path $Node.ServerName -ChildPath $Node.InstanceName + + GetQuery = @' +SELECT Name FROM [$(DatabaseName)].sys.asymmetric_keys WHERE Name = '$(AsymmetricKeyName)' FOR JSON AUTO +'@ + + TestQuery = @' +if (select count(name) from [$(DatabaseName)].sys.asymmetric_keys where name = '$(AsymmetricKeyName)') = 0 +BEGIN + RAISERROR ('Did not find the asymmetric key [$(AsymmetricKeyName)]', 16, 1) +END +ELSE +BEGIN + PRINT 'Found the asymmetric key [$(AsymmetricKeyName)]' +END +'@ + + SetQuery = @' +USE [$(DatabaseName)]; +CREATE ASYMMETRIC KEY [$(AsymmetricKeyName)] + WITH ALGORITHM = RSA_2048 + ENCRYPTION BY PASSWORD = 'P@ssw0rd1'; +'@ + + QueryTimeout = 30 + Variable = @( + ('DatabaseName={0}' -f $Node.DatabaseName) + ('AsymmetricKeyName={0}' -f $Node.AsymmetricKeyName) + ) + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + + SqlDatabaseUser 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + DatabaseName = $Node.DatabaseName + Name = $Node.User6_Name + UserType = $Node.User6_UserType + AsymmetricKeyName = $Node.AsymmetricKeyName + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.UserName, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} diff --git a/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 b/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 index 4083b1370..b70915c06 100644 --- a/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 +++ b/Tests/Integration/MSFT_SqlServerDatabaseMail.config.ps1 @@ -120,5 +120,3 @@ Configuration MSFT_SqlServerDatabaseMail_Remove_Config } } } - - diff --git a/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 b/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 index b02326b50..e090f1dec 100644 --- a/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 +++ b/Tests/Integration/MSFT_SqlServiceAccount.config.ps1 @@ -196,13 +196,13 @@ Configuration MSFT_SqlServiceAccount_StopSqlServerDefaultInstance_Config node $AllNodes.NodeName { - Service ('StartSqlServerAgentForInstance{0}' -f $Node.DefaultInstanceName) + Service ('StopSqlServerAgentForInstance{0}' -f $Node.DefaultInstanceName) { Name = 'SQLSERVERAGENT' State = 'Stopped' } - Service ('StartSqlServerDefaultInstance{0}' -f $Node.DefaultInstanceName) + Service ('StopSqlServerDefaultInstance{0}' -f $Node.DefaultInstanceName) { Name = $Node.DefaultInstanceName State = 'Stopped' diff --git a/Tests/Integration/README.md b/Tests/Integration/README.md index eaa53f575..da139978d 100644 --- a/Tests/Integration/README.md +++ b/Tests/Integration/README.md @@ -11,6 +11,9 @@ configurations then the run order for the new integration tests should be set to a higher run order number than the highest run order of the dependent integration tests. +**Below are the integration tests listed in the run order, and with the dependency +to each other. Dependencies are made to speed up the testing.** + ## SqlSetup **Run order:** 1 @@ -128,6 +131,19 @@ tests creates an Active Directory Detached Cluster with an IP address of The tests will leave the AlwaysOn service disabled. +## SqlDatabase + +**Run order:** 2 + +**Depends on:** SqlSetup + +The integration test will leave a database for other integration tests to +use. + +Database | Collation +--- | --- | --- +Database1 | Finnish_Swedish_CI_AS + ## SqlDatabaseDefaultLocation **Run order:** 2 @@ -168,12 +184,67 @@ The integration tests will leave the following logins on the SQL Server instance Login | Type | Password | Permission --- | --- | --- | --- -DscUser1 | Windows | P@ssw0rd1 | *None* -DscUser2 | Windows | P@ssw0rd1 | *None* +`$env:COMPUTERNAME`\DscUser1 | Windows User | See above | *None* +`$env:COMPUTERNAME`\DscUser2 | Windows User | See above | *None* DscUser4 | SQL | P@ssw0rd1 | *None* +`$env:COMPUTERNAME`\DscSqlUsers1 | Windows Group | -- | *None* + +>**Note:** The `$env:COMPUTERNAME` is reference to the build workers computer +>name. The SQL login could for example be 'APPVYR-WIN\DscUser1'. + +## SqlAgentAlert + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* + +## SqlAgentOperator + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* + +## SqlServerDatabaseMail + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* + +## SqlServerEndpoint + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* -> **Note:** Login DscUser3 was create disabled and was used to test removal of -> a login. +## SqlServerNetwork + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* + +## SqlServiceAccount + +**Run order:** 2 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* ## SqlRS @@ -181,8 +252,12 @@ DscUser4 | SQL | P@ssw0rd1 | *None* **Depends on:** SqlSetup, SqlRSSetup -The integration tests will install the following instances and leave it on the -AppVeyor build worker for other integration tests to use. +When running integration tests for SQL Server 2016 the integration tests +will install the following instances and leave it on the AppVeyor build +worker for other integration tests to use. +When running integration tests for SQL Server 2017 the Reporting Services +instance installed by the resource SqlRSSetup is started, and then stopped +again after the integration tests has run. Instance | Feature | Description --- | --- | --- @@ -213,6 +288,46 @@ Server Role | Members DscServerRole1 | DscUser1, DscUser2 DscServerRole2 | DscUser4 +## SqlDatabaseUser + +**Run order:** 3 + +**Depends on:** SqlSetup, SqlServerLogin, SqlDatabase + +The integration test will leave these database users for other integration tests +to use. + +**Database name:** Database1 + +Name | Type | LoginType | Login | Certificate | Asymmetric Key +--- | --- | --- | --- | --- | --- +User1 | Login | WindowsUser | `$env:COMPUTERNAME`\DscUser1 | - | - +User2 | Login | SqlLogin | DscUser4 | - | - +User3 | NoLogin | SqlLogin | - | - | - +User5 | Certificate | Certificate | - | Certificate1 | - +User6 | AsymmetricKey | AsymmetricKey | - | - | AsymmetricKey1 + +>**Note:** The `$env:COMPUTERNAME` is reference to the build workers computer +>name. The SQL login could for example be 'APPVYR-WIN\DscUser1'. + +The integration test will leave this database certificate for other integration +tests to use. + +**Database name:** Database1 + +Name | Subject | Password +--- | --- | --- +Certificate1 | SqlServerDsc Integration Test | P@ssw0rd1 + +The integration test will leave this database asymmetric key for other integration +tests to use. + +**Database name:** Database1 + +Name | Algorithm | Password +--- | --- | --- +AsymmetricKey1 | RSA_2048 | P@ssw0rd1 + ## SqlScript **Run order:** 4 @@ -254,3 +369,12 @@ Database name | Owner --- | --- ScriptDatabase3 | $env:COMPUTERNAME\SqlAdmin ScriptDatabase4 | DscAdmin1 + +## SqlServerSecureConnection + +**Run order:** 5 + +**Depends on:** SqlSetup + +*The integration tests will clean up and not leave anything on the build +worker.* diff --git a/Tests/Unit/MSFT_SqlAGListener.Tests.ps1 b/Tests/Unit/MSFT_SqlAGListener.Tests.ps1 index 160da4867..95a787f47 100644 --- a/Tests/Unit/MSFT_SqlAGListener.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlAGListener.Tests.ps1 @@ -153,7 +153,7 @@ try It 'Should call the mock function Get-SQLAlwaysOnAvailabilityGroupListener' { $result = Get-TargetResource @testParameters - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } } @@ -190,7 +190,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -227,7 +227,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -260,7 +260,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } Mock -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -MockWith { @@ -285,7 +285,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when IP address is different' { @@ -297,7 +297,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when DHCP is absent but should be present' { @@ -309,7 +309,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when DHCP is the only set parameter' { @@ -318,7 +318,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } Mock -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -MockWith { @@ -346,7 +346,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } } @@ -376,7 +376,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when IP address is the only set parameter' { @@ -385,7 +385,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } Mock -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -MockWith { @@ -410,7 +410,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } } @@ -426,7 +426,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } Mock -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -MockWith { @@ -454,7 +454,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is present when IP address is the only set parameter' { @@ -463,7 +463,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is present when port is the only set parameter' { @@ -472,7 +472,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } } @@ -502,7 +502,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } It 'Should return that desired state is present when DHCP is the only set parameter' { @@ -511,7 +511,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-SQLAlwaysOnAvailabilityGroupListener -Exactly -Times 1 -Scope It } } @@ -549,10 +549,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicListenerName = $mockUnknownListenerName @@ -565,10 +565,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicListenerName = $mockUnknownListenerName @@ -580,10 +580,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicIsDhcp = $false @@ -597,10 +597,10 @@ try { Set-TargetResource @testParameters } | Should -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicIsDhcp = $false @@ -614,10 +614,10 @@ try { Set-TargetResource @testParameters } | Should -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicIsDhcp = $false @@ -631,10 +631,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 1 -Scope It } $mockDynamicIsDhcp = $false @@ -649,10 +649,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicListenerName = $mockKnownListenerName @@ -664,10 +664,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodDropRan | Should -Be $true # Should have made one call to the Drop() method. - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicAvailabilityGroup = $mockUnknownAvailabilityGroup @@ -765,10 +765,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicIsDhcp = $false @@ -781,10 +781,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } $mockDynamicListenerName = $mockUnknownListenerName @@ -796,10 +796,10 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw $script:mockMethodDropRan | Should -Be $false # Should not have called Drop() method. - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - Assert-MockCalled New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It - Assert-MockCalled Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName New-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlAvailabilityGroupListener -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Add-SqlAvailabilityGroupListenerStaticIp -Exactly -Times 0 -Scope It } } diff --git a/Tests/Unit/MSFT_SqlAlias.Tests.ps1 b/Tests/Unit/MSFT_SqlAlias.Tests.ps1 index 50c6360e8..d7a17ebe1 100644 --- a/Tests/Unit/MSFT_SqlAlias.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlAlias.Tests.ps1 @@ -159,13 +159,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -200,13 +200,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -241,13 +241,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -285,13 +285,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -349,13 +349,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -390,15 +390,15 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context } It 'Should not call the Get-ItemProperty for the Wow6432Node-path' { - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 0 -Scope Context } } @@ -450,15 +450,15 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context } It 'Should not call the Get-ItemProperty for the Wow6432Node-path' { - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 0 -Scope Context } } @@ -493,15 +493,15 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context } It 'Should not call the Get-ItemProperty for the Wow6432Node-path' { - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 0 -Scope Context } } @@ -579,13 +579,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -620,13 +620,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -661,13 +661,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -708,15 +708,15 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context } It 'Should not call the Get-ItemProperty for the Wow6432Node-path' { - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 0 -Scope Context } } @@ -774,13 +774,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -838,15 +838,15 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context } It 'Should not call the Get-ItemProperty for the Wow6432Node-path' { - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 0 -Scope Context } } @@ -1108,7 +1108,7 @@ try It "Should return true from the test method" { Test-TargetResource @testParameters | Should -Be $true - Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It } } @@ -1123,13 +1123,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -1145,13 +1145,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -1167,13 +1167,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -1189,13 +1189,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -1228,13 +1228,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } @@ -1250,13 +1250,13 @@ try } It 'Should call the mocked functions exactly 1 time each' { - Assert-MockCalled Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` + Assert-MockCalled -CommandName Get-CimInstance -ParameterFilter { $ClassName -eq 'win32_OperatingSystem' } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPath } ` -Exactly -Times 1 -Scope Context - Assert-MockCalled Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` + Assert-MockCalled -CommandName Get-ItemProperty -ParameterFilter { $Path -eq $registryPathWow6432Node } ` -Exactly -Times 1 -Scope Context } } diff --git a/Tests/Unit/MSFT_SqlDatabase.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabase.Tests.ps1 index 54293a4c6..14adbfbd3 100644 --- a/Tests/Unit/MSFT_SqlDatabase.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabase.Tests.ps1 @@ -15,24 +15,24 @@ if (Test-SkipContinuousIntegrationTask -Type 'Unit') return } +#region HEADER $script:dscModuleName = 'SqlServerDsc' $script:dscResourceName = 'MSFT_SqlDatabase' -#region HEADER - -# Unit Test Template Version: 1.2.0 +# Unit Test Template Version: 1.2.4 $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` - (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) { - & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) } -Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force $TestEnvironment = Initialize-TestEnvironment ` -DSCModuleName $script:dscModuleName ` -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` -TestType Unit #endregion HEADER @@ -143,7 +143,7 @@ try } #endregion - Describe "MSFT_SqlDatabase\Get-TargetResource" -Tag 'Get' { + Describe 'MSFT_SqlDatabase\Get-TargetResource' -Tag 'Get' { BeforeEach { Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable } @@ -165,17 +165,16 @@ try $result.ServerName | Should -Be $testParameters.ServerName $result.InstanceName | Should -Be $testParameters.InstanceName $result.Name | Should -Be $testParameters.Name - $result.Collation | Should -Be $testParameters.Collation + $result.Collation | Should -BeNullOrEmpty } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context } } Context 'When the system is in the desired state for a database' { - $testParameters = $mockDefaultParameters $testParameters += @{ Name = 'AdventureWorks' @@ -196,14 +195,14 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context } } Assert-VerifiableMock } - Describe "MSFT_SqlDatabase\Test-TargetResource" -Tag 'Test' { + Describe 'MSFT_SqlDatabase\Test-TargetResource' -Tag 'Test' { BeforeEach { Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable } @@ -234,7 +233,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context } } @@ -251,7 +250,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -281,7 +280,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context } } @@ -298,14 +297,14 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } Assert-VerifiableMock } - Describe "MSFT_SqlDatabase\Set-TargetResource" -Tag 'Set' { + Describe 'MSFT_SqlDatabase\Set-TargetResource' -Tag 'Set' { BeforeEach { Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable Mock -CommandName New-Object -MockWith $mockNewObjectDatabase -ParameterFilter { @@ -339,11 +338,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context } It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.Database' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.Database' } -Scope Context } @@ -364,7 +363,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -383,7 +382,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.Database' } -Scope It } @@ -413,7 +412,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -436,7 +435,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } diff --git a/Tests/Unit/MSFT_SqlDatabaseDefaultLocation.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseDefaultLocation.Tests.ps1 index 793bac7f5..4cd4c40a0 100644 --- a/Tests/Unit/MSFT_SqlDatabaseDefaultLocation.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseDefaultLocation.Tests.ps1 @@ -154,7 +154,7 @@ try $getTargetResourceResult.ServerName | Should -Be $mockDefaultParameters.ServerName $getTargetResourceResult.InstanceName | Should -Be $mockDefaultParameters.InstanceName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -197,7 +197,7 @@ try $testTargetResourceResult = Test-TargetResource @mockDefaultParameters -Type $Type -Path $Path $testTargetResourceResult | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -213,7 +213,7 @@ try $testTargetResourceResult = Test-TargetResource @mockDefaultParameters -Type $Type -Path $AlterPath $testTargetResourceResult | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } Context 'When the ProcessOnlyOnActiveNode parameter is passed' { @@ -310,8 +310,8 @@ try {Set-TargetResource @mockDefaultParameters @setTargetResourceParameters} | Should -Not -Throw $script:WasMethodAlterCalled | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - Assert-MockCalled Restart-SqlService -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 1 -Scope It } It 'Should throw when the path is invalid.' -TestCases $testCases { @@ -331,8 +331,8 @@ try {Set-TargetResource @mockDefaultParameters @setTargetResourceParameters} | Should -Throw $throwInvalidPath $script:WasMethodAlterCalled | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 0 -Scope It - Assert-MockCalled Restart-SqlService -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 0 -Scope It } } @@ -357,8 +357,8 @@ try {Set-TargetResource @mockDefaultParameters @setTargetResourceParameters} | Should -Throw $throwInvalidOperation - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It - Assert-MockCalled Restart-SqlService -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 0 -Scope It } } } diff --git a/Tests/Unit/MSFT_SqlDatabaseOwner.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseOwner.Tests.ps1 index 5392278b1..ec9c36182 100644 --- a/Tests/Unit/MSFT_SqlDatabaseOwner.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseOwner.Tests.ps1 @@ -126,7 +126,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -152,7 +152,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -198,7 +198,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -217,7 +217,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -244,7 +244,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -262,7 +262,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -280,7 +280,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -299,7 +299,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -319,7 +319,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } diff --git a/Tests/Unit/MSFT_SqlDatabasePermission.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabasePermission.Tests.ps1 index d4edcaef3..8a0a4aac7 100644 --- a/Tests/Unit/MSFT_SqlDatabasePermission.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabasePermission.Tests.ps1 @@ -261,7 +261,7 @@ try { Get-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -279,7 +279,7 @@ try { Get-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -298,7 +298,7 @@ try { Get-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -315,7 +315,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -324,7 +324,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -341,7 +341,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Not -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -350,7 +350,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -367,7 +367,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -376,7 +376,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -393,7 +393,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Not -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -402,7 +402,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -429,7 +429,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -448,7 +448,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -468,7 +468,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -485,7 +485,7 @@ try Test-TargetResource @testParameters | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -502,7 +502,7 @@ try Test-TargetResource @testParameters | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -519,7 +519,7 @@ try Test-TargetResource @testParameters | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -536,7 +536,7 @@ try Test-TargetResource @testParameters | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -571,7 +571,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -591,7 +591,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -613,7 +613,7 @@ try $script:mockMethodCreateLoginRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -730,7 +730,7 @@ try $script:mockMethodCreateLoginRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -751,7 +751,7 @@ try $script:mockMethodDenyRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the method Grant() (WithGrant) without throwing' { @@ -771,7 +771,7 @@ try $script:mockMethodDenyRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the method Deny() without throwing' { @@ -791,7 +791,7 @@ try $script:mockMethodDenyRan | Should -Be $true $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -813,7 +813,7 @@ try $script:mockMethodDenyRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the method Revoke() for permission state ''GrantWithGrant'' without throwing' { @@ -833,7 +833,7 @@ try $script:mockMethodDenyRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the method Revoke() for permission state ''Deny'' without throwing' { @@ -853,7 +853,7 @@ try $script:mockMethodDenyRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } diff --git a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 index 00cb72363..e3946e463 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRecoveryModel.Tests.ps1 @@ -115,7 +115,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -138,7 +138,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -161,7 +161,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -186,7 +186,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -203,7 +203,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -229,7 +229,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -246,7 +246,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -267,7 +267,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } diff --git a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 index 3c31cf20e..f8608995a 100644 --- a/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlDatabaseRole.Tests.ps1 @@ -300,7 +300,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -315,14 +315,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as null' { $result = Get-TargetResource @testParameters $result.Members | Should -BeNullOrEmpty - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -332,7 +332,7 @@ try $result.Database | Should -Be $testParameters.Database $result.Name | Should -Be $testParameters.Name - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -347,11 +347,11 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -370,7 +370,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -386,28 +386,28 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as True' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Be $testParameters.Members - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as string array' { $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -418,7 +418,7 @@ try $result.Name | Should -Be $testParameters.Name $result.Members | Should -Be $testParameters.Members - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -434,21 +434,21 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as False' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as string array' { $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -468,7 +468,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -484,28 +484,28 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as True' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -BeNullOrEmpty - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as string array' { $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -516,7 +516,7 @@ try $result.Name | Should -Be $testParameters.Name $result.MembersToInclude | Should -Be $testParameters.MembersToInclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -532,21 +532,21 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as False' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as string array' { $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -566,7 +566,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -583,14 +583,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as True' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -601,7 +601,7 @@ try $result.Name | Should -Be $testParameters.Name $result.MembersToExclude | Should -Be $testParameters.MembersToExclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -617,21 +617,21 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return MembersInDesiredState as False' { $result = Get-TargetResource @testParameters $result.MembersInDesiredState | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as string array' { $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -659,7 +659,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -677,7 +677,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -694,11 +694,11 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.DatabaseRole' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.DatabaseRole' } -Scope Context } @@ -720,11 +720,11 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.DatabaseRole' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.DatabaseRole' } -Scope Context } @@ -745,7 +745,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should not throw when calling both the AddMember and DropMember methods' { @@ -761,7 +761,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -780,7 +780,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -799,7 +799,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should not throw when calling the AddMember method' { @@ -814,7 +814,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should throw the correct error when calling the AddMember method' { @@ -832,7 +832,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -851,7 +851,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -868,7 +868,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should throw the correct error when calling the DropMember method' { @@ -886,7 +886,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -910,7 +910,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -926,7 +926,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -942,7 +942,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -958,7 +958,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -975,7 +975,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -996,7 +996,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -1013,7 +1013,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return True when the desired database role exists and the MembersToInclude contains members that already exist' { @@ -1028,7 +1028,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return False when the desired database role exists and the MembersToInclude contains members that are missing' { @@ -1043,7 +1043,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -1064,7 +1064,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -1081,7 +1081,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return True when the desired database role exists and the MembersToExclude contains members that do not yet exist' { @@ -1096,7 +1096,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return False when the desired database role exists and the MembersToExclude contains members that already exist' { @@ -1111,7 +1111,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } diff --git a/Tests/Unit/MSFT_SqlDatabaseUser.Tests.ps1 b/Tests/Unit/MSFT_SqlDatabaseUser.Tests.ps1 new file mode 100644 index 000000000..b4ffa0205 --- /dev/null +++ b/Tests/Unit/MSFT_SqlDatabaseUser.Tests.ps1 @@ -0,0 +1,1098 @@ +<# + .SYNOPSIS + Automated unit test for MSFT_SqlDatabaseUser DSC resource. + + .NOTES + To run this script locally, please make sure to first run the bootstrap + script. Read more at + https://github.com/PowerShell/SqlServerDsc/blob/dev/CONTRIBUTING.md#bootstrap-script-assert-testenvironment +#> + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + +$script:dscModuleName = 'SqlServerDsc' +$script:dscResourceName = 'MSFT_SqlDatabaseUser' + +#region HEADER + +# Unit Test Template Version: 1.2.0 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup +{ + Add-Type -Path (Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs') -ChildPath 'SMO.cs') +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:dscResourceName { + $mockName = 'DatabaseUser1' + $mockServerName = 'localhost' + $mockInstanceName = 'MSSQLSERVER' + $mockDatabaseName = 'TestDB' + $mockLoginName = 'CONTOSO\Login1' + $mockAsymmetricKeyName = 'AsymmetricKey1' + $mockCertificateName = 'Certificate1' + $mockLoginType = 'WindowsUser' + $mockUserType = 'Login' + $mockAuthenticationType = 'Windows' + + # Default parameters that are used for the It-blocks. + $mockDefaultParameters = @{ + Name = $mockName + InstanceName = $mockInstanceName + ServerName = $mockServerName + DatabaseName = $mockDatabaseName + Verbose = $true + } + + Describe 'MSFT_SqlDatabaseUser\Get-TargetResource' -Tag 'Get' { + Context 'When the system is in the desired state' { + BeforeAll { + # Scriptblock for mocked object for mocks of Connect-SQL. + $mockSqlServerObject = { + New-Object -TypeName Object | + Add-Member -MemberType ScriptProperty -Name 'Databases' -Value { + return @( + @{ + $mockDatabaseName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockDatabaseName -PassThru | + Add-Member -MemberType ScriptProperty -Name 'Users' -Value { + return @( + @{ + $mockName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AsymmetricKey' -Value $mockAsymmetricKeyName -PassThru | + Add-Member -MemberType NoteProperty -Name 'Certificate' -Value $mockCertificateName -PassThru | + Add-Member -MemberType NoteProperty -Name 'AuthenticationType' -Value $mockAuthenticationType -PassThru | + Add-Member -MemberType NoteProperty -Name 'LoginType' -Value $mockLoginType -PassThru | + Add-Member -MemberType NoteProperty -Name 'Login' -Value $mockLoginName -PassThru -Force + } + ) + } -PassThru -Force + } + ) + } -PassThru -Force + } + + Mock -CommandName Connect-SQL -MockWith $mockSqlServerObject + } + + AfterEach { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + } + + Context 'When the configuration is absent' { + BeforeAll { + $mockMissingName = 'MissingUser1' + + $getTargetResourceParameters = $mockDefaultParameters.Clone() + $getTargetResourceParameters['Name'] = $mockMissingName + } + + It 'Should return the state as absent' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.Ensure | Should -Be 'Absent' + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @getTargetResourceParameters + $result.ServerName | Should -Be $getTargetResourceParameters.ServerName + $result.InstanceName | Should -Be $getTargetResourceParameters.InstanceName + $result.DatabaseName | Should -Be $getTargetResourceParameters.DatabaseName + $result.Name | Should -Be $mockMissingName + } + + It 'Should return $null for the rest of the properties' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.LoginName | Should -BeNullOrEmpty + $getTargetResourceResult.AsymmetricKeyName | Should -BeNullOrEmpty + $getTargetResourceResult.CertificateName | Should -BeNullOrEmpty + $getTargetResourceResult.AuthenticationType | Should -BeNullOrEmpty + $getTargetResourceResult.LoginType | Should -BeNullOrEmpty + $getTargetResourceResult.UserType | Should -BeNullOrEmpty + } + } + + Context 'When the configuration is present' { + BeforeAll { + $getTargetResourceParameters = $mockDefaultParameters.Clone() + } + + It 'Should return the state as present' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.Ensure | Should -Be 'Present' + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @getTargetResourceParameters + $result.ServerName | Should -Be $getTargetResourceParameters.ServerName + $result.InstanceName | Should -Be $getTargetResourceParameters.InstanceName + $result.DatabaseName | Should -Be $getTargetResourceParameters.DatabaseName + $result.Name | Should -Be $getTargetResourceParameters.Name + } + + It 'Should return the correct values for the rest of the properties' { + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + $getTargetResourceResult.LoginName | Should -Be $mockLoginName + $getTargetResourceResult.AsymmetricKeyName | Should -Be $mockAsymmetricKeyName + $getTargetResourceResult.CertificateName | Should -Be $mockCertificateName + $getTargetResourceResult.AuthenticationType | Should -Be $mockAuthenticationType + $getTargetResourceResult.LoginType | Should -Be 'WindowsUser' + $getTargetResourceResult.UserType | Should -Be $mockUserType + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the database name does not exist' { + BeforeAll { + Mock -CommandName Connect-SQL -MockWith $mockSqlServerObject + + $mockMissingName = 'MissingUser1' + $mockMissingDatabaseName = 'MissingDatabase1' + + $getTargetResourceParameters = $mockDefaultParameters.Clone() + $getTargetResourceParameters['DatabaseName'] = $mockMissingDatabaseName + $getTargetResourceParameters['Name'] = $mockMissingName + } + + It 'Should throw the correct error' { + { + Get-TargetResource @getTargetResourceParameters + } | Should -Throw ($script:localizedData.DatabaseNotFound -f $mockMissingDatabaseName) + + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It + } + } + } + + Assert-VerifiableMock + } + + Describe 'MSFT_SqlDatabaseUser\Test-TargetResource' -Tag 'Test' { + Context 'When the system is in the desired state' { + Context 'When the configuration is absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $null + AsymmetricKeyName = $null + CertificateName = $null + UserType = $null + AuthenticationType = $null + LoginType = $null + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['Ensure'] = 'Absent' + } + + It 'Should return the state as $true' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration is present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $null + CertificateName = $null + UserType = $mockUserType + AuthenticationType = $mockAuthenticationType + LoginType = $mockLoginType + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['UserType'] = $mockUserType + $testTargetResourceParameters['LoginName'] = $mockLoginName + } + + It 'Should return the state as $true' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $true + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the configuration should be absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['Ensure'] = 'Absent' + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration should be present' { + Context 'When the property LoginName is not in desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + LoginName = 'OtherLogin1' + AsymmetricKeyName = $null + CertificateName = $null + UserType = 'Login' + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['LoginName'] = $mockLoginName + $testTargetResourceParameters['UserType'] = 'Login' + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the property AsymmetricKeyName is not in desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + LoginName = $null + AsymmetricKeyName = 'OtherAsymmetricKey1' + CertificateName = $null + UserType = 'AsymmetricKey' + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['AsymmetricKeyName'] = $mockAsymmetricKeyName + $testTargetResourceParameters['UserType'] = 'AsymmetricKey' + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the property CertificateName is not in desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + LoginName = $null + AsymmetricKeyName = $null + CertificateName = 'OtherCertificate1' + UserType = 'Certificate' + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['CertificateName'] = $mockCertificateName + $testTargetResourceParameters['UserType'] = 'Certificate' + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the property UserType is not in desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + LoginName = $null + AsymmetricKeyName = $null + CertificateName = 'OtherCertificate1' + UserType = 'Certificate' + } + } + + $testTargetResourceParameters = $mockDefaultParameters.Clone() + $testTargetResourceParameters['LoginName'] = $mockLoginName + $testTargetResourceParameters['UserType'] = 'Login' + } + + It 'Should return the state as $false' { + $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters + $testTargetResourceResult | Should -Be $false + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } + + Assert-VerifiableMock + } + + Describe 'MSFT_SqlDatabaseUser\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Invoke-Query + Mock -CommandName Assert-SqlLogin + Mock -CommandName Assert-DatabaseAsymmetricKey + Mock -CommandName Assert-DatabaseCertificate + } + + Context 'When the system is in the desired state' { + Context 'When the configuration is absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $null + AsymmetricKeyName = $null + CertificateName = $null + UserType = $null + AuthenticationType = $null + LoginType = $null + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['Ensure'] = 'Absent' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 0 -Scope It + } + } + + Context 'When the configuration is present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $null + CertificateName = $null + UserType = $mockUserType + AuthenticationType = $mockAuthenticationType + LoginType = $mockLoginType + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['UserType'] = $mockUserType + $setTargetResourceParameters['LoginName'] = $mockLoginName + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 0 -Scope It + } + } + } + + Context 'When the system is not in the desired state' { + Context 'When the configuration should be absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $null + CertificateName = $null + UserType = $mockUserType + AuthenticationType = $mockAuthenticationType + LoginType = $mockLoginType + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['Ensure'] = 'Absent' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('DROP USER [{0}];' -f $mockName) + } -Exactly -Times 1 -Scope It + } + + Context 'When trying to drop a database user but Invoke-Query fails' { + BeforeAll { + Mock -CommandName Invoke-Query -MockWith { + throw + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['Ensure'] = 'Absent' + } + + It 'Should throw the correct error' { + { + Set-TargetResource @setTargetResourceParameters + } | Should -Throw ($script:localizedData.FailedDropDatabaseUser -f $mockName, $MockDatabaseName) + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the configuration should be present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Absent' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $null + AsymmetricKeyName = $null + CertificateName = $null + UserType = $null + AuthenticationType = $null + LoginType = $null + } + } + } + + Context 'When creating a database user with a login' { + BeforeAll { + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['LoginName'] = $mockLoginName + $setTargetResourceParameters['UserType'] = 'Login' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR LOGIN [{1}];' -f $mockName, $mockLoginName) + } -Exactly -Times 1 -Scope It + } + } + + Context 'When creating a database user without a login' { + BeforeAll { + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['UserType'] = 'NoLogin' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] WITHOUT LOGIN;' -f $mockName) + } -Exactly -Times 1 -Scope It + } + } + + Context 'When creating a database user mapped to a certificate' { + BeforeAll { + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['CertificateName'] = $mockCertificateName + $setTargetResourceParameters['UserType'] = 'Certificate' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR CERTIFICATE [{1}];' -f $mockName, $mockCertificateName) + } -Exactly -Times 1 -Scope It + } + } + + Context 'When creating a database user mapped to an asymmetric key' { + BeforeAll { + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['AsymmetricKeyName'] = $mockAsymmetricKeyName + $setTargetResourceParameters['UserType'] = 'AsymmetricKey' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR ASYMMETRIC KEY [{1}];' -f $mockName, $mockAsymmetricKeyName) + } -Exactly -Times 1 -Scope It + } + } + + Context 'When trying to create a database user but Invoke-Query fails' { + BeforeAll { + Mock -CommandName Invoke-Query -MockWith { + throw + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['UserType'] = 'NoLogin' + } + + It 'Should throw the correct error' { + { + Set-TargetResource @setTargetResourceParameters + } | Should -Throw ($script:localizedData.FailedCreateDatabaseUser -f $mockName, $MockDatabaseName, 'NoLogin') + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 1 -Scope It + } + } + } + + Context 'When properties are not in desired state' { + Context 'When the database user has the wrong login name' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $null + CertificateName = $null + UserType = $mockUserType + AuthenticationType = $mockAuthenticationType + LoginType = 'WindowsUser' + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['LoginName'] = 'OtherLogin1' + $setTargetResourceParameters['UserType'] = 'Login' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('ALTER USER [{0}] WITH NAME = [{1}], LOGIN = [{2}];' -f $mockName, $mockName, 'OtherLogin1') + } -Exactly -Times 1 -Scope It + } + + Context 'When trying to alter the login name but Invoke-Query fails' { + BeforeAll { + Mock -CommandName Invoke-Query -MockWith { + throw + } + } + + It 'Should throw the correct error' { + { + Set-TargetResource @setTargetResourceParameters + } | Should -Throw ($script:localizedData.FailedUpdateDatabaseUser -f $mockName, $MockDatabaseName, 'Login') + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the database user has the wrong asymmetric key name' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $mockAsymmetricKeyName + CertificateName = $null + UserType = 'AsymmetricKey' + AuthenticationType = $mockAuthenticationType + LoginType = 'AsymmetricKey' + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['AsymmetricKeyName'] = 'OtherAsymmetricKey1' + $setTargetResourceParameters['UserType'] = 'AsymmetricKey' + $setTargetResourceParameters['Force'] = $true + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('DROP USER [{0}];' -f $mockName) + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR ASYMMETRIC KEY [{1}];' -f $mockName, 'OtherAsymmetricKey1') + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the database user has the wrong certificate name' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $mockAsymmetricKeyName + CertificateName = $null + UserType = 'Certificate' + AuthenticationType = $mockAuthenticationType + LoginType = 'Certificate' + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['CertificateName'] = 'OtherCertificate1' + $setTargetResourceParameters['UserType'] = 'Certificate' + $setTargetResourceParameters['Force'] = $true + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('DROP USER [{0}];' -f $mockName) + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR CERTIFICATE [{1}];' -f $mockName, 'OtherCertificate1') + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the database user has the wrong certificate name' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $mockAsymmetricKeyName + CertificateName = $null + UserType = 'Certificate' + AuthenticationType = $mockAuthenticationType + LoginType = 'Certificate' + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['LoginName'] = 'OtherLogin1' + $setTargetResourceParameters['UserType'] = 'Login' + $setTargetResourceParameters['Force'] = $true + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('DROP USER [{0}];' -f $mockName) + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-Query -ParameterFilter { + $Query -eq ('CREATE USER [{0}] FOR LOGIN [{1}];' -f $mockName, 'OtherLogin1') + } -Exactly -Times 1 -Scope It + } + } + + Context 'When the configuration has not opt-in to re-create a database user' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + Ensure = 'Present' + Name = $mockName + ServerName = $mockServerName + InstanceName = $mockInstanceName + DatabaseName = $mockDatabaseName + LoginName = $mockLoginName + AsymmetricKeyName = $mockAsymmetricKeyName + CertificateName = $null + UserType = 'Certificate' + AuthenticationType = $mockAuthenticationType + LoginType = 'Certificate' + } + } + + $setTargetResourceParameters = $mockDefaultParameters.Clone() + $setTargetResourceParameters['LoginName'] = 'OtherLogin1' + $setTargetResourceParameters['UserType'] = 'Login' + } + + It 'Should not throw and should call the correct mocks' { + { Set-TargetResource @setTargetResourceParameters } | Should -Throw $script:localizedData.ForceNotEnabled + + Assert-MockCalled -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Query -Exactly -Times 0 -Scope It + } + } + } + } + + Assert-VerifiableMock + } + + Describe 'Assert-Parameters' -Tag 'Helper' { + BeforeAll { + $mockUserType = 'NoLogin' + } + + Context 'When parameter LoginName is provided, but with the wrong user type' { + It 'Should throw the correct error' { + { + Assert-Parameters -LoginName 'AnyValue' -UserType $mockUserType + } | Should -Throw ($script:localizedData.LoginNameProvidedWithWrongUserType -f $mockUserType) + } + } + + Context 'When parameter CertificateName is provided, but with the wrong user type' { + It 'Should throw the correct error' { + { + Assert-Parameters -CertificateName 'AnyValue' -UserType $mockUserType + } | Should -Throw ($script:localizedData.CertificateNameProvidedWithWrongUserType -f $mockUserType) + } + } + + Context 'When parameter AsymmetricKeyName is provided, but with the wrong user type' { + It 'Should throw the correct error' { + { + Assert-Parameters -AsymmetricKeyName 'AnyValue' -UserType $mockUserType + } | Should -Throw ($script:localizedData.AsymmetricKeyNameProvidedWithWrongUserType -f $mockUserType) + } + } + + Context 'When parameter LoginName is not provided when providing the user type ''Login''' { + It 'Should throw the correct error' { + { + Assert-Parameters -UserType 'Login' + } | Should -Throw ($script:localizedData.LoginUserTypeWithoutLoginName -f 'Login') + } + } + + Context 'When parameter AsymmetricKeyName is not provide when providing the user type ''AsymmetricKey''' { + It 'Should throw the correct error' { + { + Assert-Parameters -UserType 'AsymmetricKey' + } | Should -Throw ($script:localizedData.AsymmetricKeyUserTypeWithoutAsymmetricKeyName -f 'AsymmetricKey') + } + } + + Context 'When parameter CertificateName is not provide when providing the user type ''Certificate''' { + It 'Should throw the correct error' { + { + Assert-Parameters -UserType 'Certificate' + } | Should -Throw ($script:localizedData.CertificateUserTypeWithoutCertificateName -f 'Certificate') + } + } + } + + Describe 'ConvertTo-UserType' -Tag 'Helper' { + Context 'When converting to a user type' { + BeforeAll { + $testCases = @( + @{ + AuthenticationType = 'Windows' + LoginType = 'WindowsUser' + ExpectedResult = 'Login' + } + @{ + AuthenticationType = 'Windows' + LoginType = 'WindowsGroup' + ExpectedResult = 'Login' + } + @{ + AuthenticationType = 'Instance' + LoginType = 'SqlLogin' + ExpectedResult = 'Login' + } + @{ + AuthenticationType = 'None' + LoginType = 'SqlLogin' + ExpectedResult = 'NoLogin' + } + @{ + AuthenticationType = 'None' + LoginType = 'AsymmetricKey' + ExpectedResult = 'AsymmetricKey' + } + @{ + AuthenticationType = 'None' + LoginType = 'Certificate' + ExpectedResult = 'Certificate' + } + ) + } + + It 'Should return the correct value when converting authentication type and login type ' -TestCases $testCases { + param + ( + [Parameter()] + [System.String] + $AuthenticationType, + + [Parameter()] + [System.String] + $LoginType, + + [Parameter()] + [System.String] + $ExpectedResult + ) + + $convertToUserTypeResult = ConvertTo-UserType -AuthenticationType $AuthenticationType -LoginType $LoginType + $convertToUserTypeResult | Should -Be $ExpectedResult + } + + Context 'When calling with an unsupported authentication type' { + BeforeAll { + $mockUnsupportedValue = 'UnsupportedValue' + $mockLoginType = 'SqlLogin' + } + + It 'Should throw the correct error' { + { + ConvertTo-UserType -AuthenticationType $mockUnsupportedValue -LoginType $mockLoginType + } | Should -Throw ($script:localizedData.UnknownAuthenticationType -f $mockUnsupportedValue, $mockLoginType) + } + } + } + } + + Describe 'Assert-SqlLogin' -Tag 'Helper' { + BeforeAll { + $mockSqlServerObject = { + New-Object -TypeName Object | + Add-Member -MemberType ScriptProperty -Name 'Logins' -Value { + return @( + @{ + $mockLoginName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockLoginName -PassThru -Force + } + ) + } -PassThru -Force + } + + Mock -CommandName Connect-SQL -MockWith $mockSqlServerObject + } + + Context 'When the SQL login exist' { + BeforeAll { + $assertSqlLoginParameters = $mockDefaultParameters.Clone() + $assertSqlLoginParameters['LoginName'] = $mockLoginName + } + + It 'Should not throw any error' { + { Assert-SqlLogin @assertSqlLoginParameters } | Should -Not -Throw + } + } + + Context 'When the SQL login does not exist' { + BeforeAll { + $assertSqlLoginParameters = $mockDefaultParameters.Clone() + $assertSqlLoginParameters['LoginName'] = 'AnyValue' + } + + It 'Should throw the correct error' { + { + Assert-SqlLogin @assertSqlLoginParameters + } | Should -Throw ($script:localizedData.SqlLoginNotFound -f 'AnyValue') + } + } + } + + Describe 'Assert-DatabaseCertificate' -Tag 'Helper' { + BeforeAll { + $mockSqlServerObject = { + New-Object -TypeName Object | + Add-Member -MemberType ScriptProperty -Name 'Databases' -Value { + return @( + @{ + $mockDatabaseName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockDatabaseName -PassThru | + Add-Member -MemberType ScriptProperty -Name 'Certificates' -Value { + return @( + @{ + $mockCertificateName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockCertificateName -PassThru -Force + } + ) + } -PassThru -Force + } + ) + } -PassThru -Force + } + + Mock -CommandName Connect-SQL -MockWith $mockSqlServerObject + } + + Context 'When the certificate exist in the database' { + BeforeAll { + $assertDatabaseCertificateParameters = $mockDefaultParameters.Clone() + $assertDatabaseCertificateParameters['CertificateName'] = $mockCertificateName + } + + It 'Should not throw any error' { + { Assert-DatabaseCertificate @assertDatabaseCertificateParameters } | Should -Not -Throw + } + } + + Context 'When the certificate does not exist in the database' { + BeforeAll { + $assertDatabaseCertificateParameters = $mockDefaultParameters.Clone() + $assertDatabaseCertificateParameters['CertificateName'] = 'AnyValue' + } + + It 'Should throw the correct error' { + { + Assert-DatabaseCertificate @assertDatabaseCertificateParameters + } | Should -Throw ($script:localizedData.CertificateNotFound -f 'AnyValue', $mockDatabaseName) + } + } + } + + Describe 'Assert-DatabaseAsymmetricKey' -Tag 'Helper' { + BeforeAll { + $mockSqlServerObject = { + New-Object -TypeName Object | + Add-Member -MemberType ScriptProperty -Name 'Databases' -Value { + return @( + @{ + $mockDatabaseName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockDatabaseName -PassThru | + Add-Member -MemberType ScriptProperty -Name 'AsymmetricKeys' -Value { + return @( + @{ + $mockAsymmetricKeyName = New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name 'Name' -Value $mockAsymmetricKeyName -PassThru -Force + } + ) + } -PassThru -Force + } + ) + } -PassThru -Force + } + + Mock -CommandName Connect-SQL -MockWith $mockSqlServerObject + } + + Context 'When the asymmetric key exist in the database' { + BeforeAll { + $assertDatabaseAsymmetricKeyParameters = $mockDefaultParameters.Clone() + $assertDatabaseAsymmetricKeyParameters['AsymmetricKeyName'] = $mockAsymmetricKeyName + } + + It 'Should not throw any error' { + { Assert-DatabaseAsymmetricKey @assertDatabaseAsymmetricKeyParameters } | Should -Not -Throw + } + } + + Context 'When the asymmetric key does not exist in the database' { + BeforeAll { + $assertDatabaseAsymmetricKeyParameters = $mockDefaultParameters.Clone() + $assertDatabaseAsymmetricKeyParameters['AsymmetricKeyName'] = 'AnyValue' + } + + It 'Should throw the correct error' { + { + Assert-DatabaseAsymmetricKey @assertDatabaseAsymmetricKeyParameters + } | Should -Throw ($script:localizedData.AsymmetryKeyNotFound -f 'AnyValue', $mockDatabaseName) + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} diff --git a/Tests/Unit/MSFT_SqlServerDatabaseMail.Tests.ps1 b/Tests/Unit/MSFT_SqlServerDatabaseMail.Tests.ps1 index 5d1c9eb26..48ebb01b0 100644 --- a/Tests/Unit/MSFT_SqlServerDatabaseMail.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerDatabaseMail.Tests.ps1 @@ -192,7 +192,7 @@ try } -PassThru -Force } - Describe "MSFT_SqlServerDatabaseMail\Get-TargetResource" -Tag 'Get' { + Describe 'MSFT_SqlServerDatabaseMail\Get-TargetResource' -Tag 'Get' { BeforeAll { $mockDynamicDatabaseMailEnabledRunValue = $mockDatabaseMailEnabledConfigValue $mockDynamicLoggingLevelValue = $mockLoggingLevelExtendedValue @@ -217,7 +217,7 @@ try $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -225,7 +225,7 @@ try $result.ServerName | Should -Be $getTargetResourceParameters.ServerName $result.InstanceName | Should -Be $getTargetResourceParameters.InstanceName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return $null for the rest of the properties' { @@ -240,7 +240,7 @@ try $getTargetResourceResult.Description | Should -BeNullOrEmpty $getTargetResourceResult.TcpPort | Should -BeNullOrEmpty - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -249,7 +249,7 @@ try $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters $getTargetResourceResult.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -257,7 +257,7 @@ try $result.ServerName | Should -Be $getTargetResourceParameters.ServerName $result.InstanceName | Should -Be $getTargetResourceParameters.InstanceName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the correct values for the rest of the properties' { @@ -272,7 +272,7 @@ try $getTargetResourceResult.Description | Should -Be $mockDescription $getTargetResourceResult.TcpPort | Should -Be $mockTcpPort - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -336,7 +336,7 @@ try Assert-VerifiableMock } - Describe "MSFT_SqlServerDatabaseMail\Test-TargetResource" -Tag 'Test' { + Describe 'MSFT_SqlServerDatabaseMail\Test-TargetResource' -Tag 'Test' { BeforeAll { $mockDynamicDatabaseMailEnabledRunValue = $mockDatabaseMailEnabledConfigValue $mockDynamicLoggingLevelValue = $mockLoggingLevelExtendedValue @@ -362,7 +362,7 @@ try $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -379,7 +379,7 @@ try $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -394,7 +394,7 @@ try $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -486,7 +486,7 @@ try $testTargetResourceResult = Test-TargetResource @testTargetResourceParameters $testTargetResourceResult | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -494,7 +494,7 @@ try Assert-VerifiableMock } - Describe "MSFT_SqlServerDatabaseMail\Set-TargetResource" -Tag 'Set' { + Describe 'MSFT_SqlServerDatabaseMail\Set-TargetResource' -Tag 'Set' { BeforeAll { $mockDynamicDatabaseMailEnabledRunValue = $mockDatabaseMailEnabledConfigValue $mockDynamicLoggingLevelValue = $mockLoggingLevelExtendedValue @@ -557,7 +557,7 @@ try $script:MailProfileDropMethodCallCount | Should -Be 0 $script:MailAccountDropMethodCallCount | Should -Be 0 - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -585,7 +585,7 @@ try $script:MailProfileDropMethodCallCount | Should -Be 0 $script:MailAccountDropMethodCallCount | Should -Be 0 - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -613,7 +613,7 @@ try Set-TargetResource @setTargetResourceParameters } | Should -Throw $script:localizedData.DatabaseMailDisabled - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -632,7 +632,7 @@ try $script:MailServerAlterMethodCallCount | Should -Be 1 $script:MailAccountAlterMethodCallCount | Should -Be 0 - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -787,7 +787,7 @@ try $script:LoggingLevelAlterMethodCallCount | Should -Be 0 } - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } diff --git a/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 b/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 index 3cbec6c3e..c40033c9e 100644 --- a/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerEndpoint.Tests.ps1 @@ -194,7 +194,7 @@ try It 'Should call the mock function Connect-SQL' { Get-TargetResource @testParameters | Out-Null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -219,7 +219,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock return the endpoint with wrong endpoint type @@ -265,7 +265,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when wanted desired state is to be Present (setting all parameters)' { @@ -277,7 +277,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock do return the correct endpoint @@ -289,7 +289,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock do return the correct endpoint, but does not return the correct endpoint listener port @@ -304,7 +304,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -324,7 +324,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -344,7 +344,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -360,7 +360,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock does not return the correct endpoint @@ -372,7 +372,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -413,7 +413,7 @@ try $script:mockMethodAlterRan | Should -Be $false $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Set all method call tests variables to $false @@ -443,7 +443,7 @@ try $script:mockMethodAlterRan | Should -Be $false $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Set all method call tests variables to $false @@ -470,7 +470,7 @@ try $script:mockMethodAlterRan | Should -Be $false $script:mockMethodDropRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Set all method call tests variables to $false @@ -502,7 +502,7 @@ try $script:mockMethodAlterRan | Should -Be $true $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Set all method call tests variables to $false @@ -534,7 +534,7 @@ try $script:mockMethodAlterRan | Should -Be $true $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Set all method call tests variables to $false @@ -566,7 +566,7 @@ try $script:mockMethodAlterRan | Should -Be $true $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock does not return the correct endpoint @@ -650,7 +650,7 @@ try $script:mockMethodAlterRan | Should -Be $false $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Make sure the mock does not return the correct endpoint @@ -677,7 +677,7 @@ try $script:mockMethodAlterRan | Should -Be $false $script:mockMethodDropRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } diff --git a/Tests/Unit/MSFT_SqlServerEndpointPermission.Tests.ps1 b/Tests/Unit/MSFT_SqlServerEndpointPermission.Tests.ps1 index e07e4ac21..192b9adc3 100644 --- a/Tests/Unit/MSFT_SqlServerEndpointPermission.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerEndpointPermission.Tests.ps1 @@ -140,7 +140,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } $mockDynamicEndpointName = 'UnknownEndPoint' @@ -149,7 +149,7 @@ try It 'Should throw the correct error message' { { Get-TargetResource @testParameters } | Should -Throw ($script:localizedData.UnexpectedErrorFromGet -f $testParameters.Name) - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -179,7 +179,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -203,7 +203,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } $mockDynamicPrincipal = $mockPrincipal @@ -215,7 +215,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -229,7 +229,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } $mockDynamicPrincipal = $mockOtherPrincipal @@ -241,7 +241,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -268,7 +268,7 @@ try $script:mockMethodGrantRan | Should -Be $true $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It } $mockDynamicPrincipal = $mockPrincipal @@ -283,7 +283,7 @@ try $script:mockMethodGrantRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It } $mockDynamicEndpointName = 'UnknownEndPoint' @@ -298,7 +298,7 @@ try { Set-TargetResource @testParameters } | Should -Throw ($script:localizedData.EndpointNotFound -f $testParameters.Name) - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -318,7 +318,7 @@ try $script:mockMethodGrantRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } $mockDynamicPrincipal = $mockOtherPrincipal @@ -333,7 +333,7 @@ try $script:mockMethodGrantRan | Should -Be $false $script:mockMethodRevokeRan | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } diff --git a/Tests/Unit/MSFT_SqlServerEndpointState.Tests.ps1 b/Tests/Unit/MSFT_SqlServerEndpointState.Tests.ps1 index cb006d4e9..50c541df0 100644 --- a/Tests/Unit/MSFT_SqlServerEndpointState.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerEndpointState.Tests.ps1 @@ -114,7 +114,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -135,7 +135,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -145,7 +145,7 @@ try It 'Should throw the correct error message' { { Get-TargetResource @testParameters } | Should -Throw ($script:localizedData.EndpointErrorVerifyExist -f $testParameters.Name) - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -170,7 +170,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -191,7 +191,7 @@ try It 'Should call the mock function Connect-SQL' { $result = Get-TargetResource @testParameters - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -216,7 +216,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -229,7 +229,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } } @@ -244,7 +244,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -257,7 +257,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -269,7 +269,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $script:localizedData.UnexpectedErrorFromGet - Assert-MockCalled Connect-SQL -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 0 -Scope It } } } @@ -294,7 +294,7 @@ try Set-TargetResource @testParameters - Assert-MockCalled Set-SqlHADREndpoint -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-SqlHADREndpoint -Exactly -Times 1 -Scope It } } @@ -306,7 +306,7 @@ try Set-TargetResource @testParameters - Assert-MockCalled Set-SqlHADREndpoint -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Set-SqlHADREndpoint -Exactly -Times 1 -Scope It } } } @@ -320,7 +320,7 @@ try Set-TargetResource @testParameters - Assert-MockCalled Set-SqlHADREndpoint -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlHADREndpoint -Exactly -Times 0 -Scope It } } @@ -332,7 +332,7 @@ try Set-TargetResource @testParameters - Assert-MockCalled Set-SqlHADREndpoint -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Set-SqlHADREndpoint -Exactly -Times 0 -Scope It } } @@ -344,7 +344,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $script:localizedData.UnexpectedErrorFromGet - Assert-MockCalled Connect-SQL -Exactly -Times 0 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 0 -Scope It } } } diff --git a/Tests/Unit/MSFT_SqlServerMaxDop.Tests.ps1 b/Tests/Unit/MSFT_SqlServerMaxDop.Tests.ps1 index fa416db03..65a62ff41 100644 --- a/Tests/Unit/MSFT_SqlServerMaxDop.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerMaxDop.Tests.ps1 @@ -135,11 +135,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Test-ActiveNode' { - Assert-MockCalled Test-ActiveNode -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Test-ActiveNode -Exactly -Times 1 -Scope Context } } @@ -177,11 +177,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should not call the mock function Get-CimInstance' { - Assert-MockCalled Get-CimInstance -Exactly -Times 0 -Scope Context + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 0 -Scope Context } } @@ -200,11 +200,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should not call the mock function Get-CimInstance' { - Assert-MockCalled Get-CimInstance -Exactly -Times 0 -Scope Context + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 0 -Scope Context } } @@ -222,11 +222,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { - Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -ParameterFilter { $ClassName -eq 'Win32_Processor' } -Scope Context } @@ -246,11 +246,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { - Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -ParameterFilter { $ClassName -eq 'Win32_Processor' } -Scope Context } @@ -270,11 +270,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { - Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -ParameterFilter { $ClassName -eq 'Win32_Processor' } -Scope Context } @@ -295,11 +295,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { - Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -ParameterFilter { $ClassName -eq 'Win32_Processor' } -Scope Context } @@ -317,7 +317,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -342,7 +342,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -360,7 +360,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -376,7 +376,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -422,7 +422,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -440,7 +440,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -460,7 +460,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -481,11 +481,11 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } It 'Should call the mock function Get-CimInstance with ClassName equal to Win32_Processor' { - Assert-MockCalled Get-CimInstance -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -ParameterFilter { $ClassName -eq 'Win32_Processor' } -Scope Context } @@ -506,7 +506,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } diff --git a/Tests/Unit/MSFT_SqlServerPermission.Tests.ps1 b/Tests/Unit/MSFT_SqlServerPermission.Tests.ps1 index a27e484ec..087606534 100644 --- a/Tests/Unit/MSFT_SqlServerPermission.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerPermission.Tests.ps1 @@ -108,7 +108,7 @@ try It 'Should call the mock function Connect-SQL' { Get-TargetResource @testParameters | Out-Null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -138,7 +138,7 @@ try It 'Should call the mock function Connect-SQL' { Get-TargetResource @testParameters | Out-Null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -188,7 +188,7 @@ try It 'Should call the mock function Connect-SQL' { Get-TargetResource @testParameters | Out-Null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -220,7 +220,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return that desired state is absent when wanted desired state is to be Absent' { @@ -232,7 +232,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -245,7 +245,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return that desired state is present when wanted desired state is to be Absent' { @@ -256,7 +256,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -287,7 +287,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It } It 'Should not throw error when desired state is to be Absent' { @@ -297,7 +297,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope It } } @@ -309,7 +309,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should not throw error when desired state is to be Absent' { @@ -319,7 +319,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } Context 'When the Set-TargetResource throws an error' { diff --git a/Tests/Unit/MSFT_SqlServerRole.Tests.ps1 b/Tests/Unit/MSFT_SqlServerRole.Tests.ps1 index 968be05e1..660924e14 100644 --- a/Tests/Unit/MSFT_SqlServerRole.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerRole.Tests.ps1 @@ -191,14 +191,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as null' { $result = Get-TargetResource @testParameters $result.Members | Should -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -207,7 +207,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -221,14 +221,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Not -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Regression test for issue #790 @@ -236,7 +236,7 @@ try $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -245,7 +245,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -264,7 +264,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -279,14 +279,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Be $testParameters.Members - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Regression test for issue #790 @@ -294,7 +294,7 @@ try $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -303,7 +303,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -318,14 +318,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Regression test for issue #790 @@ -333,7 +333,7 @@ try $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -343,7 +343,7 @@ try $result.ServerRoleName | Should -Be $testParameters.ServerRoleName $result.MembersToInclude | Should -Be $testParameters.MembersToInclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -358,7 +358,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Present' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -368,7 +368,7 @@ try $result.ServerRoleName | Should -Be $testParameters.ServerRoleName $result.MembersToExclude | Should -Be $testParameters.MembersToExclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -387,7 +387,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -407,7 +407,7 @@ try } It 'Should call the mock function Connect-SQL' { - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope Context + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context } } @@ -421,14 +421,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as null' { $result = Get-TargetResource @testParameters $result.Members | Should -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -437,7 +437,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -452,14 +452,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Regression test for issue #790 @@ -467,7 +467,7 @@ try $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -476,7 +476,7 @@ try $result.InstanceName | Should -Be $testParameters.InstanceName $result.ServerRoleName | Should -Be $testParameters.ServerRoleName - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -491,14 +491,14 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the members as not null' { $result = Get-TargetResource @testParameters $result.Members | Should -Not -Be $null - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } # Regression test for issue #790 @@ -506,7 +506,7 @@ try $result = Get-TargetResource @testParameters ($result.Members -is [System.String[]]) | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -516,7 +516,7 @@ try $result.ServerRoleName | Should -Be $testParameters.ServerRoleName $result.MembersToInclude | Should -Be $testParameters.MembersToInclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -531,7 +531,7 @@ try $result = Get-TargetResource @testParameters $result.Ensure | Should -Be 'Absent' - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should return the same values as passed as parameters' { @@ -541,7 +541,7 @@ try $result.ServerRoleName | Should -Be $testParameters.ServerRoleName $result.MembersToExclude | Should -Be $testParameters.MembersToExclude - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -564,7 +564,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -579,7 +579,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -594,7 +594,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -610,7 +610,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -628,7 +628,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -644,7 +644,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -660,7 +660,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -678,7 +678,7 @@ try { Test-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -694,7 +694,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $true - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -710,7 +710,7 @@ try $result = Test-TargetResource @testParameters $result | Should -Be $false - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -737,7 +737,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -755,7 +755,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -771,11 +771,11 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.ServerRole' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.ServerRole' } -Scope Context } @@ -797,11 +797,11 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } It 'Should call the mock function New-Object with TypeName equal to Microsoft.SqlServer.Management.Smo.ServerRole' { - Assert-MockCalled New-Object -Exactly -Times 1 -ParameterFilter { + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -ParameterFilter { $TypeName -eq 'Microsoft.SqlServer.Management.Smo.ServerRole' } -Scope Context } @@ -821,7 +821,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -839,7 +839,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -856,7 +856,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -877,7 +877,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -897,7 +897,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -914,7 +914,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -935,7 +935,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -956,7 +956,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -976,7 +976,7 @@ try { Set-TargetResource @testParameters } | Should -Throw $errorMessage - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } @@ -995,7 +995,7 @@ try { Set-TargetResource @testParameters } | Should -Not -Throw - Assert-MockCalled Connect-SQL -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope It } } diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index e8357f3ff..9e3e914b9 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -353,7 +353,7 @@ InModuleScope 'SqlServerDsc.Common' { $result = Get-RegistryPropertyValue -Path $mockWrongRegistryPath -Name $mockPropertyName $result | Should -BeNullOrEmpty - Assert-MockCalled Get-ItemProperty -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ItemProperty -Exactly -Times 1 -Scope It } } @@ -368,7 +368,7 @@ InModuleScope 'SqlServerDsc.Common' { $result = Get-RegistryPropertyValue -Path $mockWrongRegistryPath -Name $mockPropertyName $result | Should -BeNullOrEmpty - Assert-MockCalled Get-ItemProperty -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ItemProperty -Exactly -Times 1 -Scope It } } @@ -394,7 +394,7 @@ InModuleScope 'SqlServerDsc.Common' { $result = Get-RegistryPropertyValue -Path $mockCorrectRegistryPath -Name $mockPropertyName $result | Should -Be $mockPropertyValue - Assert-MockCalled Get-ItemProperty -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Get-ItemProperty -Exactly -Times 1 -Scope It } } } From a6c7ef8bed4a234d25897a73dd46f7d169ddbb68 Mon Sep 17 00:00:00 2001 From: Wayne Teutenberg Date: Wed, 17 Jul 2019 19:47:02 +1200 Subject: [PATCH 07/13] Connect-SQL: Remove domain from username (#1400) - Changes to helper function Connect-SQL - When impersonating WindowsUser credential use the NetworkCredential UserName. - Added addtional verbose logging. --- CHANGELOG.md | 3 ++ .../SqlServerDsc.Common.psm1 | 20 ++++--- .../en-US/SqlServerDsc.Common.strings.psd1 | 3 +- .../sv-SE/SqlServerDsc.Common.strings.psd1 | 3 +- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 54 ++++++++++--------- 5 files changed, 49 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19b3953ec..da6f32805 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - Can pipe Connect-SQL | Invoke-Query. - Added default values to Invoke-Query. - Minor style fixes in unit tests. + - Changes to helper function Connect-SQL + - When impersonating WindowsUser credential use the NetworkCredential UserName. + - Added addtional verbose logging. - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). - Changes to SqlSetup diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index df398371e..8d9b609d9 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -988,7 +988,6 @@ function Connect-SQL $sqlServerObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' $sqlConnectionContext = $sqlServerObject.ConnectionContext - $sqlConnectionContext.ServerInstance = $databaseEngineInstance $sqlConnectionContext.StatementTimeout = $StatementTimeout $sqlConnectionContext.ApplicationName = 'SqlServerDsc' @@ -1000,24 +999,33 @@ function Connect-SQL string since this is using Integrated Security=true (SSPI). #> $connectUserName = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name + + Write-Verbose -Message ( + $script:localizedData.ConnectingUsingIntegrated -f $connectUsername + ) -Verbose } else { if ($SetupCredential) { - $connectUsername = $SetupCredential.UserName + $connectUserName = $SetupCredential.GetNetworkCredential().UserName + + Write-Verbose -Message ( + $script:localizedData.ConnectingUsingImpersonation -f $connectUsername, $LoginType + ) -Verbose if ($LoginType -eq 'SqlLogin') { $sqlConnectionContext.LoginSecure = $false - $sqlConnectionContext.Login = $connectUsername + $sqlConnectionContext.Login = $connectUserName $sqlConnectionContext.SecurePassword = $SetupCredential.Password } if ($LoginType -eq 'WindowsUser') { + $sqlConnectionContext.LoginSecure = $true $sqlConnectionContext.ConnectAsUser = $true - $sqlConnectionContext.ConnectAsUserName = $connectUsername + $sqlConnectionContext.ConnectAsUserName = $connectUserName $sqlConnectionContext.ConnectAsUserPassword = $SetupCredential.GetNetworkCredential().Password } } @@ -1028,10 +1036,6 @@ function Connect-SQL } } - Write-Verbose -Message ( - $script:localizedData.ConnectingUsingCredentials -f $connectUsername, $LoginType - ) -Verbose - try { $sqlConnectionContext.Connect() diff --git a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 index 2d30b55a8..c60d79c3d 100644 --- a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 @@ -54,6 +54,7 @@ ConvertFrom-StringData @' ClusterLoginMissingPermissions = The account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0051) ClusterLoginMissingRecommendedPermissions = The recommended account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0052) ClusterLoginPermissionsPresent = The cluster login '{0}' has the required permissions. (SQLCOMMON0053) - ConnectingUsingCredentials = Connecting using the credential '{0}' and the login type '{1}'. (SQLCOMMON0054) + ConnectingUsingIntegrated = Connecting as current user '{0}' using integrated security. (SQLCOMMON0054) CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) + ConnectingUsingImpersonation = Impersonate credential '{0}' with login type '{1}'. (SQLCOMMON0056) '@ diff --git a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 index 32633eb8d..0ed2dc922 100644 --- a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 @@ -60,6 +60,7 @@ ConvertFrom-StringData @' ClusterLoginMissingPermissions = The account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0051) ClusterLoginMissingRecommendedPermissions = The recommended account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0052) ClusterLoginPermissionsPresent = The cluster login '{0}' has the required permissions. (SQLCOMMON0053) - ConnectingUsingCredentials = Connecting using the credential '{0}' and the login type '{1}'. (SQLCOMMON0054) + ConnectingUsingIntegrated = Anslutning som nuvarande användare '{0}' med integrerad säkerhet. (SQLCOMMON0054) CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) + ConnectingUsingImpersonation = Impersoner credential '{0}' med inloggningstyp '{1}'. (SQLCOMMON0056) '@ diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 9e3e914b9..2539a4b14 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1284,10 +1284,10 @@ InModuleScope 'SqlServerDsc.Common' { throw $Message } - $mockSetupCredentialUserName = 'TestUserName12345' - $mockSetupCredentialPassword = 'StrongOne7.' - $mockSetupCredentialSecurePassword = ConvertTo-SecureString -String $mockSetupCredentialPassword -AsPlainText -Force - $mockSetupCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSetupCredentialUserName, $mockSetupCredentialSecurePassword) + $mockSqlCredentialUserName = 'TestUserName12345' + $mockSqlCredentialPassword = 'StrongOne7.' + $mockSqlCredentialSecurePassword = ConvertTo-SecureString -String $mockSqlCredentialPassword -AsPlainText -Force + $mockSqlCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSqlCredentialUserName, $mockSqlCredentialSecurePassword) } BeforeEach { @@ -1322,9 +1322,9 @@ InModuleScope 'SqlServerDsc.Common' { Context 'When connecting to the named instance using Windows Authentication impersonation' { It 'Should not throw when connecting' { - $mockExpectedDataSource = "Data Source=$env:COMPUTERNAME\$mockInstanceName;User ID=$mockSetupCredentialUserName;Password=$mockSetupCredentialPassword" + $mockExpectedDataSource = "Data Source=$env:COMPUTERNAME\$mockInstanceName;User ID=$mockSqlCredentialUserName;Password=$mockSqlCredentialPassword" - { Connect-SQLAnalysis -SQLInstanceName $mockInstanceName -SetupCredential $mockSetupCredential } | Should -Not -Throw + { Connect-SQLAnalysis -SQLInstanceName $mockInstanceName -SetupCredential $mockSqlCredential } | Should -Not -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` -ParameterFilter $mockNewObject_MicrosoftAnalysisServicesServer_ParameterFilter @@ -1390,10 +1390,10 @@ InModuleScope 'SqlServerDsc.Common' { BeforeAll { $mockExpectedQuery = '' - $mockSetupCredentialUserName = 'TestUserName12345' - $mockSetupCredentialPassword = 'StrongOne7.' - $mockSetupCredentialSecurePassword = ConvertTo-SecureString -String $mockSetupCredentialPassword -AsPlainText -Force - $mockSetupCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSetupCredentialUserName, $mockSetupCredentialSecurePassword) + $mockSqlCredentialUserName = 'TestUserName12345' + $mockSqlCredentialPassword = 'StrongOne7.' + $mockSqlCredentialSecurePassword = ConvertTo-SecureString -String $mockSqlCredentialPassword -AsPlainText -Force + $mockSqlCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSqlCredentialUserName, $mockSqlCredentialSecurePassword) $masterDatabaseObject = New-Object -TypeName PSObject $masterDatabaseObject | Add-Member -MemberType NoteProperty -Name 'Name' -Value 'master' @@ -1456,7 +1456,7 @@ InModuleScope 'SqlServerDsc.Common' { SQLInstanceName = 'MSSQLSERVER' Database = 'master' Query = '' - DatabaseCredential = $mockSetupCredential + DatabaseCredential = $mockSqlCredential } $queryParametersWithSMO = @{ @@ -2360,10 +2360,15 @@ InModuleScope 'SqlServerDsc.Common' { throw $Message } - $mockSetupCredentialUserName = 'TestUserName12345' - $mockSetupCredentialPassword = 'StrongOne7.' - $mockSetupCredentialSecurePassword = ConvertTo-SecureString -String $mockSetupCredentialPassword -AsPlainText -Force - $mockSetupCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSetupCredentialUserName, $mockSetupCredentialSecurePassword) + $mockSqlCredentialUserName = 'TestUserName12345' + $mockSqlCredentialPassword = 'StrongOne7.' + $mockSqlCredentialSecurePassword = ConvertTo-SecureString -String $mockSqlCredentialPassword -AsPlainText -Force + $mockSqlCredential = New-Object -TypeName PSCredential -ArgumentList ($mockSqlCredentialUserName, $mockSqlCredentialSecurePassword) + + $mockWinCredentialUserName = 'DOMAIN\TestUserName12345' + $mockWinCredentialPassword = 'StrongerOne7.' + $mockWinCredentialSecurePassword = ConvertTo-SecureString -String $mockWinCredentialPassword -AsPlainText -Force + $mockWinCredential = New-Object -TypeName PSCredential -ArgumentList ($mockWinCredentialUserName, $mockWinCredentialSecurePassword) } BeforeEach { @@ -2394,10 +2399,10 @@ InModuleScope 'SqlServerDsc.Common' { $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' $mockExpectedDatabaseEngineLoginSecure = $false - $databaseEngineServerObject = Connect-SQL -ServerName $mockExpectedDatabaseEngineServer -SetupCredential $mockSetupCredential -LoginType 'SqlLogin' + $databaseEngineServerObject = Connect-SQL -ServerName $mockExpectedDatabaseEngineServer -SetupCredential $mockSqlCredential -LoginType 'SqlLogin' $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $false - $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSetupCredentialUserName - $databaseEngineServerObject.ConnectionContext.SecurePassword | Should -Be $mockSetupCredentialSecurePassword + $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSqlCredentialUserName + $databaseEngineServerObject.ConnectionContext.SecurePassword | Should -Be $mockSqlCredentialSecurePassword $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly $mockExpectedDatabaseEngineServer Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` @@ -2424,10 +2429,10 @@ InModuleScope 'SqlServerDsc.Common' { $mockExpectedDatabaseEngineInstance = $mockInstanceName $mockExpectedDatabaseEngineLoginSecure = $false - $databaseEngineServerObject = Connect-SQL -InstanceName $mockExpectedDatabaseEngineInstance -SetupCredential $mockSetupCredential -LoginType 'SqlLogin' + $databaseEngineServerObject = Connect-SQL -InstanceName $mockExpectedDatabaseEngineInstance -SetupCredential $mockSqlCredential -LoginType 'SqlLogin' $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $false - $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSetupCredentialUserName - $databaseEngineServerObject.ConnectionContext.SecurePassword | Should -Be $mockSetupCredentialSecurePassword + $databaseEngineServerObject.ConnectionContext.Login | Should -Be $mockSqlCredentialUserName + $databaseEngineServerObject.ConnectionContext.SecurePassword | Should -Be $mockSqlCredentialSecurePassword $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` @@ -2456,16 +2461,17 @@ InModuleScope 'SqlServerDsc.Common' { $testParameters = @{ ServerName = $mockExpectedDatabaseEngineServer InstanceName = $mockExpectedDatabaseEngineInstance - SetupCredential = $mockSetupCredential + SetupCredential = $mockWinCredential LoginType = 'WindowsUser' } $databaseEngineServerObject = Connect-SQL @testParameters $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true - $databaseEngineServerObject.ConnectionContext.ConnectAsUserPassword | Should -BeExactly $mockSetupCredential.GetNetworkCredential().Password - $databaseEngineServerObject.ConnectionContext.ConnectAsUserName | Should -BeExactly $mockSetupCredential.GetNetworkCredential().UserName + $databaseEngineServerObject.ConnectionContext.ConnectAsUserPassword | Should -BeExactly $mockWinCredential.GetNetworkCredential().Password + $databaseEngineServerObject.ConnectionContext.ConnectAsUserName | Should -BeExactly $mockWinCredential.GetNetworkCredential().UserName $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true + $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $true Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter From 49ac264545f7993afd307241650ff7d33fe2b328 Mon Sep 17 00:00:00 2001 From: Wayne Teutenberg Date: Mon, 22 Jul 2019 17:36:48 +1200 Subject: [PATCH 08/13] SqlServerSecureConnection: Add Parameter SuppressRestart (#1406) - Changes to SqlServerSecureConnection - Add parameter SuppressRestart with default value false. This allows users to suppress restarts after changes have been made. Changes will not take effect until the service has been restarted. --- CHANGELOG.md | 3 + .../MSFT_SqlServerSecureConnection.psm1 | 67 +++++++++++++++---- .../MSFT_SqlServerSecureConnection.schema.mof | 1 + ...SFT_SqlServerSecureConnection.strings.psd1 | 1 + README.md | 3 + .../MSFT_SqlServerSecureConnection.Tests.ps1 | 26 ++++++- 6 files changed, 85 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da6f32805..1be823aa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ - Added addtional verbose logging. - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). + - Add parameter SuppressRestart with default value false. + This allows users to suppress restarts after changes have been made. + Changes will not take effect until the service has been restarted. - Changes to SqlSetup - Correct minor style violation [issue #1387](https://github.com/PowerShell/SqlServerDsc/issues/1387). - Changes to SqlDatabase diff --git a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 index 5a881a95c..f18efa3a5 100644 --- a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 +++ b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 @@ -24,6 +24,11 @@ $script:localizedData = Get-LocalizedData -ResourceName 'MSFT_SqlServerSecureCon .PARAMETER ServiceAccount Name of the account running the SQL Server service. If parameter is set to "LocalSystem", then a connection error is displayed. Use "SYSTEM" instead, in that case. + + .PARAMETER SuppressRestart + If set to $true then the required restart will be suppressed. + You will need to restart the service before changes will take effect. + The default value is $false. #> function Get-TargetResource { @@ -51,7 +56,11 @@ function Get-TargetResource [Parameter(Mandatory = $true)] [System.String] - $ServiceAccount + $ServiceAccount, + + [Parameter()] + [System.Boolean] + $SuppressRestart = $false ) Write-Verbose -Message ( @@ -155,6 +164,7 @@ function Get-TargetResource ForceEncryption = [System.Boolean] $encryptionSettings.ForceEncryption Ensure = [System.String] $ensureValue ServiceAccount = [System.String] $ServiceAccount + SuppressRestart = [System.Boolean] $SuppressRestart } } @@ -176,6 +186,11 @@ function Get-TargetResource .PARAMETER ServiceAccount Name of the account running the SQL Server service. + + .PARAMETER SuppressRestart + If set to $true then the required restart will be suppressed. + You will need to restart the service before changes will take effect. + The default value is $false. #> function Set-TargetResource { @@ -202,7 +217,11 @@ function Set-TargetResource [Parameter(Mandatory = $true)] [System.String] - $ServiceAccount + $ServiceAccount, + + [Parameter()] + [System.Boolean] + $SuppressRestart = $false ) # Configuration manager requires thumbprint to be lowercase or it won't display the configured certificate. @@ -217,6 +236,7 @@ function Set-TargetResource ForceEncryption = $ForceEncryption Ensure = $Ensure ServiceAccount = $ServiceAccount + SuppressRestart = $SuppressRestart } $encryptionState = Get-TargetResource @parameters @@ -226,35 +246,44 @@ function Set-TargetResource if ($ForceEncryption -ne $encryptionState.ForceEncryption -or $Thumbprint -ne $encryptionState.Thumbprint) { Write-Verbose -Message ( - $script:localizedData.SetEncryptionSetting ` - -f $InstanceName, $Thumbprint, $ForceEncryption + $script:localizedData.SetEncryptionSetting -f $InstanceName, $Thumbprint, $ForceEncryption ) + Set-EncryptedConnectionSetting -InstanceName $InstanceName -Thumbprint $Thumbprint -ForceEncryption $ForceEncryption } if ((Test-CertificatePermission -Thumbprint $Thumbprint -ServiceAccount $ServiceAccount) -eq $false) { Write-Verbose -Message ( - $script:localizedData.SetCertificatePermission ` - -f $Thumbprint, $ServiceAccount + $script:localizedData.SetCertificatePermission -f $Thumbprint, $ServiceAccount ) + Set-CertificatePermission -Thumbprint $Thumbprint -ServiceAccount $ServiceAccount } } else { Write-Verbose -Message ( - $script:localizedData.RemoveEncryptionSetting ` - -f $InstanceName + $script:localizedData.RemoveEncryptionSetting -f $InstanceName ) + Set-EncryptedConnectionSetting -InstanceName $InstanceName -Thumbprint '' -ForceEncryption $false } - Write-Verbose -Message ( - $script:localizedData.RestartingService ` - -f $InstanceName - ) - Restart-SqlService -SQLServer localhost -SQLInstanceName $InstanceName + if ($SuppressRestart) + { + Write-Verbose -Message ( + $script:localizedData.SuppressRequiredRestart -f $InstanceName + ) + } + else + { + Write-Verbose -Message ( + $script:localizedData.RestartingService -f $InstanceName + ) + + Restart-SqlService -SQLServer localhost -SQLInstanceName $InstanceName + } } <# @@ -275,6 +304,11 @@ function Set-TargetResource .PARAMETER ServiceAccount Name of the account running the SQL Server service. + + .PARAMETER SuppressRestart + If set to $true then the required restart will be suppressed. + You will need to restart the service before changes will take effect. + The default value is $false. #> function Test-TargetResource { @@ -302,7 +336,11 @@ function Test-TargetResource [Parameter(Mandatory = $true)] [System.String] - $ServiceAccount + $ServiceAccount, + + [Parameter()] + [System.Boolean] + $SuppressRestart = $false ) $parameters = @{ @@ -311,6 +349,7 @@ function Test-TargetResource ForceEncryption = $ForceEncryption Ensure = $Ensure ServiceAccount = $ServiceAccount + SuppressRestart = $SuppressRestart } Write-Verbose -Message ( diff --git a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.schema.mof b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.schema.mof index 2be260bb6..c416c1604 100644 --- a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.schema.mof +++ b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.schema.mof @@ -6,6 +6,7 @@ class MSFT_SqlServerSecureConnection : OMI_BaseResource [Required, Description("Thumbprint of the certificate being used for encryption. If parameter Ensure is set to 'Absent', then the parameter Certificate can be set to an empty string.")] String Thumbprint; [Write, Description("If all connections to the SQL instance should be encrypted. If this parameter is not assigned a value, the default is, set to true, that all connections must be encrypted.")] boolean ForceEncryption; [Required, Description("Name of the account running the SQL Server service. If parameter is set to 'LocalSystem', then a connection error is displayed. Use 'SYSTEM' instead, in that case.")] String ServiceAccount; + [Write, Description("If set to $true then the required restart will be suppressed. You will need to restart the service before changes will take effect. The default value is $false.")] Boolean SuppressRestart; [Write, Description("If Encryption should be Enabled (Present) or Disabled (Absent)."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; }; diff --git a/DSCResources/MSFT_SqlServerSecureConnection/en-US/MSFT_SqlServerSecureConnection.strings.psd1 b/DSCResources/MSFT_SqlServerSecureConnection/en-US/MSFT_SqlServerSecureConnection.strings.psd1 index 0cc548cde..83a521724 100644 --- a/DSCResources/MSFT_SqlServerSecureConnection/en-US/MSFT_SqlServerSecureConnection.strings.psd1 +++ b/DSCResources/MSFT_SqlServerSecureConnection/en-US/MSFT_SqlServerSecureConnection.strings.psd1 @@ -17,4 +17,5 @@ ConvertFrom-StringData @' InstanceNotFound = SQL instance '{0}' not found on SQL Server. PrivateKeyPath = Certificate private key is located at '{0}'. CouldNotFindEncryptionValues = Could not find encryption values in registry for instance '{0}'. + SuppressRequiredRestart = Service '{0}' restart has been suppressed. Changes will not take effect until the service is restarted. '@ diff --git a/README.md b/README.md index 31b599324..35ab23ff5 100644 --- a/README.md +++ b/README.md @@ -1768,6 +1768,9 @@ In that case, the 'SYSTEM' service account can be used. * **`[Boolean]` ForceEncryption** _(Write)_: If all connections to the SQL instance should be encrypted. If this parameter is not assigned a value, the default is, set to *True*, that all connections must be encrypted. +* **`[Boolean]` SuppressRestart** _(Write)_: If set to $true then the required + restart will be suppressed. You will need to restart the service before + changes will take effect. The default value is $false. #### Examples diff --git a/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 b/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 index 16d901879..b1461d234 100644 --- a/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 +++ b/Tests/Unit/MSFT_SqlServerSecureConnection.Tests.ps1 @@ -283,6 +283,7 @@ try Thumbprint = $mockThumbprint ServiceAccount = $mockServiceAccount ForceEncryption = $true + SuppressRestart = $false Ensure = 'Present' } @@ -315,13 +316,34 @@ try { Set-TargetResource @defaultParameters } | Should -Not -Throw Assert-MockCalled -CommandName Set-EncryptedConnectionSetting -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } - Assert-MockCalled -CommandName Set-CertificatePermission -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } - Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 1 -Scope It } } + Context 'When SuppressRestart is true' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + InstanceName = $mockNamedInstanceName + Thumbprint = $mockThumbprint.ToUpper() + } + } -Verifiable + } + + $defaultParameters.SuppressRestart = $true + + It 'Should suppress restarting the SQL service' { + { Set-TargetResource @defaultParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Set-EncryptedConnectionSetting -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } + Assert-MockCalled -CommandName Set-CertificatePermission -Exactly -Times 1 -Scope It -ParameterFilter { $Thumbprint -ceq $mockThumbprint.ToLower() } + Assert-MockCalled -CommandName Restart-SqlService -Exactly -Times 0 -Scope It + } + + $defaultParameters.SuppressRestart = $false + } + Context 'When only certificate permissions are set' { Mock -CommandName Get-TargetResource -MockWith { return @{ From adcf2bb53dcdbf3856ca55d03fdf1bd9b567fc83 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 22 Jul 2019 09:45:44 +0200 Subject: [PATCH 09/13] SqlServerDsc: Changes to Invoke-Query (#1407) - Changes to helper function Invoke-Query - Now it will output verbose messages of the query that is run, so it not as quiet of what it is doing when a user asks for verbose output (issue #1404). - It is possible to redact text in the verbose output by providing strings in the new parameter `RedactText`. --- CHANGELOG.md | 5 ++ .../SqlServerDsc.Common.psm1 | 47 +++++++++++-- .../en-US/SqlServerDsc.Common.strings.psd1 | 2 + .../sv-SE/SqlServerDsc.Common.strings.psd1 | 4 +- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 70 ++++++++++++++++--- 5 files changed, 114 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1be823aa6..f1cf52c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object. - Can pipe Connect-SQL | Invoke-Query. - Added default values to Invoke-Query. + - Now it will output verbose messages of the query that is run, so it + not as quiet of what it is doing when a user asks for verbose output + ([issue #1404](https://github.com/PowerShell/SqlServerDsc/issues/1404)). + - It is possible to redact text in the verbose output by providing + strings in the new parameter `RedactText`. - Minor style fixes in unit tests. - Changes to helper function Connect-SQL - When impersonating WindowsUser credential use the NetworkCredential UserName. diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index 8d9b609d9..4f36e2b04 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -1594,8 +1594,9 @@ function Restart-ReportingServicesService The query string to execute. .PARAMETER DatabaseCredential - PSCredential object with the credentials to use to impersonate a user when connecting. - If this is not provided then the current user will be used to connect to the SQL Server Database Engine instance. + PSCredential object with the credentials to use to impersonate a user + when connecting. If this is not provided then the current user will be + used to connect to the SQL Server Database Engine instance. .PARAMETER LoginType Specifies which type of logon credential should be used. The valid types are @@ -1603,8 +1604,8 @@ function Restart-ReportingServicesService then the SetupCredential needs to be specified as well. .PARAMETER SqlServerObject - You can pass in an object type of 'Microsoft.SqlServer.Management.Smo.Server'. This can also be passed in - through the pipeline allowing you to use connect-sql | invoke-query if you wish. + You can pass in an object type of 'Microsoft.SqlServer.Management.Smo.Server'. + This can also be passed in through the pipeline. See examples. .PARAMETER WithResults Specifies if the query should return results. @@ -1612,6 +1613,11 @@ function Restart-ReportingServicesService .PARAMETER StatementTimeout Set the query StatementTimeout in seconds. Default 600 seconds (10mins). + .PARAMETER RedactText + One or more strings to redact from the query when verbose messages are + written to the console. Strings here will be escaped so they will not + be interpreted as regular expressions (RegEx). + .EXAMPLE Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master ` -Query 'SELECT name FROM sys.databases' -WithResults @@ -1623,6 +1629,12 @@ function Restart-ReportingServicesService .EXAMPLE Connect-SQL @sqlConnectionParameters | Invoke-Query -Database master ` -Query 'SELECT name FROM sys.databases' -WithResults + + .EXAMPLE + Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database MyDatabase ` + -Query "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" ` + -WithResults -RedactText @('Pa\sSw0rd1','Secret PassPhrase') -Verbose + #> function Invoke-Query { @@ -1670,7 +1682,11 @@ function Invoke-Query [Parameter()] [ValidateNotNull()] [System.Int32] - $StatementTimeout = 600 + $StatementTimeout = 600, + + [Parameter()] + [System.String[]] + $RedactText ) if ($PSCmdlet.ParameterSetName -eq 'SqlObject') @@ -1694,10 +1710,27 @@ function Invoke-Query $serverObject = Connect-SQL @connectSQLParameters } + $redactedQuery = $Query + + foreach ($redactString in $RedactText) + { + <# + Escaping the string to handle strings which could look like + regular expressions, like passwords. + #> + $escapedRedactedString = [System.Text.RegularExpressions.Regex]::Escape($redactString) + + $redactedQuery = $redactedQuery -ireplace $escapedRedactedString,'*******' + } + if ($WithResults) { try { + Write-Verbose -Message ( + $script:localizedData.ExecuteQueryWithResults -f $redactedQuery + ) -Verbose + $result = $serverObject.Databases[$Database].ExecuteWithResults($Query) } catch @@ -1710,6 +1743,10 @@ function Invoke-Query { try { + Write-Verbose -Message ( + $script:localizedData.ExecuteNonQuery -f $redactedQuery + ) -Verbose + $serverObject.Databases[$Database].ExecuteNonQuery($Query) } catch diff --git a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 index c60d79c3d..0a9a44bcf 100644 --- a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 @@ -57,4 +57,6 @@ ConvertFrom-StringData @' ConnectingUsingIntegrated = Connecting as current user '{0}' using integrated security. (SQLCOMMON0054) CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) ConnectingUsingImpersonation = Impersonate credential '{0}' with login type '{1}'. (SQLCOMMON0056) + ExecuteQueryWithResults = Returning the results of the query `{0}`. (SQLCOMMON0057) + ExecuteNonQuery = Executing the query `{0}`. (SQLCOMMON0058) '@ diff --git a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 index 0ed2dc922..ed4296803 100644 --- a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 @@ -62,5 +62,7 @@ ConvertFrom-StringData @' ClusterLoginPermissionsPresent = The cluster login '{0}' has the required permissions. (SQLCOMMON0053) ConnectingUsingIntegrated = Anslutning som nuvarande användare '{0}' med integrerad säkerhet. (SQLCOMMON0054) CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) - ConnectingUsingImpersonation = Impersoner credential '{0}' med inloggningstyp '{1}'. (SQLCOMMON0056) + ConnectingUsingImpersonation = Uppträder som behörigheten '{0}' med inloggningstyp '{1}'. (SQLCOMMON0056) + ExecuteQueryWithResults = Returnerar resultatet av frågan `{0}`. (SQLCOMMON0057) + ExecuteNonQuery = Exekverar frågan `{0}`. (SQLCOMMON0058) '@ diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 2539a4b14..032b940cb 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1465,14 +1465,16 @@ InModuleScope 'SqlServerDsc.Common' { Database = 'master' } - Context 'Execute a query with no results' { + Context 'When executing a query with no results' { + AfterEach { + Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + } + It 'Should execute the query silently' { $queryParams.Query = "EXEC sp_configure 'show advanced option', '1'" $mockExpectedQuery = $queryParams.Query.Clone() { Invoke-Query @queryParams } | Should -Not -Throw - - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } It 'Should throw the correct error, ExecuteNonQueryFailed, when executing the query fails' { @@ -1481,12 +1483,37 @@ InModuleScope 'SqlServerDsc.Common' { { Invoke-Query @queryParams } | Should -Throw ( $script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database ) + } - Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly + Context 'When text should be redacted' { + BeforeAll { + Mock -CommandName Write-Verbose -ParameterFilter { + $Message -eq ( + $script:localizedData.ExecuteNonQuery -f + "select * from MyTable where password = '*******' and password = '*******'" + ) + } -MockWith { + <# + MUST return another message than the parameter filter + is looking for, otherwise we get into a endless loop. + We returning the to show in the output how the verbose + message was redacted. + #> + Write-Verbose -Message ('MOCK OUTPUT: {0}' -f $Message) -Verbose + } + } + + It 'Should execute the query silently and redact text in the verbose output' { + $queryParams.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" + $mockExpectedQuery = $queryParams.Query.Clone() + + # The `Secret PassPhrase` is using the casing like this to test case-insensitive replace. + { Invoke-Query @queryParams -RedactText @('Pa\sSw0rd1','Secret PassPhrase') } | Should -Not -Throw + } } } - Context 'Execute a query with results' { + Context 'When executing a query with results' { It 'Should execute the query and return a result set' { $queryParams.Query = 'SELECT name FROM sys.databases' $mockExpectedQuery = $queryParams.Query.Clone() @@ -1505,9 +1532,36 @@ InModuleScope 'SqlServerDsc.Common' { Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } + + Context 'When text should be redacted' { + BeforeAll { + Mock -CommandName Write-Verbose -ParameterFilter { + $Message -eq ( + $script:localizedData.ExecuteQueryWithResults -f + "select * from MyTable where password = '*******' and password = '*******'" + ) + } -MockWith { + <# + MUST return another message than the parameter filter + is looking for, otherwise we get into a endless loop. + We returning the to show in the output how the verbose + message was redacted. + #> + Write-Verbose -Message ('MOCK OUTPUT: {0}' -f $Message) -Verbose + } + } + + It 'Should execute the query silently and redact text in the verbose output' { + $queryParams.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" + $mockExpectedQuery = $queryParams.Query.Clone() + + # The `Secret PassPhrase` is using the casing like this to test case-insensitive replace. + { Invoke-Query @queryParams -RedactText @('Pa\sSw0rd1','Secret PassPhrase') -WithResults } | Should -Not -Throw + } + } } - Context 'Pass in an SMO Server Object' { + Context 'When passing in an SMO Server Object' { Context 'Execute a query with no results' { It 'Should execute the query silently' { $queryParametersWithSMO.Query = "EXEC sp_configure 'show advanced option', '1'" @@ -1529,7 +1583,7 @@ InModuleScope 'SqlServerDsc.Common' { } } - Context 'Execute a query with results' { + Context 'When executing a query with results' { It 'Should execute the query and return a result set' { $queryParametersWithSMO.Query = 'SELECT name FROM sys.databases' $mockExpectedQuery = $queryParametersWithSMO.Query.Clone() @@ -1550,7 +1604,7 @@ InModuleScope 'SqlServerDsc.Common' { } } - Context 'Execute a query with piped SMO server object' { + Context 'When executing a query with piped SMO server object' { It 'Should execute the query and return a result set' { $mockQuery = 'SELECT name FROM sys.databases' $mockExpectedQuery = $mockQuery From f9e2939f02966db508ca4302c1ec38d388b26dc4 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Mon, 22 Jul 2019 12:31:54 +0200 Subject: [PATCH 10/13] SqlServerDsc: Consequently use parameters ServerName and InstanceName (#1409) - Changes to SqlServerDsc - Changes to helper function Invoke-Query - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Connect-SQLAnalysis - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Restart-SqlService - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Restart-ReportingServicesService - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Split-FullSqlInstanceName - Parameters and function name changed to use correct casing. - Changes to helper function Get-SqlInstanceMajorVersion - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Test-LoginEffectivePermissions - Parameters now match that of Connect-SQL (issue #1392). - Changes to helper function Test-AvailabilityReplicaSeedingModeAutomatic - Parameters now match that of Connect-SQL (issue #1392). --- CHANGELOG.md | 18 +- .../MSFT_SqlAGDatabase.psm1 | 16 +- .../MSFT_SqlAlwaysOnService.psm1 | 2 +- .../MSFT_SqlDatabaseDefaultLocation.psm1 | 2 +- DSCResources/MSFT_SqlRS/MSFT_SqlRS.psm1 | 2 +- .../MSFT_SqlServerConfiguration.psm1 | 2 +- .../MSFT_SqlServerNetwork.psm1 | 6 +- .../MSFT_SqlServerSecureConnection.psm1 | 6 +- .../MSFT_SqlServiceAccount.psm1 | 3 +- DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 | 2 +- .../SqlServerDsc.Common.psm1 | 198 +++++++++--------- Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 82 ++++---- 12 files changed, 179 insertions(+), 160 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1cf52c85..db2bdcacb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Changes to helper function Invoke-Query - Fixes issues in [issue #1355](https://github.com/PowerShell/SqlServerDsc/issues/1355). - Works together with Connect-SQL now. - - Parameters and Aliases now match that of Connect-SQL. + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). - Can now pass in credentials. - Can now pass in 'Microsoft.SqlServer.Management.Smo.Server' object. - Can also pipe in 'Microsoft.SqlServer.Management.Smo.Server' object. @@ -25,7 +25,21 @@ - Minor style fixes in unit tests. - Changes to helper function Connect-SQL - When impersonating WindowsUser credential use the NetworkCredential UserName. - - Added addtional verbose logging. + - Added additional verbose logging. + - Changes to helper function Connect-SQLAnalysis + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Restart-SqlService + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Restart-ReportingServicesService + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Split-FullSqlInstanceName + - Parameters and function name changed to use correct casing. + - Changes to helper function Get-SqlInstanceMajorVersion + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Test-LoginEffectivePermissions + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Test-AvailabilityReplicaSeedingModeAutomatic + - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). - Changes to SqlServerSecureConnection - Forced $Thumbprint to lowercase to fix [issue #1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). - Add parameter SuppressRestart with default value false. diff --git a/DSCResources/MSFT_SqlAGDatabase/MSFT_SqlAGDatabase.psm1 b/DSCResources/MSFT_SqlAGDatabase/MSFT_SqlAGDatabase.psm1 index 7f1d23a05..0b450e717 100644 --- a/DSCResources/MSFT_SqlAGDatabase/MSFT_SqlAGDatabase.psm1 +++ b/DSCResources/MSFT_SqlAGDatabase/MSFT_SqlAGDatabase.psm1 @@ -316,7 +316,7 @@ function Set-TargetResource $availabilityReplicaFilestreamLevel = @{} foreach ( $availabilityGroupReplica in $secondaryReplicas ) { - $connectSqlParameters = Split-FullSQLInstanceName -FullSQLInstanceName $availabilityGroupReplica.Name + $connectSqlParameters = Split-FullSqlInstanceName -FullSqlInstanceName $availabilityGroupReplica.Name $currentAvailabilityGroupReplicaServerObject = Connect-SQL @connectSqlParameters $availabilityReplicaFilestreamLevel.Add($availabilityGroupReplica.Name, $currentAvailabilityGroupReplicaServerObject.FilestreamLevel) } @@ -334,7 +334,7 @@ function Set-TargetResource $availabilityReplicaContainmentEnabled = @{} foreach ( $availabilityGroupReplica in $secondaryReplicas ) { - $connectSqlParameters = Split-FullSQLInstanceName -FullSQLInstanceName $availabilityGroupReplica.Name + $connectSqlParameters = Split-FullSqlInstanceName -FullSqlInstanceName $availabilityGroupReplica.Name $currentAvailabilityGroupReplicaServerObject = Connect-SQL @connectSqlParameters $availabilityReplicaContainmentEnabled.Add($availabilityGroupReplica.Name, $currentAvailabilityGroupReplicaServerObject.Configuration.ContainmentEnabled.ConfigValue) } @@ -355,14 +355,14 @@ function Set-TargetResource $availabilityReplicaMissingDirectories = @{} foreach ( $availabilityGroupReplica in $secondaryReplicas ) { - $connectSqlParameters = Split-FullSQLInstanceName -FullSQLInstanceName $availabilityGroupReplica.Name + $connectSqlParameters = Split-FullSqlInstanceName -FullSqlInstanceName $availabilityGroupReplica.Name $currentAvailabilityGroupReplicaServerObject = Connect-SQL @connectSqlParameters $missingDirectories = @() foreach ( $databaseFileDirectory in $databaseFileDirectories ) { $fileExistsQuery = "EXEC master.dbo.xp_fileexist '$databaseFileDirectory'" - $fileExistsResult = Invoke-Query -SQLServer $currentAvailabilityGroupReplicaServerObject.NetName -SQLInstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $fileExistsQuery -WithResults + $fileExistsResult = Invoke-Query -ServerName $currentAvailabilityGroupReplicaServerObject.NetName -InstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $fileExistsQuery -WithResults if ( $fileExistsResult.Tables.Rows.'File is a Directory' -ne 1 ) { @@ -393,7 +393,7 @@ function Set-TargetResource $availabilityReplicaMissingCertificates = @{} foreach ( $availabilityGroupReplica in $secondaryReplicas ) { - $connectSqlParameters = Split-FullSQLInstanceName -FullSQLInstanceName $availabilityGroupReplica.Name + $connectSqlParameters = Split-FullSqlInstanceName -FullSqlInstanceName $availabilityGroupReplica.Name $currentAvailabilityGroupReplicaServerObject = Connect-SQL @connectSqlParameters [System.Array]$installedCertificateThumbprints = $currentAvailabilityGroupReplicaServerObject.Databases['master'].Certificates | ForEach-Object { [System.BitConverter]::ToString($_.Thumbprint) } @@ -540,13 +540,13 @@ function Set-TargetResource foreach ( $availabilityGroupReplica in $secondaryReplicas ) { # Connect to the replica - $connectSqlParameters = Split-FullSQLInstanceName -FullSQLInstanceName $availabilityGroupReplica.Name + $connectSqlParameters = Split-FullSqlInstanceName -FullSqlInstanceName $availabilityGroupReplica.Name $currentAvailabilityGroupReplicaServerObject = Connect-SQL @connectSqlParameters $currentReplicaAvailabilityGroupObject = $currentAvailabilityGroupReplicaServerObject.AvailabilityGroups[$AvailabilityGroupName] # Restore the database - Invoke-Query -SQLServer $currentAvailabilityGroupReplicaServerObject.NetName -SQLInstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $restoreDatabaseQueryString -StatementTimeout 0 - Invoke-Query -SQLServer $currentAvailabilityGroupReplicaServerObject.NetName -SQLInstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $restoreLogQueryString -StatementTimeout 0 + Invoke-Query -ServerName $currentAvailabilityGroupReplicaServerObject.NetName -InstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $restoreDatabaseQueryString -StatementTimeout 0 + Invoke-Query -ServerName $currentAvailabilityGroupReplicaServerObject.NetName -InstanceName $currentAvailabilityGroupReplicaServerObject.ServiceName -Database master -Query $restoreLogQueryString -StatementTimeout 0 # Add the database to the Availability Group Add-SqlAvailabilityDatabase -InputObject $currentReplicaAvailabilityGroupObject -Database $databaseToAddToAvailabilityGroup diff --git a/DSCResources/MSFT_SqlAlwaysOnService/MSFT_SqlAlwaysOnService.psm1 b/DSCResources/MSFT_SqlAlwaysOnService/MSFT_SqlAlwaysOnService.psm1 index 1798de057..053f479c2 100644 --- a/DSCResources/MSFT_SqlAlwaysOnService/MSFT_SqlAlwaysOnService.psm1 +++ b/DSCResources/MSFT_SqlAlwaysOnService/MSFT_SqlAlwaysOnService.psm1 @@ -150,7 +150,7 @@ function Set-TargetResource ) # Now restart the SQL service so that all dependent services are also returned to their previous state - Restart-SqlService -SQLServer $ServerName -SQLInstanceName $InstanceName -Timeout $RestartTimeout + Restart-SqlService -ServerName $ServerName -InstanceName $InstanceName -Timeout $RestartTimeout # Verify always on was set if ( -not ( Test-TargetResource @PSBoundParameters ) ) diff --git a/DSCResources/MSFT_SqlDatabaseDefaultLocation/MSFT_SqlDatabaseDefaultLocation.psm1 b/DSCResources/MSFT_SqlDatabaseDefaultLocation/MSFT_SqlDatabaseDefaultLocation.psm1 index 823c6a140..76e7b4a53 100644 --- a/DSCResources/MSFT_SqlDatabaseDefaultLocation/MSFT_SqlDatabaseDefaultLocation.psm1 +++ b/DSCResources/MSFT_SqlDatabaseDefaultLocation/MSFT_SqlDatabaseDefaultLocation.psm1 @@ -193,7 +193,7 @@ Function Set-TargetResource if ($RestartService) { Write-Verbose -Message ($script:localizedData.RestartSqlServer -f $ServerName, $InstanceName) - Restart-SqlService -SQLServer $ServerName -SQLInstanceName $InstanceName + Restart-SqlService -ServerName $ServerName -InstanceName $InstanceName } } catch diff --git a/DSCResources/MSFT_SqlRS/MSFT_SqlRS.psm1 b/DSCResources/MSFT_SqlRS/MSFT_SqlRS.psm1 index 71dc65df9..13b461c4d 100644 --- a/DSCResources/MSFT_SqlRS/MSFT_SqlRS.psm1 +++ b/DSCResources/MSFT_SqlRS/MSFT_SqlRS.psm1 @@ -726,7 +726,7 @@ function Set-TargetResource elseif ( $restartReportingService -and (-not $SuppressRestart) ) { Write-Verbose -Message $script:localizedData.Restart - Restart-ReportingServicesService -SQLInstanceName $InstanceName -WaitTime 30 + Restart-ReportingServicesService -InstanceName $InstanceName -WaitTime 30 } } diff --git a/DSCResources/MSFT_SqlServerConfiguration/MSFT_SqlServerConfiguration.psm1 b/DSCResources/MSFT_SqlServerConfiguration/MSFT_SqlServerConfiguration.psm1 index c30c72917..cc70dfbf6 100644 --- a/DSCResources/MSFT_SqlServerConfiguration/MSFT_SqlServerConfiguration.psm1 +++ b/DSCResources/MSFT_SqlServerConfiguration/MSFT_SqlServerConfiguration.psm1 @@ -171,7 +171,7 @@ function Set-TargetResource -f $ServerName, $InstanceName ) - Restart-SqlService -SQLServer $ServerName -SQLInstanceName $InstanceName -Timeout $RestartTimeout + Restart-SqlService -ServerName $ServerName -InstanceName $InstanceName -Timeout $RestartTimeout } else { diff --git a/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 b/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 index 31a82a65a..1e6b013fe 100644 --- a/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 +++ b/DSCResources/MSFT_SqlServerNetwork/MSFT_SqlServerNetwork.psm1 @@ -215,9 +215,9 @@ function Set-TargetResource if ($RestartService -and $isRestartNeeded) { $restartSqlServiceParameters = @{ - SQLServer = $ServerName - SQLInstanceName = $InstanceName - Timeout = $RestartTimeout + ServerName = $ServerName + InstanceName = $InstanceName + Timeout = $RestartTimeout } if ($getTargetResourceResult.IsEnabled -eq $false -and $IsEnabled -eq $true) diff --git a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 index f18efa3a5..248bb1bbb 100644 --- a/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 +++ b/DSCResources/MSFT_SqlServerSecureConnection/MSFT_SqlServerSecureConnection.psm1 @@ -236,7 +236,6 @@ function Set-TargetResource ForceEncryption = $ForceEncryption Ensure = $Ensure ServiceAccount = $ServiceAccount - SuppressRestart = $SuppressRestart } $encryptionState = Get-TargetResource @parameters @@ -282,7 +281,7 @@ function Set-TargetResource $script:localizedData.RestartingService -f $InstanceName ) - Restart-SqlService -SQLServer localhost -SQLInstanceName $InstanceName + Restart-SqlService -ServerName localhost -InstanceName $InstanceName } } @@ -309,6 +308,8 @@ function Set-TargetResource If set to $true then the required restart will be suppressed. You will need to restart the service before changes will take effect. The default value is $false. + + Not used in Test-TargetResource. #> function Test-TargetResource { @@ -349,7 +350,6 @@ function Test-TargetResource ForceEncryption = $ForceEncryption Ensure = $Ensure ServiceAccount = $ServiceAccount - SuppressRestart = $SuppressRestart } Write-Verbose -Message ( diff --git a/DSCResources/MSFT_SqlServiceAccount/MSFT_SqlServiceAccount.psm1 b/DSCResources/MSFT_SqlServiceAccount/MSFT_SqlServiceAccount.psm1 index f5b841338..d56d23f23 100644 --- a/DSCResources/MSFT_SqlServiceAccount/MSFT_SqlServiceAccount.psm1 +++ b/DSCResources/MSFT_SqlServiceAccount/MSFT_SqlServiceAccount.psm1 @@ -260,7 +260,8 @@ function Set-TargetResource if ($RestartService) { Write-Verbose -Message ($script:localizedData.RestartingService -f $InstanceName) - Restart-SqlService -SQLServer $ServerName -SQLInstanceName $InstanceName + + Restart-SqlService -ServerName $ServerName -InstanceName $InstanceName } } diff --git a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 index b50142baf..d9d237243 100644 --- a/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 +++ b/DSCResources/MSFT_SqlSetup/MSFT_SqlSetup.psm1 @@ -336,7 +336,7 @@ function Get-TargetResource $analysisServiceAccountUsername = $analysisServiceCimInstance.StartName $AsSvcStartupType = ConvertTo-StartupType -StartMode $analysisServiceCimInstance.StartMode - $analysisServer = Connect-SQLAnalysis -SQLServer $sqlHostName -SQLInstanceName $InstanceName + $analysisServer = Connect-SQLAnalysis -ServerName $sqlHostName -InstanceName $InstanceName $analysisCollation = $analysisServer.ServerProperties['CollationName'].Value $analysisDataDirectory = $analysisServer.ServerProperties['DataDir'].Value diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index 4f36e2b04..883d0fed6 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -918,10 +918,10 @@ function Start-SqlSetupProcess .SYNOPSIS Connect to a SQL Server Database Engine and return the server object. - .PARAMETER SQLServer + .PARAMETER ServerName String containing the host name of the SQL Server to connect to. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName String containing the SQL Server Database Engine instance to connect to. .PARAMETER SetupCredential @@ -1075,10 +1075,10 @@ function Connect-SQL .SYNOPSIS Connect to a SQL Server Analysis Service and return the server object. - .PARAMETER SQLServer + .PARAMETER ServerName String containing the host name of the SQL Server to connect to. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName String containing the SQL Server Analysis Service instance to connect to. .PARAMETER SetupCredential @@ -1093,12 +1093,12 @@ function Connect-SQLAnalysis [Parameter()] [ValidateNotNullOrEmpty()] [System.String] - $SQLServer = $env:COMPUTERNAME, + $ServerName = $env:COMPUTERNAME, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] - $SQLInstanceName = 'MSSQLSERVER', + $InstanceName = 'MSSQLSERVER', [Parameter()] [ValidateNotNullOrEmpty()] @@ -1109,13 +1109,13 @@ function Connect-SQLAnalysis $null = [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.AnalysisServices') - if ($SQLInstanceName -eq 'MSSQLSERVER') + if ($InstanceName -eq 'MSSQLSERVER') { - $analysisServiceInstance = $SQLServer + $analysisServiceInstance = $ServerName } else { - $analysisServiceInstance = "$SQLServer\$SQLInstanceName" + $analysisServiceInstance = "$ServerName\$InstanceName" } if ($SetupCredential) @@ -1158,7 +1158,7 @@ function Connect-SQLAnalysis .SYNOPSIS Returns the major SQL version for the specific instance. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName String containing the name of the SQL instance to be configured. Default value is 'MSSQLSERVER'. .OUTPUTS @@ -1172,15 +1172,15 @@ function Get-SqlInstanceMajorVersion ( [Parameter(Mandatory = $true)] [System.String] - $SQLInstanceName = 'MSSQLSERVER' + $InstanceName = 'MSSQLSERVER' ) - $sqlInstanceId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$SQLInstanceName + $sqlInstanceId = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL').$InstanceName $sqlVersion = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$sqlInstanceId\Setup").Version if (-not $sqlVersion) { - $errorMessage = $script:localizedData.SqlServerVersionIsInvalid -f $SQLInstanceName + $errorMessage = $script:localizedData.SqlServerVersionIsInvalid -f $InstanceName New-InvalidResultException -Message $errorMessage } @@ -1321,10 +1321,10 @@ function Import-SQLPSModule .SYNOPSIS Restarts a SQL Server instance and associated services - .PARAMETER SQLServer + .PARAMETER ServerName Hostname of the SQL Server to be configured - .PARAMETER SQLInstanceName + .PARAMETER InstanceName Name of the SQL instance to be configured. Default is 'MSSQLSERVER' .PARAMETER Timeout @@ -1345,16 +1345,16 @@ function Import-SQLPSModule resource when it's used to disable protocol. .EXAMPLE - Restart-SqlService -SQLServer localhost + Restart-SqlService -ServerName localhost .EXAMPLE - Restart-SqlService -SQLServer localhost -SQLInstanceName 'NamedInstance' + Restart-SqlService -ServerName localhost -InstanceName 'NamedInstance' .EXAMPLE - Restart-SqlService -SQLServer localhost -SQLInstanceName 'NamedInstance' -SkipClusterCheck -SkipWaitForOnline + Restart-SqlService -ServerName localhost -InstanceName 'NamedInstance' -SkipClusterCheck -SkipWaitForOnline .EXAMPLE - Restart-SqlService -SQLServer CLU01 -Timeout 300 + Restart-SqlService -ServerName CLU01 -Timeout 300 #> function Restart-SqlService { @@ -1363,11 +1363,11 @@ function Restart-SqlService ( [Parameter(Mandatory = $true)] [System.String] - $SQLServer, + $ServerName, [Parameter()] [System.String] - $SQLInstanceName = 'MSSQLSERVER', + $InstanceName = 'MSSQLSERVER', [Parameter()] [System.UInt32] @@ -1385,7 +1385,7 @@ function Restart-SqlService if (-not $SkipClusterCheck.IsPresent) { ## Connect to the instance - $serverObject = Connect-SQL -ServerName $SQLServer -InstanceName $SQLInstanceName + $serverObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName if ($serverObject.IsClustered) { @@ -1430,13 +1430,13 @@ function Restart-SqlService if ($restartWindowsService) { - if ($SQLInstanceName -eq 'MSSQLSERVER') + if ($InstanceName -eq 'MSSQLSERVER') { $serviceName = 'MSSQLSERVER' } else { - $serviceName = 'MSSQL${0}' -f $SQLInstanceName + $serviceName = 'MSSQL${0}' -f $InstanceName } Write-Verbose -Message ($script:localizedData.GetServiceInformation -f 'SQL Server') -Verbose @@ -1459,7 +1459,7 @@ function Restart-SqlService } } - Write-Verbose -Message ($script:localizedData.WaitingInstanceTimeout -f $SQLServer, $SQLInstanceName, $Timeout) -Verbose + Write-Verbose -Message ($script:localizedData.WaitingInstanceTimeout -f $ServerName, $InstanceName, $Timeout) -Verbose if (-not $SkipWaitForOnline.IsPresent) { @@ -1468,7 +1468,7 @@ function Restart-SqlService do { # This call, if it fails, will take between ~9-10 seconds to return. - $testConnectionServerObject = Connect-SQL -ServerName $SQLServer -InstanceName $SQLInstanceName -ErrorAction SilentlyContinue + $testConnectionServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName -ErrorAction SilentlyContinue # Make sure we have an SMO object to test Status if ($testConnectionServerObject) @@ -1488,7 +1488,7 @@ function Restart-SqlService # Was the timeout period reach before able to connect to the SQL Server instance? if (-not $testConnectionServerObject -or $testConnectionServerObject.Status -ne 'Online') { - $errorMessage = $script:localizedData.FailedToConnectToInstanceTimeout -f $SQLServer, $SQLInstanceName, $Timeout + $errorMessage = $script:localizedData.FailedToConnectToInstanceTimeout -f $ServerName, $InstanceName, $Timeout New-InvalidOperationException -Message $errorMessage } } @@ -1498,7 +1498,7 @@ function Restart-SqlService .SYNOPSIS Restarts a Reporting Services instance and associated services - .PARAMETER SQLInstanceName + .PARAMETER InstanceName Name of the instance to be restarted. Default is 'MSSQLSERVER' (the default instance). @@ -1513,14 +1513,14 @@ function Restart-ReportingServicesService ( [Parameter()] [System.String] - $SQLInstanceName = 'MSSQLSERVER', + $InstanceName = 'MSSQLSERVER', [Parameter()] [System.UInt16] $WaitTime = 0 ) - if ($SQLInstanceName -eq 'SSRS') + if ($InstanceName -eq 'SSRS') { # Check if we're dealing with SSRS 2017 $ServiceName = 'SQLServerReportingServices' @@ -1537,9 +1537,9 @@ function Restart-ReportingServicesService Pre-2017 SSRS support multiple instances, check if we're dealing with a named instance. #> - if (-not ($SQLInstanceName -eq 'MSSQLSERVER')) + if (-not ($InstanceName -eq 'MSSQLSERVER')) { - $ServiceName += '${0}' -f $SQLInstanceName + $ServiceName += '${0}' -f $InstanceName } Write-Verbose -Message ($script:localizedData.GetServiceInformation -f $ServiceName) -Verbose @@ -1581,10 +1581,10 @@ function Restart-ReportingServicesService .SYNOPSIS Executes a query on the specified database. - .PARAMETER SQLServer + .PARAMETER ServerName The hostname of the server that hosts the SQL instance. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName The name of the SQL instance that hosts the database. .PARAMETER Database @@ -1619,11 +1619,11 @@ function Restart-ReportingServicesService be interpreted as regular expressions (RegEx). .EXAMPLE - Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master ` + Invoke-Query -ServerName Server1 -InstanceName MSSQLSERVER -Database master ` -Query 'SELECT name FROM sys.databases' -WithResults .EXAMPLE - Invoke-Query -SQLServer Server1 -SQLInstanceName MSSQLSERVER -Database master ` + Invoke-Query -ServerName Server1 -InstanceName MSSQLSERVER -Database master ` -Query 'RESTORE DATABASE [NorthWinds] WITH RECOVERY' .EXAMPLE @@ -1642,15 +1642,13 @@ function Invoke-Query param ( [Parameter(ParameterSetName='SqlServer')] - [Alias('ServerName')] [ValidateNotNullOrEmpty()] [System.String] - $SQLServer = $env:COMPUTERNAME, + $ServerName = $env:COMPUTERNAME, [Parameter(ParameterSetName='SqlServer')] - [Alias('InstanceName')] [System.String] - $SQLInstanceName = 'MSSQLSERVER', + $InstanceName = 'MSSQLSERVER', [Parameter(Mandatory = $true)] [System.String] @@ -1696,8 +1694,8 @@ function Invoke-Query elseif ($PSCmdlet.ParameterSetName -eq 'SqlServer') { $connectSQLParameters = @{ - ServerName = $SQLServer - InstanceName = $SQLInstanceName + ServerName = $ServerName + InstanceName = $InstanceName LoginType = $LoginType StatementTimeout = $StatementTimeout } @@ -1796,10 +1794,10 @@ function Update-AvailabilityGroupReplica .SYNOPSIS Impersonates a login and determines whether required permissions are present. - .PARAMETER SQLServer + .PARAMETER ServerName String containing the host name of the SQL Server to connect to. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName String containing the SQL Server Database Engine instance to connect to. .PARAMETER LoginName @@ -1836,11 +1834,11 @@ function Test-LoginEffectivePermissions [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $SQLServer, + $ServerName, [Parameter(Mandatory = $true)] [System.String] - $SQLInstanceName, + $InstanceName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -1865,10 +1863,10 @@ function Test-LoginEffectivePermissions $permissionsPresent = $false $invokeQueryParameters = @{ - SQLServer = $SQLServer - SQLInstanceName = $SQLInstanceName - Database = 'master' - WithResults = $true + ServerName = $ServerName + InstanceName = $InstanceName + Database = 'master' + WithResults = $true } if ( [System.String]::IsNullOrEmpty($SecurableName) ) @@ -1890,7 +1888,7 @@ function Test-LoginEffectivePermissions " } - Write-Verbose -Message ($script:localizedData.GetEffectivePermissionForLogin -f $LoginName, $sqlInstanceName) -Verbose + Write-Verbose -Message ($script:localizedData.GetEffectivePermissionForLogin -f $LoginName, $InstanceName) -Verbose $loginEffectivePermissionsResult = Invoke-Query @invokeQueryParameters -Query $queryToGetEffectivePermissionsForLogin $loginEffectivePermissions = $loginEffectivePermissionsResult.Tables.Rows.permission_name @@ -1914,10 +1912,10 @@ function Test-LoginEffectivePermissions .SYNOPSIS Determine if the seeding mode of the specified availability group is automatic. - .PARAMETER SQLServer + .PARAMETER ServerName The hostname of the server that hosts the SQL instance. - .PARAMETER SQLInstanceName + .PARAMETER InstanceName The name of the SQL instance that hosts the availability group. .PARAMETER AvailabilityGroupName @@ -1933,11 +1931,11 @@ function Test-AvailabilityReplicaSeedingModeAutomatic [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $SQLServer, + $ServerName, [Parameter(Mandatory = $true)] [System.String] - $SQLInstanceName, + $InstanceName, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] @@ -1953,16 +1951,16 @@ function Test-AvailabilityReplicaSeedingModeAutomatic # Assume automatic seeding is disabled by default $availabilityReplicaSeedingModeAutomatic = $false - $serverObject = Connect-SQL -ServerName $SQLServer -InstanceName $SQLInstanceName + $serverObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName # Only check the seeding mode if this is SQL 2016 or newer if ( $serverObject.Version -ge 13 ) { $invokeQueryParams = @{ - SQLServer = $SQLServer - SQLInstanceName = $SQLInstanceName - Database = 'master' - WithResults = $true + ServerName = $ServerName + InstanceName = $InstanceName + Database = 'master' + WithResults = $true } $queryToGetSeedingMode = " @@ -2044,87 +2042,93 @@ function Test-ImpersonatePermissions # The impersonate any login permission only exists in SQL 2014 and above $testLoginEffectivePermissionsParams = @{ - SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS - SQLInstanceName = $ServerObject.ServiceName - LoginName = $ServerObject.ConnectionContext.TrueLogin - Permissions = @('IMPERSONATE ANY LOGIN') + ServerName = $ServerObject.ComputerNamePhysicalNetBIOS + InstanceName = $ServerObject.ServiceName + LoginName = $ServerObject.ConnectionContext.TrueLogin + Permissions = @('IMPERSONATE ANY LOGIN') } $impersonatePermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams + if ($impersonatePermissionsPresent) { - Write-Verbose -Message ( 'The login "{0}" has impersonate any login permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" has impersonate any login permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.SInstanceName ) -Verbose return $impersonatePermissionsPresent } else { - Write-Verbose -Message ( 'The login "{0}" does not have impersonate any login permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" does not have impersonate any login permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName ) -Verbose } # Check for sysadmin / control server permission which allows impersonation $testLoginEffectivePermissionsParams = @{ - SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS - SQLInstanceName = $ServerObject.ServiceName - LoginName = $ServerObject.ConnectionContext.TrueLogin - Permissions = @('CONTROL SERVER') + ServerName = $ServerObject.ComputerNamePhysicalNetBIOS + InstanceName = $ServerObject.ServiceName + LoginName = $ServerObject.ConnectionContext.TrueLogin + Permissions = @('CONTROL SERVER') } + $impersonatePermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams + if ($impersonatePermissionsPresent) { - Write-Verbose -Message ( 'The login "{0}" has control server permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" has control server permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName ) -Verbose return $impersonatePermissionsPresent } else { - Write-Verbose -Message ( 'The login "{0}" does not have control server permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" does not have control server permissions on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName ) -Verbose } if (-not [System.String]::IsNullOrEmpty($SecurableName)) { # Check for login-specific impersonation permissions $testLoginEffectivePermissionsParams = @{ - SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS - SQLInstanceName = $ServerObject.ServiceName - LoginName = $ServerObject.ConnectionContext.TrueLogin - Permissions = @('IMPERSONATE') - SecurableClass = 'LOGIN' - SecurableName = $SecurableName + ServerName = $ServerObject.ComputerNamePhysicalNetBIOS + InstanceName = $ServerObject.ServiceName + LoginName = $ServerObject.ConnectionContext.TrueLogin + Permissions = @('IMPERSONATE') + SecurableClass = 'LOGIN' + SecurableName = $SecurableName } $impersonatePermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams + if ($impersonatePermissionsPresent) { - Write-Verbose -Message ( 'The login "{0}" has impersonate permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName, $SecurableName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" has impersonate permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName, $SecurableName ) -Verbose return $impersonatePermissionsPresent } else { - Write-Verbose -Message ( 'The login "{0}" does not have impersonate permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName, $SecurableName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" does not have impersonate permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName, $SecurableName ) -Verbose } # Check for login-specific control permissions $testLoginEffectivePermissionsParams = @{ - SQLServer = $ServerObject.ComputerNamePhysicalNetBIOS - SQLInstanceName = $ServerObject.ServiceName - LoginName = $ServerObject.ConnectionContext.TrueLogin - Permissions = @('CONTROL') - SecurableClass = 'LOGIN' - SecurableName = $SecurableName + ServerName = $ServerObject.ComputerNamePhysicalNetBIOS + InstanceName = $ServerObject.ServiceName + LoginName = $ServerObject.ConnectionContext.TrueLogin + Permissions = @('CONTROL') + SecurableClass = 'LOGIN' + SecurableName = $SecurableName } $impersonatePermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams + if ($impersonatePermissionsPresent) { - Write-Verbose -Message ( 'The login "{0}" has control permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName, $SecurableName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" has control permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName, $SecurableName ) -Verbose return $impersonatePermissionsPresent } else { - Write-Verbose -Message ( 'The login "{0}" does not have control permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName, $SecurableName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" does not have control permissions on the instance "{1}\{2}" for the login "{3}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName, $SecurableName ) -Verbose } } - Write-Verbose -Message ( 'The login "{0}" does not have any impersonate permissions required on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.SQLServer, $testLoginEffectivePermissionsParams.SQLInstanceName ) -Verbose + Write-Verbose -Message ( 'The login "{0}" does not have any impersonate permissions required on the instance "{1}\{2}".' -f $testLoginEffectivePermissionsParams.LoginName, $testLoginEffectivePermissionsParams.ServerName, $testLoginEffectivePermissionsParams.InstanceName ) -Verbose + return $impersonatePermissionsPresent } @@ -2132,11 +2136,11 @@ function Test-ImpersonatePermissions .SYNOPSIS Takes a SQL Instance name in the format of 'Server\Instance' and splits it into a hash table prepared to be passed into Connect-SQL. - .PARAMETER FullSQLInstanceName + .PARAMETER FullSqlInstanceName The full SQL instance name string to be split. .OUTPUTS - Hash table with the properties SQLServer and SQLInstanceName. + Hash table with the properties ServerName and InstanceName. #> function Split-FullSqlInstanceName { @@ -2145,10 +2149,10 @@ function Split-FullSqlInstanceName [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] - $FullSQLInstanceName + $FullSqlInstanceName ) - $sqlServer, $sqlInstanceName = $FullSQLInstanceName.Split('\') + $sqlServer, $sqlInstanceName = $FullSqlInstanceName.Split('\') if ( [System.String]::IsNullOrEmpty($sqlInstanceName) ) { @@ -2193,10 +2197,10 @@ function Test-ClusterPermissions if ( $ServerObject.Logins[$loginName] -and -not $clusterPermissionsPresent ) { $testLoginEffectivePermissionsParams = @{ - SQLServer = $sqlServer - SQLInstanceName = $sqlInstanceName - LoginName = $loginName - Permissions = $availabilityGroupManagementPerms + ServerName = $sqlServer + InstanceName = $sqlInstanceName + LoginName = $loginName + Permissions = $availabilityGroupManagementPerms } $clusterPermissionsPresent = Test-LoginEffectivePermissions @testLoginEffectivePermissionsParams diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 032b940cb..228b14e98 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1062,7 +1062,7 @@ InModuleScope 'SqlServerDsc.Common' { $mockDynamicStatus = 'Online' It 'Should restart SQL Service and running SQL Agent service' { - { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'MSSQLSERVER' } | Should -Not -Throw + { Restart-SqlService -ServerName $env:ComputerName -InstanceName 'MSSQLSERVER' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1075,7 +1075,7 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should restart SQL Service, and not do cluster cluster check' { Mock -CommandName Get-CimInstance - { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOCLUSTERCHECK' -SkipClusterCheck } | Should -Not -Throw + { Restart-SqlService -ServerName $env:ComputerName -InstanceName 'NOCLUSTERCHECK' -SkipClusterCheck } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL -Scope It -Exactly -Times 1 Assert-MockCalled -CommandName Get-Service -Scope It -Exactly -Times 1 @@ -1087,7 +1087,7 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should restart SQL Service, and not do cluster cluster check nor check online status' { Mock -CommandName Get-CimInstance - { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOCONNECT' -SkipClusterCheck -SkipWaitForOnline } | Should -Not -Throw + { Restart-SqlService -ServerName $env:ComputerName -InstanceName 'NOCONNECT' -SkipClusterCheck -SkipWaitForOnline } | Should -Not -Throw Assert-MockCalled -CommandName Get-Service -Scope It -Exactly -Times 1 Assert-MockCalled -CommandName Restart-Service -Scope It -Exactly -Times 1 @@ -1097,7 +1097,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart SQL Service and not try to restart missing SQL Agent service' { - { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'NOAGENT' } | Should -Not -Throw + { Restart-SqlService -ServerName $env:ComputerName -InstanceName 'NOAGENT' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1108,7 +1108,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart SQL Service and not try to restart stopped SQL Agent service' { - { Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'STOPPEDAGENT' } | Should -Not -Throw + { Restart-SqlService -ServerName $env:ComputerName -InstanceName 'STOPPEDAGENT' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1136,7 +1136,7 @@ InModuleScope 'SqlServerDsc.Common' { $errorMessage = $localizedData.FailedToConnectToInstanceTimeout -f $env:ComputerName, 'MSSQLSERVER', 4 { - Restart-SqlService -SQLServer $env:ComputerName -SQLInstanceName 'MSSQLSERVER' -Timeout 4 + Restart-SqlService -ServerName $env:ComputerName -InstanceName 'MSSQLSERVER' -Timeout 4 } | Should -Throw $errorMessage Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { @@ -1213,7 +1213,7 @@ InModuleScope 'SqlServerDsc.Common' { $mockDynamicStatus = 'Online' It 'Should restart SQL Server and SQL Agent resources for a clustered default instance' { - { Restart-SqlService -SQLServer 'CLU01' } | Should -Not -Throw + { Restart-SqlService -ServerName 'CLU01' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1225,7 +1225,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart SQL Server and SQL Agent resources for a clustered named instance' { - { Restart-SqlService -SQLServer 'CLU01' -SQLInstanceName 'NAMEDINSTANCE' } | Should -Not -Throw + { Restart-SqlService -ServerName 'CLU01' -InstanceName 'NAMEDINSTANCE' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1237,7 +1237,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should not try to restart a SQL Agent resource that is not online' { - { Restart-SqlService -SQLServer 'CLU01' -SQLInstanceName 'STOPPEDAGENT' } | Should -Not -Throw + { Restart-SqlService -ServerName 'CLU01' -InstanceName 'STOPPEDAGENT' } | Should -Not -Throw Assert-MockCalled -CommandName Connect-SQL { $PSBoundParameters.ContainsKey('ErrorAction') -eq $false @@ -1313,7 +1313,7 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should not throw when connecting' { $mockExpectedDataSource = "Data Source=$env:COMPUTERNAME\$mockInstanceName" - { Connect-SQLAnalysis -SQLInstanceName $mockInstanceName } | Should -Not -Throw + { Connect-SQLAnalysis -InstanceName $mockInstanceName } | Should -Not -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` -ParameterFilter $mockNewObject_MicrosoftAnalysisServicesServer_ParameterFilter @@ -1324,7 +1324,7 @@ InModuleScope 'SqlServerDsc.Common' { It 'Should not throw when connecting' { $mockExpectedDataSource = "Data Source=$env:COMPUTERNAME\$mockInstanceName;User ID=$mockSqlCredentialUserName;Password=$mockSqlCredentialPassword" - { Connect-SQLAnalysis -SQLInstanceName $mockInstanceName -SetupCredential $mockSqlCredential } | Should -Not -Throw + { Connect-SQLAnalysis -InstanceName $mockInstanceName -SetupCredential $mockSqlCredential } | Should -Not -Throw Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` -ParameterFilter $mockNewObject_MicrosoftAnalysisServicesServer_ParameterFilter @@ -1371,11 +1371,11 @@ InModuleScope 'SqlServerDsc.Common' { $mockExpectedDataSource = "Force wrong data source" $testParameters = @{ - SQLServer = 'DummyHost' - SQLInstanceName = $mockInstanceName + ServerName = 'DummyHost' + InstanceName = $mockInstanceName } - $mockCorrectErrorMessage = ($script:localizedData.FailedToConnectToAnalysisServicesInstance -f "$($testParameters.SQLServer)\$($testParameters.SQLInstanceName)") + $mockCorrectErrorMessage = ($script:localizedData.FailedToConnectToAnalysisServicesInstance -f "$($testParameters.ServerName)\$($testParameters.InstanceName)") { Connect-SQLAnalysis @testParameters } | Should -Throw $mockCorrectErrorMessage Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` @@ -1452,8 +1452,8 @@ InModuleScope 'SqlServerDsc.Common' { } $queryParams = @{ - SQLServer = 'Server1' - SQLInstanceName = 'MSSQLSERVER' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' Database = 'master' Query = '' DatabaseCredential = $mockSqlCredential @@ -1461,7 +1461,7 @@ InModuleScope 'SqlServerDsc.Common' { $queryParametersWithSMO = @{ Query = '' - SqlServerObject = $mockSMOServer + SqlServerObject = $mockSMOServer Database = 'master' } @@ -1682,15 +1682,15 @@ InModuleScope 'SqlServerDsc.Common' { } $testLoginEffectiveServerPermissionsParams = @{ - SQLServer = 'Server1' - SQLInstanceName = 'MSSQLSERVER' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' Login = 'NT SERVICE\ClusSvc' Permissions = @() } $testLoginEffectiveLoginPermissionsParams = @{ - SQLServer = 'Server1' - SQLInstanceName = 'MSSQLSERVER' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' Login = 'NT SERVICE\ClusSvc' Permissions = @() SecurableClass = 'LOGIN' @@ -2050,7 +2050,7 @@ InModuleScope 'SqlServerDsc.Common' { Context 'When calling Get-SqlInstanceMajorVersion' { It 'Should return the correct major SQL version number' { - $result = Get-SqlInstanceMajorVersion -SQLInstanceName $mockInstanceName + $result = Get-SqlInstanceMajorVersion -InstanceName $mockInstanceName $result | Should -Be $mockSqlMajorVersion Assert-MockCalled -CommandName Get-ItemProperty -Exactly -Times 1 -Scope It ` @@ -2070,7 +2070,7 @@ InModuleScope 'SqlServerDsc.Common' { } -Verifiable $mockCorrectErrorMessage = ($script:localizedData.SqlServerVersionIsInvalid -f $mockInstanceName) - { Get-SqlInstanceMajorVersion -SQLInstanceName $mockInstanceName } | Should -Throw $mockCorrectErrorMessage + { Get-SqlInstanceMajorVersion -InstanceName $mockInstanceName } | Should -Throw $mockCorrectErrorMessage Assert-MockCalled -CommandName Get-ItemProperty -Exactly -Times 1 -Scope It ` -ParameterFilter $mockGetItemProperty_ParameterFilter_MicrosoftSQLServer_InstanceNames_SQL @@ -2200,9 +2200,9 @@ InModuleScope 'SqlServerDsc.Common' { } $testAvailabilityReplicaSeedingModeAutomaticParams = @{ - SQLServer = 'Server1' - SQLInstanceName = 'MSSQLSERVER' - AvailabilityGroupName = 'Group1' + ServerName = 'Server1' + InstanceName = 'MSSQLSERVER' + AvailabilityGroupName = 'Group1' AvailabilityReplicaName = 'Replica2' } @@ -2572,28 +2572,28 @@ InModuleScope 'SqlServerDsc.Common' { Assert-VerifiableMock } - Describe 'SqlServerDsc.Common\Split-FullSQLInstanceName' { - Context 'When the "FullSQLInstanceName" parameter is not supplied' { - It 'Should throw when the "FullSQLInstanceName" parameter is $null' { - { Split-FullSQLInstanceName -FullSQLInstanceName $null } | Should -Throw + Describe 'SqlServerDsc.Common\Split-FullSqlInstanceName' { + Context 'When the "FullSqlInstanceName" parameter is not supplied' { + It 'Should throw when the "FullSqlInstanceName" parameter is $null' { + { Split-FullSqlInstanceName -FullSqlInstanceName $null } | Should -Throw } - It 'Should throw when the "FullSQLInstanceName" parameter is an empty string' { - { Split-FullSQLInstanceName -FullSQLInstanceName '' } | Should -Throw + It 'Should throw when the "FullSqlInstanceName" parameter is an empty string' { + { Split-FullSqlInstanceName -FullSqlInstanceName '' } | Should -Throw } } - Context 'When the "FullSQLInstanceName" parameter is supplied' { - It 'Should throw when the "FullSQLInstanceName" parameter is "ServerName"' { - $result = Split-FullSQLInstanceName -FullSQLInstanceName 'ServerName' + Context 'When the "FullSqlInstanceName" parameter is supplied' { + It 'Should throw when the "FullSqlInstanceName" parameter is "ServerName"' { + $result = Split-FullSqlInstanceName -FullSqlInstanceName 'ServerName' $result.Count | Should -Be 2 $result.ServerName | Should -Be 'ServerName' $result.InstanceName | Should -Be 'MSSQLSERVER' } - It 'Should throw when the "FullSQLInstanceName" parameter is "ServerName\InstanceName"' { - $result = Split-FullSQLInstanceName -FullSQLInstanceName 'ServerName\InstanceName' + It 'Should throw when the "FullSqlInstanceName" parameter is "ServerName\InstanceName"' { + $result = Split-FullSqlInstanceName -FullSqlInstanceName 'ServerName\InstanceName' $result.Count | Should -Be 2 $result.ServerName | Should -Be 'ServerName' @@ -2724,7 +2724,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart the service and dependent service' { - { Restart-ReportingServicesService -SQLInstanceName 'MSSQLSERVER' } | Should -Not -Throw + { Restart-ReportingServicesService -InstanceName 'MSSQLSERVER' } | Should -Not -Throw Assert-MockCalled -CommandName Get-Service -ParameterFilter { $Name -eq $mockServiceName @@ -2749,7 +2749,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart the service and dependent service' { - { Restart-ReportingServicesService -SQLInstanceName 'SSRS' } | Should -Not -Throw + { Restart-ReportingServicesService -InstanceName 'SSRS' } | Should -Not -Throw Assert-MockCalled -CommandName Get-Service -ParameterFilter { $Name -eq $mockServiceName @@ -2774,7 +2774,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart the service and dependent service' { - { Restart-ReportingServicesService -SQLInstanceName 'TEST' } | Should -Not -Throw + { Restart-ReportingServicesService -InstanceName 'TEST' } | Should -Not -Throw Assert-MockCalled -CommandName Get-Service -ParameterFilter { $Name -eq $mockServiceName @@ -2800,7 +2800,7 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should restart the service and dependent service' { - { Restart-ReportingServicesService -SQLInstanceName 'TEST' -WaitTime 1 } | Should -Not -Throw + { Restart-ReportingServicesService -InstanceName 'TEST' -WaitTime 1 } | Should -Not -Throw Assert-MockCalled -CommandName Get-Service -ParameterFilter { $Name -eq $mockServiceName From 81a68cea81a82dcbbe3aa4035f805c72f4c4a6a6 Mon Sep 17 00:00:00 2001 From: Johan Ljunggren Date: Sat, 27 Jul 2019 10:42:04 +0200 Subject: [PATCH 11/13] SqlServerDsc: Changes to helper function Connect-SQL (#1410) - Changes to SqlServerDsc - Changes to helper function Connect-SQL - Connect-SQL now uses parameter sets to more intuitive evaluate that the correct parameters are used in different scenarios (issue #1403). --- CHANGELOG.md | 3 + .../SqlServerDsc.Common.psm1 | 93 +++++----- .../en-US/SqlServerDsc.Common.strings.psd1 | 1 - .../sv-SE/SqlServerDsc.Common.strings.psd1 | 1 - Tests/Unit/SqlServerDsc.Common.Tests.ps1 | 159 +++++++++++------- 5 files changed, 158 insertions(+), 99 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db2bdcacb..217c7112a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,9 @@ - Changes to helper function Connect-SQL - When impersonating WindowsUser credential use the NetworkCredential UserName. - Added additional verbose logging. + - Connect-SQL now uses parameter sets to more intuitive evaluate that + the correct parameters are used in different scenarios + ([issue #1403](https://github.com/PowerShell/SqlServerDsc/issues/1403)). - Changes to helper function Connect-SQLAnalysis - Parameters now match that of Connect-SQL ([issue #1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). - Changes to helper function Restart-SqlService diff --git a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 index 883d0fed6..3dc00e4ce 100644 --- a/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 +++ b/Modules/SqlServerDsc.Common/SqlServerDsc.Common.psm1 @@ -920,54 +920,71 @@ function Start-SqlSetupProcess .PARAMETER ServerName String containing the host name of the SQL Server to connect to. + Defaults to $env:COMPUTERNAME. .PARAMETER InstanceName String containing the SQL Server Database Engine instance to connect to. + Defaults to 'MSSQLSERVER'. .PARAMETER SetupCredential - PSCredential object with the credentials to use to impersonate a user when connecting. - If this is not provided then the current user will be used to connect to the SQL Server Database Engine instance. + The credentials to use to impersonate a user when connecting to the + SQL Server Database Engine instance. If this parameter is left out, then + the current user will be used to connect to the SQL Server Database Engine + instance using Windows Integrated authentication. .PARAMETER LoginType Specifies which type of logon credential should be used. The valid types - are Integrated, WindowsUser, or SqlLogin. If WindowsUser or SqlLogin are - specified then the parameter SetupCredential needs to be specified as well. - If set to 'Integrated' then the credentials that the resource current are - run with will be used. + are 'WindowsUser' or 'SqlLogin'. Defaults to 'WindowsUser' If set to 'WindowsUser' then the it will impersonate using the Windows login specified in the parameter SetupCredential. If set to 'WindowsUser' then the it will impersonate using the native SQL login specified in the parameter SetupCredential. - Default value is 'Integrated'. .PARAMETER StatementTimeout - Set the query StatementTimeout in seconds. Default 600 seconds (10mins). + Set the query StatementTimeout in seconds. Default 600 seconds (10 minutes). + + .EXAMPLE + Connect-SQL + + Connects to the default instance on the local server. + + .EXAMPLE + Connect-SQL -InstanceName 'MyInstance' + + Connects to the instance 'MyInstance' on the local server. + + .EXAMPLE + Connect-SQL ServerName 'sql.company.local' -InstanceName 'MyInstance' + + Connects to the instance 'MyInstance' on the server 'sql.company.local'. #> function Connect-SQL { - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName='SqlServer')] param ( - [Parameter()] + [Parameter(ParameterSetName='SqlServer')] + [Parameter(ParameterSetName='SqlServerWithCredential')] [ValidateNotNull()] [System.String] $ServerName = $env:COMPUTERNAME, - [Parameter()] + [Parameter(ParameterSetName='SqlServer')] + [Parameter(ParameterSetName='SqlServerWithCredential')] [ValidateNotNull()] [System.String] $InstanceName = 'MSSQLSERVER', - [Parameter()] + [Parameter(ParameterSetName='SqlServerWithCredential', Mandatory = $true)] [ValidateNotNull()] [Alias('DatabaseCredential')] [System.Management.Automation.PSCredential] $SetupCredential, - [Parameter()] - [ValidateSet('Integrated', 'WindowsUser', 'SqlLogin')] + [Parameter(ParameterSetName='SqlServerWithCredential')] + [ValidateSet('WindowsUser', 'SqlLogin')] [System.String] - $LoginType = 'Integrated', + $LoginType = 'WindowsUser', [Parameter()] [ValidateNotNull()] @@ -992,7 +1009,7 @@ function Connect-SQL $sqlConnectionContext.StatementTimeout = $StatementTimeout $sqlConnectionContext.ApplicationName = 'SqlServerDsc' - if ($LoginType -eq 'Integrated') + if ($PSCmdlet.ParameterSetName -eq 'SqlServer') { <# This is only used for verbose messaging and not for the connection @@ -1006,33 +1023,25 @@ function Connect-SQL } else { - if ($SetupCredential) - { - $connectUserName = $SetupCredential.GetNetworkCredential().UserName + $connectUserName = $SetupCredential.GetNetworkCredential().UserName - Write-Verbose -Message ( - $script:localizedData.ConnectingUsingImpersonation -f $connectUsername, $LoginType - ) -Verbose - - if ($LoginType -eq 'SqlLogin') - { - $sqlConnectionContext.LoginSecure = $false - $sqlConnectionContext.Login = $connectUserName - $sqlConnectionContext.SecurePassword = $SetupCredential.Password - } + Write-Verbose -Message ( + $script:localizedData.ConnectingUsingImpersonation -f $connectUsername, $LoginType + ) -Verbose - if ($LoginType -eq 'WindowsUser') - { - $sqlConnectionContext.LoginSecure = $true - $sqlConnectionContext.ConnectAsUser = $true - $sqlConnectionContext.ConnectAsUserName = $connectUserName - $sqlConnectionContext.ConnectAsUserPassword = $SetupCredential.GetNetworkCredential().Password - } + if ($LoginType -eq 'SqlLogin') + { + $sqlConnectionContext.LoginSecure = $false + $sqlConnectionContext.Login = $connectUserName + $sqlConnectionContext.SecurePassword = $SetupCredential.Password } - else + + if ($LoginType -eq 'WindowsUser') { - $errorMessage = $script:localizedData.CredentialsNotSpecified -f $LoginType - New-InvalidArgumentException -ArgumentName 'SetupCredential' -Message $errorMessage + $sqlConnectionContext.LoginSecure = $true + $sqlConnectionContext.ConnectAsUser = $true + $sqlConnectionContext.ConnectAsUserName = $connectUserName + $sqlConnectionContext.ConnectAsUserPassword = $SetupCredential.GetNetworkCredential().Password } } @@ -1696,10 +1705,14 @@ function Invoke-Query $connectSQLParameters = @{ ServerName = $ServerName InstanceName = $InstanceName - LoginType = $LoginType StatementTimeout = $StatementTimeout } + if ($LoginType -ne 'Integrated') + { + $connectSQLParameters['LoginType'] = $LoginType + } + if ($PSBoundParameters.ContainsKey('DatabaseCredential')) { $connectSQLParameters.SetupCredential = $DatabaseCredential diff --git a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 index 0a9a44bcf..7ada50485 100644 --- a/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/en-US/SqlServerDsc.Common.strings.psd1 @@ -55,7 +55,6 @@ ConvertFrom-StringData @' ClusterLoginMissingRecommendedPermissions = The recommended account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0052) ClusterLoginPermissionsPresent = The cluster login '{0}' has the required permissions. (SQLCOMMON0053) ConnectingUsingIntegrated = Connecting as current user '{0}' using integrated security. (SQLCOMMON0054) - CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) ConnectingUsingImpersonation = Impersonate credential '{0}' with login type '{1}'. (SQLCOMMON0056) ExecuteQueryWithResults = Returning the results of the query `{0}`. (SQLCOMMON0057) ExecuteNonQuery = Executing the query `{0}`. (SQLCOMMON0058) diff --git a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 index ed4296803..f4aa0093f 100644 --- a/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 +++ b/Modules/SqlServerDsc.Common/sv-SE/SqlServerDsc.Common.strings.psd1 @@ -61,7 +61,6 @@ ConvertFrom-StringData @' ClusterLoginMissingRecommendedPermissions = The recommended account '{0}' is missing one or more of the following permissions: {1} (SQLCOMMON0052) ClusterLoginPermissionsPresent = The cluster login '{0}' has the required permissions. (SQLCOMMON0053) ConnectingUsingIntegrated = Anslutning som nuvarande användare '{0}' med integrerad säkerhet. (SQLCOMMON0054) - CredentialsNotSpecified = The Logon type of '{0}' was specified which requires credentials, but the credentials parameter was not specified. (SQLCOMMON0055) ConnectingUsingImpersonation = Uppträder som behörigheten '{0}' med inloggningstyp '{1}'. (SQLCOMMON0056) ExecuteQueryWithResults = Returnerar resultatet av frågan `{0}`. (SQLCOMMON0057) ExecuteNonQuery = Exekverar frågan `{0}`. (SQLCOMMON0058) diff --git a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 index 228b14e98..14d4a8a13 100644 --- a/Tests/Unit/SqlServerDsc.Common.Tests.ps1 +++ b/Tests/Unit/SqlServerDsc.Common.Tests.ps1 @@ -1451,7 +1451,7 @@ InModuleScope 'SqlServerDsc.Common' { Mock -CommandName New-InvalidOperationException -MockWith $mockThrowLocalizedMessage -Verifiable } - $queryParams = @{ + $queryParameters = @{ ServerName = 'Server1' InstanceName = 'MSSQLSERVER' Database = 'master' @@ -1471,17 +1471,22 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should execute the query silently' { - $queryParams.Query = "EXEC sp_configure 'show advanced option', '1'" - $mockExpectedQuery = $queryParams.Query.Clone() + $queryParameters.Query = "EXEC sp_configure 'show advanced option', '1'" + $mockExpectedQuery = $queryParameters.Query.Clone() - { Invoke-Query @queryParams } | Should -Not -Throw + { Invoke-Query @queryParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { + # Should not be called with a login type. + $PSBoundParameters.ContainsKey('LoginType') -eq $false + } -Scope It -Times 1 -Exactly } It 'Should throw the correct error, ExecuteNonQueryFailed, when executing the query fails' { - $queryParams.Query = 'BadQuery' + $queryParameters.Query = 'BadQuery' - { Invoke-Query @queryParams } | Should -Throw ( - $script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database + { Invoke-Query @queryParameters } | Should -Throw ( + $script:localizedData.ExecuteNonQueryFailed -f $queryParameters.Database ) } @@ -1504,30 +1509,60 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should execute the query silently and redact text in the verbose output' { - $queryParams.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" - $mockExpectedQuery = $queryParams.Query.Clone() + $queryParameters.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" + $mockExpectedQuery = $queryParameters.Query.Clone() # The `Secret PassPhrase` is using the casing like this to test case-insensitive replace. - { Invoke-Query @queryParams -RedactText @('Pa\sSw0rd1','Secret PassPhrase') } | Should -Not -Throw + { Invoke-Query @queryParameters -RedactText @('Pa\sSw0rd1','Secret PassPhrase') } | Should -Not -Throw } } } - Context 'When executing a query with results' { + Context 'When executing a query with no results using Windows impersonation' { + It 'Should execute the query silently' { + $testParameters = $queryParameters.Clone() + $testParameters.LoginType = 'WindowsUser' + $testParameters.Query = "EXEC sp_configure 'show advanced option', '1'" + $mockExpectedQuery = $testParameters.Query.Clone() + + { Invoke-Query @testParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { + $LoginType -eq 'WindowsUser' + } -Scope It -Times 1 -Exactly + } + } + + Context 'when executing a query with no results using SQL impersonation' { + It 'Should execute the query silently' { + $testParameters = $queryParameters.Clone() + $testParameters.LoginType = 'SqlLogin' + $testParameters.Query = "EXEC sp_configure 'show advanced option', '1'" + $mockExpectedQuery = $testParameters.Query.Clone() + + { Invoke-Query @testParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Connect-SQL -ParameterFilter { + $LoginType -eq 'SqlLogin' + } -Scope It -Times 1 -Exactly + } + } + + Context 'when executing a query with results' { It 'Should execute the query and return a result set' { - $queryParams.Query = 'SELECT name FROM sys.databases' - $mockExpectedQuery = $queryParams.Query.Clone() + $queryParameters.Query = 'SELECT name FROM sys.databases' + $mockExpectedQuery = $queryParameters.Query.Clone() - Invoke-Query @queryParams -WithResults | Should -Not -BeNullOrEmpty + Invoke-Query @queryParameters -WithResults | Should -Not -BeNullOrEmpty Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly } It 'Should throw the correct error, ExecuteQueryWithResultsFailed, when executing the query fails' { - $queryParams.Query = 'BadQuery' + $queryParameters.Query = 'BadQuery' - { Invoke-Query @queryParams -WithResults } | Should -Throw ( - $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + { Invoke-Query @queryParameters -WithResults } | Should -Throw ( + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParameters.Database ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 1 -Exactly @@ -1552,11 +1587,11 @@ InModuleScope 'SqlServerDsc.Common' { } It 'Should execute the query silently and redact text in the verbose output' { - $queryParams.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" - $mockExpectedQuery = $queryParams.Query.Clone() + $queryParameters.Query = "select * from MyTable where password = 'Pa\ssw0rd1' and password = 'secret passphrase'" + $mockExpectedQuery = $queryParameters.Query.Clone() # The `Secret PassPhrase` is using the casing like this to test case-insensitive replace. - { Invoke-Query @queryParams -RedactText @('Pa\sSw0rd1','Secret PassPhrase') -WithResults } | Should -Not -Throw + { Invoke-Query @queryParameters -RedactText @('Pa\sSw0rd1','Secret PassPhrase') -WithResults } | Should -Not -Throw } } } @@ -1576,7 +1611,7 @@ InModuleScope 'SqlServerDsc.Common' { $queryParametersWithSMO.Query = 'BadQuery' { Invoke-Query @queryParametersWithSMO } | Should -Throw ( - $script:localizedData.ExecuteNonQueryFailed -f $queryParams.Database + $script:localizedData.ExecuteNonQueryFailed -f $queryParameters.Database ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly @@ -1597,7 +1632,7 @@ InModuleScope 'SqlServerDsc.Common' { $queryParametersWithSMO.Query = 'BadQuery' { Invoke-Query @queryParametersWithSMO -WithResults } | Should -Throw ( - $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParameters.Database ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly @@ -1620,7 +1655,7 @@ InModuleScope 'SqlServerDsc.Common' { { $mockSMOServer | Invoke-Query -Query $mockQuery -Database master -WithResults } | Should -Throw ( - $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParams.Database + $script:localizedData.ExecuteQueryWithResultsFailed -f $queryParameters.Database ) Assert-MockCalled -CommandName Connect-SQL -Scope It -Times 0 -Exactly @@ -2508,27 +2543,56 @@ InModuleScope 'SqlServerDsc.Common' { } Context 'When connecting to the named instance using Windows Authentication impersonation' { - It 'Should return the correct service instance' { + BeforeAll { $mockExpectedDatabaseEngineServer = $env:COMPUTERNAME $mockExpectedDatabaseEngineInstance = $mockInstanceName + } - $testParameters = @{ - ServerName = $mockExpectedDatabaseEngineServer - InstanceName = $mockExpectedDatabaseEngineInstance - SetupCredential = $mockWinCredential - LoginType = 'WindowsUser' + Context 'When using the default login type' { + BeforeAll { + $testParameters = @{ + ServerName = $mockExpectedDatabaseEngineServer + InstanceName = $mockExpectedDatabaseEngineInstance + SetupCredential = $mockWinCredential + } } - $databaseEngineServerObject = Connect-SQL @testParameters - $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" - $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true - $databaseEngineServerObject.ConnectionContext.ConnectAsUserPassword | Should -BeExactly $mockWinCredential.GetNetworkCredential().Password - $databaseEngineServerObject.ConnectionContext.ConnectAsUserName | Should -BeExactly $mockWinCredential.GetNetworkCredential().UserName - $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true - $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $true + It 'Should return the correct service instance' { + $databaseEngineServerObject = Connect-SQL @testParameters + $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" + $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true + $databaseEngineServerObject.ConnectionContext.ConnectAsUserPassword | Should -BeExactly $mockWinCredential.GetNetworkCredential().Password + $databaseEngineServerObject.ConnectionContext.ConnectAsUserName | Should -BeExactly $mockWinCredential.GetNetworkCredential().UserName + $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true + $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $true - Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` - -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` + -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter + } + } + + Context 'When using the default login type' { + BeforeAll { + $testParameters = @{ + ServerName = $mockExpectedDatabaseEngineServer + InstanceName = $mockExpectedDatabaseEngineInstance + SetupCredential = $mockWinCredential + LoginType = 'WindowsUser' + } + } + + It 'Should return the correct service instance' { + $databaseEngineServerObject = Connect-SQL @testParameters + $databaseEngineServerObject.ConnectionContext.ServerInstance | Should -BeExactly "$mockExpectedDatabaseEngineServer\$mockExpectedDatabaseEngineInstance" + $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true + $databaseEngineServerObject.ConnectionContext.ConnectAsUserPassword | Should -BeExactly $mockWinCredential.GetNetworkCredential().Password + $databaseEngineServerObject.ConnectionContext.ConnectAsUserName | Should -BeExactly $mockWinCredential.GetNetworkCredential().UserName + $databaseEngineServerObject.ConnectionContext.ConnectAsUser | Should -Be $true + $databaseEngineServerObject.ConnectionContext.LoginSecure | Should -Be $true + + Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` + -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter + } } } @@ -2550,25 +2614,6 @@ InModuleScope 'SqlServerDsc.Common' { } } - Context 'When the logon type is WindowsUser or SqlLogin, but not credentials were passed' { - It 'Should throw the correct error' { - $mockExpectedDatabaseEngineServer = 'TestServer' - $mockExpectedDatabaseEngineInstance = 'MSSQLSERVER' - - $connectSqlParameters = @{ - ServerName = $mockExpectedDatabaseEngineServer - LoginType = 'WindowsUser' - } - - $mockCorrectErrorMessage = $script:localizedData.CredentialsNotSpecified -f $connectSqlParameters.LoginType - - { Connect-SQL @connectSqlParameters } | Should -Throw $mockCorrectErrorMessage - - Assert-MockCalled -CommandName New-Object -Exactly -Times 1 -Scope It ` - -ParameterFilter $mockNewObject_MicrosoftDatabaseEngine_ParameterFilter - } - } - Assert-VerifiableMock } From 9a5268ef5cff9dd93bc2bf0d2bf1d387ae2101e0 Mon Sep 17 00:00:00 2001 From: Jess Pomfret Date: Thu, 1 Aug 2019 13:48:53 -0400 Subject: [PATCH 12/13] SqlAgentFailsafe: Adding resource to manage Agent Failsafe Operator (#1414) - Changes to SqlServerDsc - New DSC resource SqlAgentFailsafe --- CHANGELOG.md | 1 + .../MSFT_SqlAgentFailsafe.psm1 | 290 +++++++++++++ .../MSFT_SqlAgentFailsafe.schema.mof | 9 + .../en-US/MSFT_SqlAgentFailsafe.strings.psd1 | 13 + .../SqlAgentFailsafe/1-AddFailsafe.ps1 | 21 + .../SqlAgentFailsafe/2-RemoveFailsafe.ps1 | 20 + README.md | 36 +- ...SFT_SqlAgentFailsafe.Integration.Tests.ps1 | 147 +++++++ .../MSFT_SqlAgentFailsafe.config.ps1 | 86 ++++ Tests/Unit/MSFT_SqlAgentFailsafe.Tests.ps1 | 387 ++++++++++++++++++ 10 files changed, 1008 insertions(+), 2 deletions(-) create mode 100644 DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.psm1 create mode 100644 DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.schema.mof create mode 100644 DSCResources/MSFT_SqlAgentFailsafe/en-US/MSFT_SqlAgentFailsafe.strings.psd1 create mode 100644 Examples/Resources/SqlAgentFailsafe/1-AddFailsafe.ps1 create mode 100644 Examples/Resources/SqlAgentFailsafe/2-RemoveFailsafe.ps1 create mode 100644 Tests/Integration/MSFT_SqlAgentFailsafe.Integration.Tests.ps1 create mode 100644 Tests/Integration/MSFT_SqlAgentFailsafe.config.ps1 create mode 100644 Tests/Unit/MSFT_SqlAgentFailsafe.Tests.ps1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 217c7112a..4bbcbc024 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased - Changes to SqlServerDsc + - New DSC resource SqlAgentFailsafe - New DSC resource SqlDatabaseUser ([issue #846](https://github.com/PowerShell/SqlServerDsc/issues/846)). - Adds ability to create database users with more fine-grained control, e.g. re-mapping of orphaned logins or a different login. Supports diff --git a/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.psm1 b/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.psm1 new file mode 100644 index 000000000..7e1bcc8e6 --- /dev/null +++ b/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.psm1 @@ -0,0 +1,290 @@ +$script:resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent +$script:modulesFolderPath = Join-Path -Path $script:resourceModulePath -ChildPath 'Modules' + +$script:resourceHelperModulePath = Join-Path -Path $script:modulesFolderPath -ChildPath 'SqlServerDsc.Common' +Import-Module -Name (Join-Path -Path $script:resourceHelperModulePath -ChildPath 'SqlServerDsc.Common.psm1') + +$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_SqlAgentFailsafe' + +<# + .SYNOPSIS + This function gets the SQL Agent Failsafe Operator. + + .PARAMETER Name + The name of the SQL Agent Failsafe Operator. + + .PARAMETER ServerName + The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME. + + .PARAMETER InstanceName + The name of the SQL instance to be configured. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ServerName = $env:COMPUTERNAME, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $InstanceName + ) + + $returnValue = @{ + Name = $null + Ensure = 'Absent' + ServerName = $ServerName + InstanceName = $InstanceName + NotificationMethod = $null + } + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + if ($sqlServerObject) + { + Write-Verbose -Message ( + $script:localizedData.GetSqlAgentFailsafe + ) + + $sqlAlertSystemObject = $sqlServerObject.JobServer.AlertSystem | Where-Object -FilterScript {$_.FailSafeOperator -eq $Name} + + if ($sqlAlertSystemObject) + { + Write-Verbose -Message ( + $script:localizedData.SqlFailsafePresent ` + -f $Name + ) + + $returnValue['Ensure'] = 'Present' + $returnValue['Name'] = $sqlAlertSystemObject.FailSafeOperator + $returnValue['NotificationMethod'] = $sqlAlertSystemObject.NotificationMethod + } + else + { + Write-Verbose -Message ( + $script:localizedData.SqlFailsafeAbsent ` + -f $Name + ) + } + } + else + { + $errorMessage = $script:localizedData.ConnectServerFailed -f $ServerName, $InstanceName + New-InvalidOperationException -Message $errorMessage + } + + return $returnValue +} + +<# + .SYNOPSIS + This function sets the SQL Agent Failsafe Operator. + + .PARAMETER Ensure + Specifies if the SQL Agent Failsafe Operator should be present or absent. Default is Present + + .PARAMETER Name + The name of the SQL Agent Failsafe Operator. + + .PARAMETER ServerName + The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME. + + .PARAMETER InstanceName + The name of the SQL instance to be configured. + + .PARAMETER NotificationMethod + The method of notification for the Failsafe Operator. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter()] + [ValidateSet('Present', 'Absent')] + [ValidateNotNullOrEmpty()] + [System.String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter()] + [System.String] + $ServerName = $env:COMPUTERNAME, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $InstanceName, + + [Parameter()] + [ValidateSet('None', 'NotifyEmail', 'Pager', 'NetSend', 'NotifyAll')] + [System.String] + $NotificationMethod + ) + + $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName + + if ($sqlServerObject) + { + switch ($Ensure) + { + 'Present' + { + try + { + $sqlAlertSystemObject = $sqlServerObject.JobServer.AlertSystem + + Write-Verbose -Message ( + $script:localizedData.UpdateFailsafeOperator ` + -f $Name + ) + + $sqlAlertSystemObject.FailSafeOperator = $Name + if ($PSBoundParameters.ContainsKey('NotificationMethod')) + { + Write-Verbose -Message ( + $script:localizedData.UpdateNotificationMethod ` + -f $NotificationMethod, $Name + ) + + $sqlAlertSystemObject.NotificationMethod = $NotificationMethod + } + + $sqlAlertSystemObject.Alter() + } + catch + { + $errorMessage = $script:localizedData.UpdateFailsafeOperatorError -f $Name, $ServerName, $InstanceName + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + + 'Absent' + { + try + { + $sqlAlertSystemObject = $sqlServerObject.JobServer.AlertSystem + + Write-Verbose -Message ( + $script:localizedData.RemoveFailsafeOperator + ) + + $sqlAlertSystemObject.FailSafeOperator = $null + $sqlAlertSystemObject.NotificationMethod = 'None' + $sqlAlertSystemObject.Alter() + } + catch + { + $errorMessage = $script:localizedData.UpdateFailsafeOperatorError -f $Name, $ServerName, $InstanceName + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + } + } + else + { + $errorMessage = $script:localizedData.ConnectServerFailed -f $ServerName, $InstanceName + New-InvalidOperationException -Message $errorMessage + } +} + +<# + .SYNOPSIS + This function tests the SQL Agent Failsafe Operator. + + .PARAMETER Ensure + Specifies if the SQL Agent Failsafe Operator should be present or absent. Default is Present + + .PARAMETER Name + The name of the SQL Agent Failsafe Operator. + + .PARAMETER ServerName + The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME. + + .PARAMETER InstanceName + The name of the SQL instance to be configured. + + .PARAMETER NotificationMethod + The method of notification for the Failsafe Operator. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter()] + [ValidateSet('Present', 'Absent')] + [System.String] + $Ensure = 'Present', + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $Name, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [System.String] + $InstanceName, + + [Parameter()] + [System.String] + $ServerName = $env:COMPUTERNAME, + + [Parameter()] + [ValidateSet('None', 'NotifyEmail', 'Pager', 'NetSend', 'NotifyAll')] + [System.String] + $NotificationMethod + ) + + $getTargetResourceParameters = @{ + ServerName = $ServerName + InstanceName = $InstanceName + Name = $Name + } + + $returnValue = $false + + $getTargetResourceResult = Get-TargetResource @getTargetResourceParameters + + Write-Verbose -Message ( + $script:localizedData.TestingConfiguration + ) + + if ($Ensure -eq 'Present') + { + $returnValue = Test-DscParameterState ` + -CurrentValues $getTargetResourceResult ` + -DesiredValues $PSBoundParameters ` + -ValuesToCheck @( + 'FailsafeOperator' + 'NotificationMethod' + ) + } + else + { + if ($Ensure -eq $getTargetResourceResult.Ensure) + { + $returnValue = $true + } + } + + return $returnValue +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.schema.mof b/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.schema.mof new file mode 100644 index 000000000..d3c8f80c1 --- /dev/null +++ b/DSCResources/MSFT_SqlAgentFailsafe/MSFT_SqlAgentFailsafe.schema.mof @@ -0,0 +1,9 @@ +[ClassVersion("1.0.0.0"), FriendlyName("SqlAgentFailsafe")] +class MSFT_SqlAgentFailsafe : OMI_BaseResource +{ + [Required, Description("The name of the SQL Agent Failsafe Operator.")] String Name; + [Write, Description("Specifies if the SQL Agent Failsafe Operator should be present or absent. Default is Present."), ValueMap{"Present","Absent"}, Values{"Present","Absent"}] String Ensure; + [Write, Description("The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME.")] String ServerName; + [Key, Description("The name of the SQL instance to be configured.")] String InstanceName; + [Write, Description("The method of notification for the Failsafe Operator."), ValueMap{"None","NotifyEmail","Pager","NetSend","NotifyAll"}, Values{"None","NotifyEmail","Pager","NetSend","NotifyAll"}] String NotificationMethod; +}; diff --git a/DSCResources/MSFT_SqlAgentFailsafe/en-US/MSFT_SqlAgentFailsafe.strings.psd1 b/DSCResources/MSFT_SqlAgentFailsafe/en-US/MSFT_SqlAgentFailsafe.strings.psd1 new file mode 100644 index 000000000..030969a3b --- /dev/null +++ b/DSCResources/MSFT_SqlAgentFailsafe/en-US/MSFT_SqlAgentFailsafe.strings.psd1 @@ -0,0 +1,13 @@ +# Localized resources for MSFT_SqlServerAgentFailsafe + +ConvertFrom-StringData @' + GetSqlAgentFailsafe = Getting SQL Agent Failsafe Operator. + SqlFailsafePresent = SQL Agent Failsafe Operator '{0}' is present. + SqlFailsafeAbsent = SQL Agent Failsafe Operator '{0}' is absent. + UpdateFailsafeOperator = Updating Sql Agent Failsafe Operator to '{0}'. + UpdateNotificationMethod = Updating notification method to '{0}' for SQL Agent Failsafe Operator '{1}'. + UpdateFailsafeOperatorError = Unable to update Sql Agent Failsafe Operator '{0}' on {1}\\{2}. + RemoveFailsafeOperator = Removing Sql Agent Failsafe Operator. + ConnectServerFailed = Unable to connect to {0}\\{1}. + TestingConfiguration = Determines if the SQL Agent Failsafe Operator is in the desired state. +'@ diff --git a/Examples/Resources/SqlAgentFailsafe/1-AddFailsafe.ps1 b/Examples/Resources/SqlAgentFailsafe/1-AddFailsafe.ps1 new file mode 100644 index 000000000..a95dcc4e7 --- /dev/null +++ b/Examples/Resources/SqlAgentFailsafe/1-AddFailsafe.ps1 @@ -0,0 +1,21 @@ +<# + .EXAMPLE + This example shows how to ensure that the SQL Agent + Failsafe Operator 'FailsafeOp' exists with the correct Notification. +#> + +Configuration Example +{ + + Import-DscResource -ModuleName SqlServerDsc + + node localhost { + SqlAgentFailsafe Add_FailsafeOp { + Ensure = 'Present' + Name = 'FailsafeOp' + ServerName = 'TestServer' + InstanceName = 'MSSQLServer' + NotificationMethod = 'NotifyEmail' + } + } +} diff --git a/Examples/Resources/SqlAgentFailsafe/2-RemoveFailsafe.ps1 b/Examples/Resources/SqlAgentFailsafe/2-RemoveFailsafe.ps1 new file mode 100644 index 000000000..b9232e2d0 --- /dev/null +++ b/Examples/Resources/SqlAgentFailsafe/2-RemoveFailsafe.ps1 @@ -0,0 +1,20 @@ +<# + .EXAMPLE + This example shows how to ensure that the SQL Agent + failsafe operator FailsafeOp does not exist. +#> + +Configuration Example +{ + + Import-DscResource -ModuleName SqlServerDsc + + node localhost { + SqlAgentFailsafe Remove_FailsafeOp { + Ensure = 'Absent' + Name = 'FailsafeOp' + ServerName = 'TestServer' + InstanceName = 'MSSQLServer' + } + } +} diff --git a/README.md b/README.md index 35ab23ff5..0d1927a94 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,8 @@ A full list of changes in each version can be found in the [change log](CHANGELO to manage the database membership in Availability Groups. * [**SqlAgentAlert**](#sqlagentalert) resource to manage SQL Agent Alerts. +* [**SqlAgentFailsafe**](#sqlagentfailsafe) + resource to manage SQL Agent Failsafe Operator. * [**SqlAgentOperator**](#sqlagentoperator) resource to manage SQL Agent Operators. * [**SqlAGListener**](#sqlaglistener) @@ -336,7 +338,7 @@ update the severity or message id. * **`[String]` Name** _(Key)_: The name of the SQL Agent Alert. * **`[String]` Ensure** _(Write)_: Specifies if the SQL Agent Alert should be present or absent. Default is Present. { *Present* | Absent } -* **`[String]` ServerName** _(Key)_: The host name of the SQL Server to be +* **`[String]` ServerName** _(Write)_: The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME. * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. * **`[String]` Severity** _(Write)_: The severity of the SQL Agent Alert. @@ -351,6 +353,36 @@ update the severity or message id. All issues are not listed here, see [here for all open issues](https://github.com/PowerShell/SqlServerDsc/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+SqlAgentAlert). +### SqlAgentFailsafe + +This resource is used to add/remove the SQL Agent Failsafe Operator. You can also +update the Notification method. + +#### Requirements + +* Target machine must be running Windows Server 2008 R2 or later. +* Target machine must be running SQL Server Database Engine 2008 or later. + +#### Parameters + +* **`[String]` Name** _(Key)_: The name of the SQL Agent Failsafe Operator. +* **`[String]` Ensure** _(Write)_: Specifies if the SQL Agent Failsafe Operator + should be present or absent. Default is Present. { *Present* | Absent } +* **`[String]` ServerName** _(Write)_: The host name of the SQL Server to be + configured. Default is $env:COMPUTERNAME. +* **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. +* **`[String]` NotificationMethod** _(Write)_: The method of notification for the + Failsafe Operator. The default is none. + +#### Examples + +* [Add SQL Agent Failsafe Operator](/Examples/Resources/SqlAgentFailsafe/1-AddFailsafe.ps1) +* [Remove SQL Agent Failsafe Operator](/Examples/Resources/SqlAgentFailsafe/2-RemoveFailsafe.ps1) + +#### Known issues + +All issues are not listed here, see [here for all open issues](https://github.com/PowerShell/SqlServerDsc/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+SqlAgentFailsafe). + ### SqlAgentOperator This resource is used to add/remove SQL Agent Operators. You can also update @@ -366,7 +398,7 @@ the operators email address. * **`[String]` Name** _(Key)_: The name of the SQL Agent Operator. * **`[String]` Ensure** _(Write)_: Specifies if the SQL Agent Operator should be present or absent. Default is Present. { *Present* | Absent } -* **`[String]` ServerName** _(Key)_: The host name of the SQL Server to be +* **`[String]` ServerName** _(Write)_: The host name of the SQL Server to be configured. Default is $env:COMPUTERNAME. * **`[String]` InstanceName** _(Key)_: The name of the SQL instance to be configured. * **`[String]` EmailAddress** _(Write)_: The email address to be used for diff --git a/Tests/Integration/MSFT_SqlAgentFailsafe.Integration.Tests.ps1 b/Tests/Integration/MSFT_SqlAgentFailsafe.Integration.Tests.ps1 new file mode 100644 index 000000000..0c9f2d79a --- /dev/null +++ b/Tests/Integration/MSFT_SqlAgentFailsafe.Integration.Tests.ps1 @@ -0,0 +1,147 @@ +# This is used to make sure the integration test run in the correct order. +[Microsoft.DscResourceKit.IntegrationTest(OrderNumber = 2)] +param() + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Integration' -Category @('Integration_SQL2016','Integration_SQL2017')) +{ + return +} + +$script:dscModuleName = 'SqlServerDsc' +$script:dscResourceFriendlyName = 'SqlAgentFailsafe' +$script:dscResourceName = "MSFT_$($script:dscResourceFriendlyName)" + +##region HEADER +# Integration Test Template Version: 1.3.2 +[String] $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath 'DscResource.Tests')) +} + +Import-Module -Name (Join-Path -Path $script:moduleRoot -ChildPath (Join-Path -Path 'DSCResource.Tests' -ChildPath 'TestHelper.psm1')) -Force +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Integration +#endregion + +# Using try/finally to always cleanup. +try +{ + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + $configurationName = "$($script:dscResourceName)_Add_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.Name + $resourceCurrentState.NotificationMethod | Should -Be $ConfigurationData.AllNodes.NotificationMethod + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + $configurationName = "$($script:dscResourceName)_Remove_Config" + + Context ('When using configuration {0}' -f $configurationName) { + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + # The variable $ConfigurationData was dot-sourced above. + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -BeNullOrEmpty + $resourceCurrentState.NotificationMethod | Should -BeNullOrEmpty + } + + It 'Should return $true when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + } +} +finally +{ + #region FOOTER + + Restore-TestEnvironment -TestEnvironment $TestEnvironment + + #endregion +} diff --git a/Tests/Integration/MSFT_SqlAgentFailsafe.config.ps1 b/Tests/Integration/MSFT_SqlAgentFailsafe.config.ps1 new file mode 100644 index 000000000..f9aa90d47 --- /dev/null +++ b/Tests/Integration/MSFT_SqlAgentFailsafe.config.ps1 @@ -0,0 +1,86 @@ +#region HEADER +# Integration Test Config Template Version: 1.2.0 +#endregion + +$configFile = [System.IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, 'json') +if (Test-Path -Path $configFile) +{ + <# + Allows reading the configuration data from a JSON file, + for real testing scenarios outside of the CI. + #> + $ConfigurationData = Get-Content -Path $configFile | ConvertFrom-Json +} +else +{ + $ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + + UserName = "$env:COMPUTERNAME\SqlInstall" + Password = 'P@ssw0rd1' + + ServerName = $env:COMPUTERNAME + InstanceName = 'DSCSQLTEST' + + Name = 'FailsafeOp' + NotificationMethod = 'NotifyEmail' + + CertificateFile = $env:DscPublicCertificatePath + } + ) + } +} + +<# + .SYNOPSIS + Adds a SQL Agent Failsafe Operator. +#> +Configuration MSFT_SqlAgentFailsafe_Add_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlAgentFailsafe 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.Name + NotificationMethod = $Node.NotificationMethod + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + +<# + .SYNOPSIS + Removes a SQL Agent Failsafe Operator. +#> +Configuration MSFT_SqlAgentFailsafe_Remove_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlAgentFailsafe 'Integration_Test' + { + Ensure = 'Absent' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.Name + NotificationMethod = $Node.NotificationMethod + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + + diff --git a/Tests/Unit/MSFT_SqlAgentFailsafe.Tests.ps1 b/Tests/Unit/MSFT_SqlAgentFailsafe.Tests.ps1 new file mode 100644 index 000000000..154d58385 --- /dev/null +++ b/Tests/Unit/MSFT_SqlAgentFailsafe.Tests.ps1 @@ -0,0 +1,387 @@ +<# + .SYNOPSIS + Automated unit test for MSFT_SqlAgentFailsafe DSC resource. + + .NOTES + To run this script locally, please make sure to first run the bootstrap + script. Read more at + https://github.com/PowerShell/SqlServerDsc/blob/dev/CONTRIBUTING.md#bootstrap-script-assert-testenvironment +#> + +Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') + +if (Test-SkipContinuousIntegrationTask -Type 'Unit') +{ + return +} + +$script:dscModuleName = 'SqlServerDsc' +$script:dscResourceName = 'MSFT_SqlAgentFailsafe' + +#region HEADER + +# Unit Test Template Version: 1.2.0 +$script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +if ( (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests'))) -or ` + (-not (Test-Path -Path (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1'))) ) +{ + & git @('clone', 'https://github.com/PowerShell/DscResource.Tests.git', (Join-Path -Path $script:moduleRoot -ChildPath '\DSCResource.Tests\')) +} + +Import-Module (Join-Path -Path $script:moduleRoot -ChildPath 'DSCResource.Tests\TestHelper.psm1') -Force + +$TestEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -TestType Unit + +#endregion HEADER + +function Invoke-TestSetup +{ + # Loading mocked classes +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $TestEnvironment +} + +# Begin Testing +try +{ + Invoke-TestSetup + + InModuleScope $script:dscResourceName { + $mockServerName = 'localhost' + $mockInstanceName = 'MSSQLSERVER' + $mockSqlAgentFailsafeName = 'FailsafeOp' + $mockSqlAgentFailsafeNotification = 'NotifyEmail' + $mockInvalidOperationForAlterMethod = $false + + # Default parameters that are used for the It-blocks + $mockDefaultParameters = @{ + InstanceName = $mockInstanceName + ServerName = $mockServerName + } + + #region Function mocks + $mockConnectSQL = { + return @( + ( + New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name InstanceName -Value $mockInstanceName -PassThru | + Add-Member -MemberType NoteProperty -Name ComputerNamePhysicalNetBIOS -Value $mockServerName -PassThru | + Add-Member -MemberType ScriptProperty -Name JobServer -Value { + return (New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name Name -Value $mockServerName -PassThru | + Add-Member -MemberType ScriptProperty -Name AlertSystem -Value { + return ( New-Object -TypeName Object | + Add-Member -MemberType NoteProperty -Name FailSafeOperator -Value $mockSqlAgentFailsafeName -PassThru -Force | + Add-Member -MemberType NoteProperty -Name NotificationMethod -Value $mockSqlAgentFailsafeNotification -PassThru -Force | + Add-Member -MemberType ScriptMethod -Name Alter -Value { + if ($mockInvalidOperationForAlterMethod) + { + throw 'Mock Alter Method was called with invalid operation.' + } + } -PassThru -Force + ) + } -PassThru + ) + } -PassThru -Force + ) + ) + } + #endregion + + Describe "MSFT_SqlAgentFailsafe\Get-TargetResource" -Tag 'Get' { + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + } + + Context 'When Connect-SQL returns nothing' { + It 'Should throw the correct error' { + Mock -CommandName Connect-SQL -MockWith { + return $null + } + + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + } + + { Get-TargetResource @testParameters } | Should -Throw ($script:localizedData.ConnectServerFailed -f $testParameters.ServerName, $testParameters.InstanceName) + } + } + + Context 'When the system is not in the desired state' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'DifferentOp' + } + + It 'Should return the state as absent' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Absent' + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context + } + } + + Context 'When the system is in the desired state for a sql agent failsafe operator' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + } + + It 'Should return the state as present' { + $result = Get-TargetResource @testParameters + $result.Ensure | Should -Be 'Present' + } + + It 'Should return the same values as passed as parameters' { + $result = Get-TargetResource @testParameters + $result.ServerName | Should -Be $testParameters.ServerName + $result.InstanceName | Should -Be $testParameters.InstanceName + $result.Name | Should -Be $testParameters.Name + $result.NotificationMethod | Should -Be $mockSqlAgentFailsafeNotification + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context + } + } + Assert-VerifiableMock + } + + Describe "MSFT_SqlAgentFailsafe\Test-TargetResource" -Tag 'Test' { + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + } + + Context 'When the system is not in the desired state and Ensure is set to Present' { + It 'Should return the state as false when desired sql agent failsafe operator does not exist' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'MissingFailsafe' + NotificationMethod = 'NotifyEmail' + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + } + + It 'Should return the state as false when desired sql agent failsafe operator exists but has the incorrect notification method' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Present' + NotificationMethod = 'Pager' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context + } + } + + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should return the state as false when non-desired sql agent failsafe operator exists' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Absent' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $false + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context + } + } + + Context 'When the system is in the desired state and Ensure is set to Present' { + It 'Should return the state as true when desired sql agent failsafe operator exist' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Present' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + } + + It 'Should return the state as true when desired sql agent failsafe operator exists and has the correct notification method' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Present' + NotificationMethod = 'NotifyEmail' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context + } + } + + Context 'When the system is in the desired state and Ensure is set to Absent' { + It 'Should return the state as true when desired sql agent failsafe operator does not exist' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'NotFailsafe' + Ensure = 'Absent' + } + + $result = Test-TargetResource @testParameters + $result | Should -Be $true + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 1 -Scope Context + } + } + Assert-VerifiableMock + } + + Describe "MSFT_SqlAgentFailsafe\Set-TargetResource" -Tag 'Set' { + BeforeEach { + Mock -CommandName Connect-SQL -MockWith $mockConnectSQL -Verifiable + } + + Context 'When Connect-SQL returns nothing' { + It 'Should throw the correct error' { + Mock -CommandName Get-TargetResource -Verifiable + Mock -CommandName Connect-SQL -MockWith { + return $null + } + + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Present' + } + + { Set-TargetResource @testParameters } | Should -Throw ($script:localizedData.ConnectServerFailed -f $testParameters.ServerName, $testParameters.InstanceName) + } + } + + Context 'When the system is not in the desired state and Ensure is set to Present'{ + It 'Should not throw when adding the sql agent failsafe operator' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'Newfailsafe' + Ensure = 'Present' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + } + + It 'Should not throw when changing the severity' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'Newfailsafe' + Ensure = 'Present' + NotificationMethod = 'Pager' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + } + + It 'Should throw when notification method is not valid' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'Newfailsafe' + Ensure = 'Present' + NotificationMethod = 'Letter' + } + + { Set-TargetResource @testParameters } | Should -Throw + } + + $mockInvalidOperationForAlterMethod = $true + It 'Should throw the correct error when Alter() method was called with invalid operation' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'NewFailsafe' + Ensure = 'Present' + } + + $errorMessage = ($script:localizedData.UpdateFailsafeOperatorError -f $testParameters.Name, $testParameters.ServerName, $testParameters.InstanceName) + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 3 -Scope Context + } + } + + Context 'When the system is not in the desired state and Ensure is set to Absent' { + It 'Should not throw when removing the sql agent failsafe operator' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Absent' + } + + { Set-TargetResource @testParameters } | Should -Not -Throw + } + + $mockInvalidOperationForAlterMethod = $true + It 'Should throw the correct error when Alter() method was called with invalid operation' { + $testParameters = $mockDefaultParameters + + $testParameters += @{ + Name = 'FailsafeOp' + Ensure = 'Absent' + } + + $errorMessage = ($script:localizedData.UpdateFailsafeOperatorError -f $testParameters.Name, $testParameters.ServerName, $testParameters.InstanceName) + { Set-TargetResource @testParameters } | Should -Throw $errorMessage + } + + It 'Should call the mock function Connect-SQL' { + Assert-MockCalled -CommandName Connect-SQL -Exactly -Times 2 -Scope Context + } + } + Assert-VerifiableMock + } + } +} +finally +{ + Invoke-TestCleanup +} From 2337c7cbaa9c47d5bf82802ec77167922ec892b6 Mon Sep 17 00:00:00 2001 From: Michael Greene Date: Wed, 7 Aug 2019 13:34:22 -0700 Subject: [PATCH 13/13] Releasing version 13.1.0.0 --- CHANGELOG.md | 2 + SqlServerDsc.psd1 | 127 +++++++++++++++++++++------------------------- 2 files changed, 59 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bbcbc024..3f75440be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +## 13.1.0.0 + - Changes to SqlServerDsc - New DSC resource SqlAgentFailsafe - New DSC resource SqlDatabaseUser ([issue #846](https://github.com/PowerShell/SqlServerDsc/issues/846)). diff --git a/SqlServerDsc.psd1 b/SqlServerDsc.psd1 index 65bd89b61..72088abd7 100644 --- a/SqlServerDsc.psd1 +++ b/SqlServerDsc.psd1 @@ -1,6 +1,6 @@ @{ # Version number of this module. - moduleVersion = '13.0.0.0' + moduleVersion = '13.1.0.0' # ID used to uniquely identify this module GUID = '693ee082-ed36-45a7-b490-88b07c86b42f' @@ -50,77 +50,63 @@ # ReleaseNotes of this module ReleaseNotes = '- Changes to SqlServerDsc - - Added SqlAgentAlert resource. - - Opt-in to the common test "Common Test - Validation Localization". - - Opt-in to the common test "Common Test - Flagged Script Analyzer Rules" - ([issue 1101](https://github.com/PowerShell/SqlServerDsc/issues/1101)). - - Removed the helper function `New-TerminatingError`, `New-WarningMessage` - and `New-VerboseMessage` in favor of the the new - [localization helper functions](https://github.com/PowerShell/DscResources/blob/master/StyleGuidelines.mdlocalization). - - Combine DscResource.LocalizationHelper and DscResource.Common into - SqlServerDsc.Common ([issue 1357](https://github.com/PowerShell/SqlServerDsc/issues/1357)). - - Update Assert-TestEnvironment.ps1 to not error if strict mode is enabled - and there are no missing dependencies ([issue 1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). -- Changes to SqlServerDsc.Common - - Added StatementTimeout to function "Connect-SQL" with default 600 seconds (10mins). - - Added StatementTimeout to function "Invoke-Query" with default 600 seconds (10mins) - ([issue 1358](https://github.com/PowerShell/SqlServerDsc/issues/1358)). + - New DSC resource SqlAgentFailsafe + - New DSC resource SqlDatabaseUser ([issue 846](https://github.com/PowerShell/SqlServerDsc/issues/846)). + - Adds ability to create database users with more fine-grained control, + e.g. re-mapping of orphaned logins or a different login. Supports + creating a user with or without login name, and database users mapped + to a certificate or asymmetric key. + - Changes to helper function Invoke-Query + - Fixes issues in [issue 1355](https://github.com/PowerShell/SqlServerDsc/issues/1355). + - Works together with Connect-SQL now. + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Can now pass in credentials. + - Can now pass in "Microsoft.SqlServer.Management.Smo.Server" object. + - Can also pipe in "Microsoft.SqlServer.Management.Smo.Server" object. + - Can pipe Connect-SQL | Invoke-Query. + - Added default values to Invoke-Query. + - Now it will output verbose messages of the query that is run, so it + not as quiet of what it is doing when a user asks for verbose output + ([issue 1404](https://github.com/PowerShell/SqlServerDsc/issues/1404)). + - It is possible to redact text in the verbose output by providing + strings in the new parameter `RedactText`. + - Minor style fixes in unit tests. - Changes to helper function Connect-SQL - - The function now make it more clear that when using the parameter - `SetupCredential` is impersonates that user, and by default it does - not impersonates a user but uses the credential that the resource - is run as (for example the built-in credential parameter - `PsDscRunAsCredential`). [@kungfu71186](https://github.com/kungfu71186) - - Added parameter alias `-DatabaseCredential` for the parameter - `-SetupCredential`. [@kungfu71186](https://github.com/kungfu71186) -- Changes to SqlAG - - Added en-US localization. -- Changes to SqlAGReplica - - Added en-US localization. - - Improved verbose message output when creating availability group replica, - removing a availability group replica, and joining the availability - group replica to the availability group. -- Changes to SqlAlwaysOnService - - Now outputs the correct verbose message when restarting the service. -- Changes to SqlServerMemory - - Now outputs the correct verbose messages when calculating the dynamic - memory, and when limiting maximum memory. -- Changes to SqlServerRole - - Now outputs the correct verbose message when the members of a role is - not in desired state. -- Changes to SqlAgentOperator - - Fix minor issue that when unable to connect to an instance. Instead - of showing a message saying that connect failed another unrelated - error message could have been shown, because of an error in the code. - - Fix typo in test it block. -- Changes to SqlDatabaseRole - - BREAKING CHANGE: Refactored to enable creation/deletion of the database role - itself as well as management of the role members. *Note that the resource no - longer adds database users.* ([issue 845](https://github.com/PowerShell/SqlServerDsc/issues/845), - [issue 847](https://github.com/PowerShell/SqlServerDsc/issues/847), - [issue 1252](https://github.com/PowerShell/SqlServerDsc/issues/1252), - [issue 1339](https://github.com/PowerShell/SqlServerDsc/issues/1339)). - [Paul Shamus @pshamus](https://github.com/pshamus) + - When impersonating WindowsUser credential use the NetworkCredential UserName. + - Added additional verbose logging. + - Connect-SQL now uses parameter sets to more intuitive evaluate that + the correct parameters are used in different scenarios + ([issue 1403](https://github.com/PowerShell/SqlServerDsc/issues/1403)). + - Changes to helper function Connect-SQLAnalysis + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Restart-SqlService + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Restart-ReportingServicesService + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Split-FullSqlInstanceName + - Parameters and function name changed to use correct casing. + - Changes to helper function Get-SqlInstanceMajorVersion + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Test-LoginEffectivePermissions + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). + - Changes to helper function Test-AvailabilityReplicaSeedingModeAutomatic + - Parameters now match that of Connect-SQL ([issue 1392](https://github.com/PowerShell/SqlServerDsc/issues/1392)). +- Changes to SqlServerSecureConnection + - Forced $Thumbprint to lowercase to fix [issue 1350](https://github.com/PowerShell/SqlServerDsc/issues/1350). + - Add parameter SuppressRestart with default value false. + This allows users to suppress restarts after changes have been made. + Changes will not take effect until the service has been restarted. - Changes to SqlSetup - - Add an Action type of "Upgrade". This will ask setup to do a version - upgrade where possible ([issue 1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). - - Fix an error when testing for DQS installation ([issue 1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). - - Changed the logic of how default value of FailoverClusterGroupName is - set since that was preventing the resource to be able to be debugged - ([issue 448](https://github.com/PowerShell/SqlServerDsc/issues/448)). - - Added RSInstallMode parameter ([issue 1163](https://github.com/PowerShell/SqlServerDsc/issues/1163)). -- Changes to SqlWindowsFirewall - - Where a version upgrade has changed paths for a database engine, the - existing firewall rule for that instance will be updated rather than - another one created ([issue 1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). - Other firewall rules can be fixed to work in the same way later. -- Changes to SqlAGDatabase - - Added new parameter "ReplaceExisting" with default false. - This allows forced restores when a database already exists on secondary. - - Added StatementTimeout to Invoke-Query to fix Issue1358 - - Fix issue where calling Get would return an error because the database - name list may have been returned as a string instead of as a string array - ([issue 1368](https://github.com/PowerShell/SqlServerDsc/issues/1368)). + - Correct minor style violation [issue 1387](https://github.com/PowerShell/SqlServerDsc/issues/1387). +- Changes to SqlDatabase + - Get-TargetResource now correctly return `$null` for the collation property + when the database does not exist ([issue 1395](https://github.com/PowerShell/SqlServerDsc/issues/1395)). + - No longer enforces the collation property if the Collation parameter + is not part of the configuration ([issue 1396](https://github.com/PowerShell/SqlServerDsc/issues/1396)). + - Updated resource description in README.md + - Fix examples to use `PsDscRunAsCredential` ([issue 760](https://github.com/PowerShell/SqlServerDsc/issues/760)). + - Added integration tests ([issue 739](https://github.com/PowerShell/SqlServerDsc/issues/739)). + - Updated unit tests to the latest template ([issue 1068](https://github.com/PowerShell/SqlServerDsc/issues/1068)). ' @@ -149,5 +135,6 @@ +