Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added integration tests for `Test-SqlDscIsLogin` command to ensure it functions
correctly in real environments
[issue #2230](https://github.com/dsccommunity/SqlServerDsc/issues/2230).
- Added integration tests for `Test-SqlDscIsDatabasePrincipal` command to ensure it
functions correctly in real environments
[issue #2231](https://github.com/dsccommunity/SqlServerDsc/issues/2231).
- Added integration tests for `Set-SqlDscAudit` command to ensure it functions
correctly in real environments
[issue #2236](https://github.com/dsccommunity/SqlServerDsc/issues/2236).
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ stages:
'tests/Integration/Commands/New-SqlDscRole.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1'
'tests/Integration/Commands/Test-SqlDscIsRole.Integration.Tests.ps1'
'tests/Integration/Commands/Test-SqlDscIsDatabasePrincipal.Integration.Tests.ps1'
'tests/Integration/Commands/Grant-SqlDscServerPermission.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscServerPermission.Integration.Tests.ps1'
'tests/Integration/Commands/Set-SqlDscServerPermission.Integration.Tests.ps1'
Expand Down
38 changes: 37 additions & 1 deletion source/Public/Remove-SqlDscDatabase.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
on instances with a large amount of databases it might be better to make
sure the **ServerObject** is recent enough, or pass in **DatabaseObject**.

.PARAMETER DropConnections
Specifies that all active connections to the database should be dropped
before removing the database. This sets the database to single-user mode
with immediate rollback of active transactions, which forcibly disconnects
all users and allows the database to be removed even when there are
active connections.

.EXAMPLE
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
$databaseObject = $serverObject | Get-SqlDscDatabase -Name 'MyDatabase'
Expand All @@ -43,6 +50,14 @@

Removes the database named **MyDatabase** without prompting for confirmation.

.EXAMPLE
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
$serverObject | Remove-SqlDscDatabase -Name 'MyDatabase' -DropConnections -Force

Drops all active connections to the database named **MyDatabase** and then removes it
without prompting for confirmation. This is useful when the database has active
connections that prevent removal.

.OUTPUTS
None.
#>
Expand Down Expand Up @@ -72,7 +87,11 @@ function Remove-SqlDscDatabase

[Parameter(ParameterSetName = 'ServerObject')]
[System.Management.Automation.SwitchParameter]
$Refresh
$Refresh,

[Parameter()]
[System.Management.Automation.SwitchParameter]
$DropConnections
)

process
Expand Down Expand Up @@ -126,6 +145,23 @@ function Remove-SqlDscDatabase
{
try
{
# Drop all active connections if requested
if ($DropConnections.IsPresent)
{
Write-Verbose -Message ($script:localizedData.Database_DroppingConnections -f $Name)

try
{
$DatabaseObject.UserAccess = 'Single'
$DatabaseObject.Alter([Microsoft.SqlServer.Management.Smo.TerminationClause]::RollbackTransactionsImmediately)
}
catch
{
$errorMessage = $script:localizedData.Database_DropConnectionsFailed -f $Name
New-InvalidOperationException -Message $errorMessage -ErrorRecord $_
}
}

Write-Verbose -Message ($script:localizedData.Database_Removing -f $Name)

$DatabaseObject.Drop()
Expand Down
2 changes: 2 additions & 0 deletions source/en-US/SqlServerDsc.strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ ConvertFrom-StringData @'
Database_Removed = Database '{0}' was removed successfully.
Database_RemoveFailed = Failed to remove database '{0}' from instance '{1}'.
Database_CannotRemoveSystem = Cannot remove system database '{0}'.
Database_DroppingConnections = Dropping all active connections to database '{0}'.
Database_DropConnectionsFailed = Failed to drop active connections for database '{0}'.
Database_Remove_ShouldProcessVerboseDescription = Removing the database '{0}' from the instance '{1}'.
Database_Remove_ShouldProcessVerboseWarning = Are you sure you want to remove the database '{0}'?
# This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ INSERT INTO TestTable (Name, Value) VALUES ('Test1', 100), ('Test2', 200), ('Tes
{
try
{
Remove-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -ErrorAction 'Stop'
Remove-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -DropConnections -ErrorAction 'Stop'
}
catch
{
Expand Down
1 change: 1 addition & 0 deletions tests/Integration/Commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ Test-SqlDscIsLoginEnabled | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DS
New-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | SqlDscIntegrationTestRole_Persistent role
Get-SqlDscRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
Test-SqlDscIsRole | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
Test-SqlDscIsDatabasePrincipal | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Test database and database principals
Grant-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | Grants CreateEndpoint permission to role
Get-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
Set-SqlDscServerPermission | 2 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')]
param ()

BeforeDiscovery {
try
{
if (-not (Get-Module -Name 'DscResource.Test'))
{
# Assumes dependencies have been resolved, so if this module is not available, run 'noop' task.
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
{
# Redirect all streams to $null, except the error stream (stream 2)
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null
}

# If the dependencies have not been resolved, this will throw an error.
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
}
}
catch [System.IO.FileNotFoundException]
{
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks noop" first.'
}
}

BeforeAll {
$script:moduleName = 'SqlServerDsc'

Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop'
}

Describe 'Test-SqlDscIsDatabasePrincipal' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') {
BeforeAll {
$script:mockInstanceName = 'DSCSQLTEST'

$mockSqlAdministratorUserName = 'SqlAdmin' # Using computer name as NetBIOS name throw exception.
$mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force

$script:mockSqlAdminCredential = [System.Management.Automation.PSCredential]::new($mockSqlAdministratorUserName, $mockSqlAdministratorPassword)

$script:serverObject = Connect-SqlDscDatabaseEngine -InstanceName $script:mockInstanceName -Credential $script:mockSqlAdminCredential

# Use a test database that should exist - we'll create it if it doesn't exist
$script:testDatabaseName = 'IntegrationTestDatabase'

# Create test database if it doesn't exist
if (-not $script:serverObject.Databases[$script:testDatabaseName])
{
$null = New-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testDatabaseName -Force -ErrorAction 'Stop'
}

# Test principals that should exist in the database
$script:testUserName = 'IntegrationTestUser'
$script:testRoleName = 'IntegrationTestRole'
$script:testAppRoleName = 'IntegrationTestAppRole'

# Create test database user if it doesn't exist
$testDatabase = $script:serverObject.Databases[$script:testDatabaseName]
if (-not $testDatabase.Users[$script:testUserName])
{
$sqlCommand = "USE [$script:testDatabaseName]; CREATE USER [$script:testUserName] WITHOUT LOGIN;"
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
}

# Create test database role if it doesn't exist
if (-not $testDatabase.Roles[$script:testRoleName])
{
$sqlCommand = "USE [$script:testDatabaseName]; CREATE ROLE [$script:testRoleName];"
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
}

# Create test application role if it doesn't exist
if (-not $testDatabase.ApplicationRoles[$script:testAppRoleName])
{
$sqlCommand = "USE [$script:testDatabaseName]; CREATE APPLICATION ROLE [$script:testAppRoleName] WITH PASSWORD = 'TestPassword123';"
$null = $script:serverObject.ConnectionContext.ExecuteNonQuery($sqlCommand)
}

# Refresh database objects to ensure they are loaded
$null = $testDatabase.Users.Refresh()
$null = $testDatabase.Roles.Refresh()
$null = $testDatabase.ApplicationRoles.Refresh()
}

AfterAll {
Disconnect-SqlDscDatabaseEngine -ServerObject $script:serverObject
}

Context 'When testing database user existence' {
It 'Should return True when database user exists' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}

It 'Should return False when database user does not exist' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'NonExistentUser'

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}

It 'Should return False when database user exists but ExcludeUsers is specified' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName -ExcludeUsers

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}

It 'Should return True for dbo user' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'dbo'

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}
}

Context 'When testing database role existence' {
It 'Should return True when database role exists' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}

It 'Should return True when fixed role exists' {
# Test with built-in db_datareader role
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'db_datareader'

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}

It 'Should return False when fixed role exists but ExcludeFixedRoles is specified' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name 'db_datareader' -ExcludeFixedRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}

It 'Should return False when database role exists but ExcludeRoles is specified' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}

It 'Should return True for user-defined role when ExcludeFixedRoles is specified' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeFixedRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}
}

Context 'When testing application role existence' {
It 'Should return True when application role exists' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testAppRoleName

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}

It 'Should return False when application role exists but ExcludeApplicationRoles is specified' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testAppRoleName -ExcludeApplicationRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}
}

Context 'When testing multiple exclusion parameters' {
It 'Should return False when principal exists but all types are excluded' {
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName -ExcludeUsers -ExcludeRoles -ExcludeApplicationRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeFalse
}

It 'Should work with combination of exclusion parameters' {
# Test role when users and app roles are excluded
$result = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testRoleName -ExcludeUsers -ExcludeApplicationRoles

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}
}

Context 'When testing pipeline parameter support' {
It 'Should accept ServerObject from pipeline' {
$result = $script:serverObject | Test-SqlDscIsDatabasePrincipal -DatabaseName $script:testDatabaseName -Name $script:testUserName

$result | Should -BeOfType [System.Boolean]
$result | Should -BeTrue
}
}

Context 'When testing error conditions' {
It 'Should throw when database does not exist' {
{ Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName 'NonExistentDatabase' -Name 'SomePrincipal' -ErrorAction 'Stop' } | Should -Throw
}
}

Context 'When testing case sensitivity' {
It 'Should handle case differences correctly for database names' {
# Database names are case-insensitive in SQL Server
$result1 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName.ToUpper() -Name $script:testUserName
$result2 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName.ToLower() -Name $script:testUserName

$result1 | Should -BeOfType [System.Boolean]
$result2 | Should -BeOfType [System.Boolean]
$result1 | Should -Be $result2
}

It 'Should handle case differences correctly for principal names' {
# Principal names are case-insensitive in SQL Server
$result1 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName.ToUpper()
$result2 = Test-SqlDscIsDatabasePrincipal -ServerObject $script:serverObject -DatabaseName $script:testDatabaseName -Name $script:testUserName.ToLower()

$result1 | Should -BeOfType [System.Boolean]
$result2 | Should -BeOfType [System.Boolean]
$result1 | Should -Be $result2
}
}
}
Loading
Loading