diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d6657199..cc7d7a26ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -195,6 +195,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- `Remove-SqlDscAgentAlert` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2193](https://github.com/dsccommunity/SqlServerDsc/issues/2193)). +- `Set-SqlDscAgentAlert` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2196](https://github.com/dsccommunity/SqlServerDsc/issues/2196)). +- `Remove-SqlDscDatabase` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2195](https://github.com/dsccommunity/SqlServerDsc/issues/2195)). +- `Remove-SqlDscRole` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2199](https://github.com/dsccommunity/SqlServerDsc/issues/2199)). +- `New-SqlDscDatabase` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2200](https://github.com/dsccommunity/SqlServerDsc/issues/2200)). +- `Set-SqlDscDatabase` + - Now uses `$PSCmdlet.ThrowTerminatingError()` instead of exception helper + functions for proper terminating error handling ([issue #2198](https://github.com/dsccommunity/SqlServerDsc/issues/2198)). - `Add-SqlDscTraceFlag` - Improved de-duplication logic to normalize element types to `[System.UInt32]` before sorting and removing duplicates, ensuring proper handling of mixed diff --git a/source/Public/Get-SqlDscRole.ps1 b/source/Public/Get-SqlDscRole.ps1 index 0640e62f59..5fd900d33b 100644 --- a/source/Public/Get-SqlDscRole.ps1 +++ b/source/Public/Get-SqlDscRole.ps1 @@ -73,9 +73,9 @@ function Get-SqlDscRole if (-not $roleObject) { - Write-Verbose -Message ($script:localizedData.Role_NotFound -f $Name) + Write-Verbose -Message ($script:localizedData.Get_SqlDscRole_NotFound -f $Name) - $missingRoleMessage = $script:localizedData.Role_NotFound -f $Name + $missingRoleMessage = $script:localizedData.Get_SqlDscRole_NotFound -f $Name $writeErrorParameters = @{ Message = $missingRoleMessage @@ -85,6 +85,8 @@ function Get-SqlDscRole } Write-Error @writeErrorParameters + + return } else { diff --git a/source/Public/New-SqlDscDatabase.ps1 b/source/Public/New-SqlDscDatabase.ps1 index ba805ce511..855785a11f 100644 --- a/source/Public/New-SqlDscDatabase.ps1 +++ b/source/Public/New-SqlDscDatabase.ps1 @@ -147,7 +147,15 @@ function New-SqlDscDatabase if ($CompatibilityLevel -notin $supportedCompatibilityLevels.$($ServerObject.VersionMajor)) { $errorMessage = $script:localizedData.Database_InvalidCompatibilityLevel -f $CompatibilityLevel, $ServerObject.InstanceName - New-ArgumentException -ArgumentName 'CompatibilityLevel' -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.ArgumentException]::new($errorMessage), + 'NSD0003', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidArgument, + $CompatibilityLevel + ) + ) } } @@ -157,7 +165,15 @@ function New-SqlDscDatabase if ($Collation -notin $ServerObject.EnumCollations().Name) { $errorMessage = $script:localizedData.Database_InvalidCollation -f $Collation, $ServerObject.InstanceName - New-ArgumentException -ArgumentName 'Collation' -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.ArgumentException]::new($errorMessage), + 'NSD0004', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidArgument, + $Collation + ) + ) } } diff --git a/source/Public/New-SqlDscRole.ps1 b/source/Public/New-SqlDscRole.ps1 index a15f37afe0..38b752d318 100644 --- a/source/Public/New-SqlDscRole.ps1 +++ b/source/Public/New-SqlDscRole.ps1 @@ -70,6 +70,14 @@ function New-SqlDscRole $Refresh ) + begin + { + if ($Force.IsPresent -and -not $Confirm) + { + $ConfirmPreference = 'None' + } + } + process { if ($Refresh.IsPresent) @@ -88,18 +96,18 @@ function New-SqlDscRole $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( [System.InvalidOperationException]::new($errorMessage), - 'NSR0001', # cspell: disable-line + 'NSDR0001', # cspell: disable-line [System.Management.Automation.ErrorCategory]::ResourceExists, $Name ) ) } - $verboseDescriptionMessage = $script:localizedData.Role_Create_ShouldProcessVerboseDescription -f $Name, $ServerObject.InstanceName - $verboseWarningMessage = $script:localizedData.Role_Create_ShouldProcessVerboseWarning -f $Name + $descriptionMessage = $script:localizedData.Role_Create_ShouldProcessDescription -f $Name, $ServerObject.InstanceName + $confirmationMessage = $script:localizedData.Role_Create_ShouldProcessConfirmation -f $Name $captionMessage = $script:localizedData.Role_Create_ShouldProcessCaption - if ($Force.IsPresent -or $PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage)) { try { @@ -110,12 +118,8 @@ function New-SqlDscRole $serverRole.Owner = $Owner } - Write-Verbose -Message ($script:localizedData.Role_Creating -f $Name) - $serverRole.Create() - Write-Verbose -Message ($script:localizedData.Role_Created -f $Name) - return $serverRole } catch @@ -125,7 +129,7 @@ function New-SqlDscRole $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( [System.InvalidOperationException]::new($errorMessage, $_.Exception), - 'NSR0002', # cspell: disable-line + 'NSDR0002', # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $Name ) diff --git a/source/Public/Remove-SqlDscAgentAlert.ps1 b/source/Public/Remove-SqlDscAgentAlert.ps1 index 05e6e8a9e2..96fcbde4fb 100644 --- a/source/Public/Remove-SqlDscAgentAlert.ps1 +++ b/source/Public/Remove-SqlDscAgentAlert.ps1 @@ -117,9 +117,13 @@ function Remove-SqlDscAgentAlert if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { + Write-Verbose -Message ($script:localizedData.Remove_SqlDscAgentAlert_RemovingAlert -f $alertObjectToRemove.Name) + try { - Write-Verbose -Message ($script:localizedData.Remove_SqlDscAgentAlert_RemovingAlert -f $alertObjectToRemove.Name) + $originalErrorActionPreference = $ErrorActionPreference + + $ErrorActionPreference = 'Stop' $alertObjectToRemove.Drop() @@ -128,7 +132,19 @@ function Remove-SqlDscAgentAlert catch { $errorMessage = $script:localizedData.Remove_SqlDscAgentAlert_RemoveFailed -f $alertObjectToRemove.Name - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'RSAA0005', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $alertObjectToRemove + ) + ) + } + finally + { + $ErrorActionPreference = $originalErrorActionPreference } } } diff --git a/source/Public/Remove-SqlDscDatabase.ps1 b/source/Public/Remove-SqlDscDatabase.ps1 index 8b21e95036..4addcb1694 100644 --- a/source/Public/Remove-SqlDscDatabase.ps1 +++ b/source/Public/Remove-SqlDscDatabase.ps1 @@ -94,6 +94,14 @@ function Remove-SqlDscDatabase $DropConnections ) + begin + { + if ($Force.IsPresent -and -not $Confirm) + { + $ConfirmPreference = 'None' + } + } + process { if ($PSCmdlet.ParameterSetName -eq 'ServerObject') @@ -104,74 +112,90 @@ function Remove-SqlDscDatabase $ServerObject.Databases.Refresh() } - Write-Verbose -Message ($script:localizedData.Database_Remove -f $Name, $ServerObject.InstanceName) - - # Check if the database is a system database (cannot be dropped) - $systemDatabases = @('master', 'model', 'msdb', 'tempdb') - if ($Name -in $systemDatabases) - { - $errorMessage = $script:localizedData.Database_CannotRemoveSystem -f $Name - New-InvalidOperationException -Message $errorMessage - } - # Get the database object $DatabaseObject = $ServerObject.Databases[$Name] if (-not $DatabaseObject) { - $errorMessage = $script:localizedData.Database_NotFound -f $Name - New-InvalidOperationException -Message $errorMessage + $errorMessage = $script:localizedData.Remove_SqlDscDatabase_NotFound -f $Name + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.ItemNotFoundException]::new($errorMessage), + 'RSDD0002', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $Name + ) + ) } } else { $Name = $DatabaseObject.Name - Write-Verbose -Message ($script:localizedData.Database_Remove -f $Name, $DatabaseObject.Parent.InstanceName) + } - # Check if the database is a system database (cannot be dropped) - $systemDatabases = @('master', 'model', 'msdb', 'tempdb') - if ($Name -in $systemDatabases) - { - $errorMessage = $script:localizedData.Database_CannotRemoveSystem -f $Name - New-InvalidOperationException -Message $errorMessage - } + # Check if the database is a system database (cannot be dropped) + if ($Name -in @('master', 'model', 'msdb', 'tempdb')) + { + $errorMessage = $script:localizedData.Database_CannotRemoveSystem -f $Name + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage), + 'RSDD0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $Name + ) + ) } - $verboseDescriptionMessage = $script:localizedData.Database_Remove_ShouldProcessVerboseDescription -f $Name, $DatabaseObject.Parent.InstanceName - $verboseWarningMessage = $script:localizedData.Database_Remove_ShouldProcessVerboseWarning -f $Name + $descriptionMessage = $script:localizedData.Database_Remove_ShouldProcessDescription -f $Name, $DatabaseObject.Parent.InstanceName + $confirmationMessage = $script:localizedData.Database_Remove_ShouldProcessConfirmation -f $Name $captionMessage = $script:localizedData.Database_Remove_ShouldProcessCaption - if ($Force.IsPresent -or $PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage)) { - try + # Drop all active connections if requested + if ($DropConnections.IsPresent) { - # Drop all active connections if requested - if ($DropConnections.IsPresent) + Write-Verbose -Message ($script:localizedData.Database_DroppingConnections -f $Name) + + try { - 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 $_ - } + $DatabaseObject.UserAccess = [Microsoft.SqlServer.Management.Smo.DatabaseUserAccess]::Single + $DatabaseObject.Alter([Microsoft.SqlServer.Management.Smo.TerminationClause]::RollbackTransactionsImmediately) } + catch + { + $errorMessage = $script:localizedData.Database_DropConnectionsFailed -f $Name + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'RSDD0004', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $DatabaseObject + ) + ) + } + } - Write-Verbose -Message ($script:localizedData.Database_Removing -f $Name) - + try + { $DatabaseObject.Drop() - - Write-Verbose -Message ($script:localizedData.Database_Removed -f $Name) } catch { $errorMessage = $script:localizedData.Database_RemoveFailed -f $Name, $DatabaseObject.Parent.InstanceName - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'RSDD0005', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $DatabaseObject + ) + ) } } } diff --git a/source/Public/Remove-SqlDscRole.ps1 b/source/Public/Remove-SqlDscRole.ps1 index 9d44f43928..2a08c00f28 100644 --- a/source/Public/Remove-SqlDscRole.ps1 +++ b/source/Public/Remove-SqlDscRole.ps1 @@ -75,6 +75,14 @@ function Remove-SqlDscRole $Refresh ) + begin + { + if ($Force.IsPresent -and -not $Confirm) + { + $ConfirmPreference = 'None' + } + } + process { if ($PSCmdlet.ParameterSetName -eq 'ServerObject') @@ -92,8 +100,16 @@ function Remove-SqlDscRole if (-not $RoleObject) { - $errorMessage = $script:localizedData.Role_NotFound -f $Name - New-InvalidOperationException -Message $errorMessage + $errorMessage = $script:localizedData.Remove_SqlDscRole_NotFound -f $Name + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.ItemNotFoundException]::new($errorMessage), + 'RSDR0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $Name + ) + ) } } else @@ -106,27 +122,39 @@ function Remove-SqlDscRole if ($RoleObject.IsFixedRole) { $errorMessage = $script:localizedData.Role_CannotRemoveBuiltIn -f $Name - New-InvalidOperationException -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage), + 'RSDR0002', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $RoleObject + ) + ) } - $verboseDescriptionMessage = $script:localizedData.Role_Remove_ShouldProcessVerboseDescription -f $Name, $RoleObject.Parent.InstanceName - $verboseWarningMessage = $script:localizedData.Role_Remove_ShouldProcessVerboseWarning -f $Name + $descriptionMessage = $script:localizedData.Role_Remove_ShouldProcessDescription -f $Name, $RoleObject.Parent.InstanceName + $confirmationMessage = $script:localizedData.Role_Remove_ShouldProcessConfirmation -f $Name $captionMessage = $script:localizedData.Role_Remove_ShouldProcessCaption - if ($Force.IsPresent -or $PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage)) { try { - Write-Verbose -Message ($script:localizedData.Role_Removing -f $Name) - $RoleObject.Drop() - - Write-Verbose -Message ($script:localizedData.Role_Removed -f $Name) } catch { $errorMessage = $script:localizedData.Role_RemoveFailed -f $Name, $RoleObject.Parent.InstanceName - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'RSDR0003', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $RoleObject + ) + ) } } } diff --git a/source/Public/Set-SqlDscAgentAlert.ps1 b/source/Public/Set-SqlDscAgentAlert.ps1 index 02c7cf8970..8f4ea8638c 100644 --- a/source/Public/Set-SqlDscAgentAlert.ps1 +++ b/source/Public/Set-SqlDscAgentAlert.ps1 @@ -141,7 +141,15 @@ function Set-SqlDscAgentAlert if ($null -eq $alertObjectToUpdate) { $errorMessage = $script:localizedData.Set_SqlDscAgentAlert_AlertNotFound -f $Name - New-ObjectNotFoundException -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.Management.Automation.ItemNotFoundException]::new($errorMessage), + 'SSAA0002', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::ObjectNotFound, + $Name + ) + ) } } else @@ -209,7 +217,15 @@ function Set-SqlDscAgentAlert catch { $errorMessage = $script:localizedData.Set_SqlDscAgentAlert_UpdateFailed -f $alertObjectToUpdate.Name - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'SSAA0008', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $alertObjectToUpdate + ) + ) } } } diff --git a/source/Public/Set-SqlDscDatabase.ps1 b/source/Public/Set-SqlDscDatabase.ps1 index afd5893f46..e160cd11e6 100644 --- a/source/Public/Set-SqlDscDatabase.ps1 +++ b/source/Public/Set-SqlDscDatabase.ps1 @@ -108,6 +108,14 @@ function Set-SqlDscDatabase $PassThru ) + begin + { + if ($Force.IsPresent -and -not $Confirm) + { + $ConfirmPreference = 'None' + } + } + process { if ($PSCmdlet.ParameterSetName -eq 'ServerObject') @@ -126,7 +134,15 @@ function Set-SqlDscDatabase if (-not $DatabaseObject) { $errorMessage = $script:localizedData.Database_NotFound -f $Name - New-InvalidOperationException -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage), + 'SSDD0001', # SQL Server Database - Database not found + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $Name + ) + ) } } else @@ -154,7 +170,15 @@ function Set-SqlDscDatabase if ($CompatibilityLevel -notin $supportedCompatibilityLevels.$($ServerObject.VersionMajor)) { $errorMessage = $script:localizedData.Database_InvalidCompatibilityLevel -f $CompatibilityLevel, $ServerObject.InstanceName - New-ObjectNotFoundException -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.ArgumentException]::new($errorMessage), + 'SSDD0002', # SQL Server Database - Invalid compatibility level + [System.Management.Automation.ErrorCategory]::InvalidArgument, + $CompatibilityLevel + ) + ) } } @@ -164,7 +188,15 @@ function Set-SqlDscDatabase if ($Collation -notin $ServerObject.EnumCollations().Name) { $errorMessage = $script:localizedData.Database_InvalidCollation -f $Collation, $ServerObject.InstanceName - New-ObjectNotFoundException -Message $errorMessage + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.ArgumentException]::new($errorMessage), + 'SSDD0003', # SQL Server Database - Invalid collation + [System.Management.Automation.ErrorCategory]::InvalidArgument, + $Collation + ) + ) } } @@ -172,7 +204,7 @@ function Set-SqlDscDatabase $verboseWarningMessage = $script:localizedData.Database_Set_ShouldProcessVerboseWarning -f $Name $captionMessage = $script:localizedData.Database_Set_ShouldProcessCaption - if ($Force.IsPresent -or $PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) + if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { try { @@ -221,7 +253,15 @@ function Set-SqlDscDatabase catch { $errorMessage = $script:localizedData.Database_SetFailed -f $Name, $ServerObject.InstanceName - New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + [System.InvalidOperationException]::new($errorMessage, $_.Exception), + 'SSDD0004', # SQL Server Database - Set failed + [System.Management.Automation.ErrorCategory]::InvalidOperation, + $DatabaseObject + ) + ) } } } diff --git a/source/en-US/SqlServerDsc.strings.psd1 b/source/en-US/SqlServerDsc.strings.psd1 index e246006182..b7151d3083 100644 --- a/source/en-US/SqlServerDsc.strings.psd1 +++ b/source/en-US/SqlServerDsc.strings.psd1 @@ -314,27 +314,24 @@ ConvertFrom-StringData @' Role_Get = Getting server roles from instance '{0}'. Role_GetAll = Getting all server roles. Role_Found = Found server role '{0}'. - Role_NotFound = Server role '{0}' was not found. + Get_SqlDscRole_NotFound = Server role '{0}' was not found. (GSDR0001) ## New-SqlDscRole Role_Create = Creating server role '{0}' on instance '{1}'. - Role_Creating = Creating server role '{0}'. - Role_Created = Server role '{0}' was created successfully. Role_CreateFailed = Failed to create server role '{0}' on instance '{1}'. Role_AlreadyExists = Server role '{0}' already exists on instance '{1}'. - Role_Create_ShouldProcessVerboseDescription = Creating the server role '{0}' on the instance '{1}'. - Role_Create_ShouldProcessVerboseWarning = Are you sure you want to create the server role '{0}'? + Role_Create_ShouldProcessDescription = Creating the server role '{0}' on the instance '{1}'. + Role_Create_ShouldProcessConfirmation = Are you sure you want to create the server role '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Role_Create_ShouldProcessCaption = Create server role on instance ## Remove-SqlDscRole Role_Remove = Removing server role '{0}' from instance '{1}'. - Role_Removing = Removing server role '{0}'. - Role_Removed = Server role '{0}' was removed successfully. - Role_RemoveFailed = Failed to remove server role '{0}' from instance '{1}'. - Role_CannotRemoveBuiltIn = Cannot remove built-in server role '{0}'. - Role_Remove_ShouldProcessVerboseDescription = Removing the server role '{0}' from the instance '{1}'. - Role_Remove_ShouldProcessVerboseWarning = Are you sure you want to remove the server role '{0}'? + Remove_SqlDscRole_NotFound = Server role '{0}' was not found. (RSDR0001) + Role_RemoveFailed = Failed to remove server role '{0}' from instance '{1}'. (RSDR0003) + Role_CannotRemoveBuiltIn = Cannot remove built-in server role '{0}'. (RSDR0002) + Role_Remove_ShouldProcessDescription = Removing the server role '{0}' from the instance '{1}'. + Role_Remove_ShouldProcessConfirmation = Are you sure you want to remove the server role '{0}'? # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. Role_Remove_ShouldProcessCaption = Remove server role from instance @@ -380,17 +377,15 @@ ConvertFrom-StringData @' Database_Set_ShouldProcessCaption = Set database properties on instance ## Remove-SqlDscDatabase - Database_Remove = Removing database '{0}' from instance '{1}'. - Database_Removing = Removing database '{0}'. - 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}'? + Database_Remove_ShouldProcessDescription = Removing the database '{0}' from the instance '{1}'. + Database_Remove_ShouldProcessConfirmation = 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. Database_Remove_ShouldProcessCaption = Remove database from instance + Remove_SqlDscDatabase_NotFound = Database '{0}' was not found. ## Test-SqlDscDatabase Database_Test = Testing the state of database '{0}' on instance '{1}'. diff --git a/tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1 b/tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1 index 0bc417f2b4..bc6736b284 100644 --- a/tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1 +++ b/tests/Integration/Commands/Get-SqlDscRole.Integration.Tests.ps1 @@ -96,7 +96,7 @@ Describe 'Get-SqlDscRole' -Tag @('Integration_SQL2017', 'Integration_SQL2019', ' It 'Should throw an error when the role does not exist' { { Get-SqlDscRole -ServerObject $script:serverObject -Name 'NonExistentRole' -ErrorAction 'Stop' } | - Should -Throw -ExpectedMessage 'Server role ''NonExistentRole'' was not found.' + Should -Throw } It 'Should return null when the role does not exist and error action is SilentlyContinue' { diff --git a/tests/Unit/Public/Get-SqlDscRole.Tests.ps1 b/tests/Unit/Public/Get-SqlDscRole.Tests.ps1 index 53baefd178..e8ef3a130a 100644 --- a/tests/Unit/Public/Get-SqlDscRole.Tests.ps1 +++ b/tests/Unit/Public/Get-SqlDscRole.Tests.ps1 @@ -124,7 +124,7 @@ Describe 'Get-SqlDscRole' -Tag 'Public' { Mock -CommandName 'Write-Verbose' # Test the exact error message first - $expectedMessage = 'Server role ''NonExistentRole'' was not found.' + $expectedMessage = 'Server role ''NonExistentRole'' was not found. (GSDR0001)' { Get-SqlDscRole -ServerObject $mockServerObject -Name 'NonExistentRole' -ErrorAction 'Stop' } | Should -Throw -ExpectedMessage $expectedMessage diff --git a/tests/Unit/Public/New-SqlDscDatabase.Tests.ps1 b/tests/Unit/Public/New-SqlDscDatabase.Tests.ps1 index c9ec3b76e8..3a005b42d6 100644 --- a/tests/Unit/Public/New-SqlDscDatabase.Tests.ps1 +++ b/tests/Unit/Public/New-SqlDscDatabase.Tests.ps1 @@ -133,13 +133,23 @@ Describe 'New-SqlDscDatabase' -Tag 'Public' { } It 'Should throw error when CompatibilityLevel is invalid for SQL Server version' { - { New-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDB' -CompatibilityLevel 'Version80' -Force } | - Should -Throw -ExpectedMessage '*not a valid compatibility level*' + $errorRecord = { New-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDB' -CompatibilityLevel 'Version80' -Force } | + Should -Throw -ExpectedMessage '*not a valid compatibility level*' -PassThru + + $errorRecord.Exception.Message | Should -BeLike '*not a valid compatibility level*' + $errorRecord.FullyQualifiedErrorId | Should -Be 'NSD0003,New-SqlDscDatabase' + $errorRecord.CategoryInfo.Category | Should -Be 'InvalidArgument' + $errorRecord.CategoryInfo.TargetName | Should -Be 'Version80' } It 'Should throw error when Collation is invalid' { - { New-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDB' -Collation 'InvalidCollation' -Force } | - Should -Throw -ExpectedMessage '*not a valid collation*' + $errorRecord = { New-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDB' -Collation 'InvalidCollation' -Force } | + Should -Throw -ExpectedMessage '*not a valid collation*' -PassThru + + $errorRecord.Exception.Message | Should -BeLike '*not a valid collation*' + $errorRecord.FullyQualifiedErrorId | Should -Be 'NSD0004,New-SqlDscDatabase' + $errorRecord.CategoryInfo.Category | Should -Be 'InvalidArgument' + $errorRecord.CategoryInfo.TargetName | Should -Be 'InvalidCollation' } } diff --git a/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 b/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 index 80fe543558..b1d142d8bc 100644 --- a/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 +++ b/tests/Unit/Public/Remove-SqlDscAgentAlert.Tests.ps1 @@ -211,9 +211,13 @@ Describe 'Remove-SqlDscAgentAlert' -Tag 'Public' { $script:mockFailingAlert | Add-Member -MemberType ScriptMethod -Name 'Drop' -Value { throw 'Removal failed' } -Force } - It 'Should throw error when removal fails' { - { Remove-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Force } | - Should -Throw -ExpectedMessage '*Failed to remove*' + It 'Should throw correct error when removal fails' { + $errorRecord = { Remove-SqlDscAgentAlert -ServerObject $script:mockServerObject -Name 'TestAlert' -Force } | + Should -Throw -PassThru + + $errorRecord.Exception.Message | Should -BeLike '*Failed to remove*TestAlert*' + $errorRecord.Exception | Should -BeOfType [System.InvalidOperationException] + $errorRecord.FullyQualifiedErrorId | Should -Be 'RSAA0005,Remove-SqlDscAgentAlert' } } diff --git a/tests/Unit/Public/Remove-SqlDscDatabase.Tests.ps1 b/tests/Unit/Public/Remove-SqlDscDatabase.Tests.ps1 index 33e21d7a05..be81988d1c 100644 --- a/tests/Unit/Public/Remove-SqlDscDatabase.Tests.ps1 +++ b/tests/Unit/Public/Remove-SqlDscDatabase.Tests.ps1 @@ -80,13 +80,21 @@ Describe 'Remove-SqlDscDatabase' -Tag 'Public' { } It 'Should throw error when database does not exist' { + $expectedMessage = InModuleScope -ScriptBlock { + $script:localizedData.Remove_SqlDscDatabase_NotFound -f 'NonExistentDatabase' + } + { Remove-SqlDscDatabase -ServerObject $mockServerObject -Name 'NonExistentDatabase' -Force } | - Should -Throw -ExpectedMessage '*not found*' + Should -Throw -ExpectedMessage ('*{0}*' -f $expectedMessage) -ErrorId 'RSDD0002,Remove-SqlDscDatabase' } It 'Should throw error when trying to remove system database' { + $expectedMessage = InModuleScope -ScriptBlock { + $script:localizedData.Database_CannotRemoveSystem -f 'master' + } + { Remove-SqlDscDatabase -ServerObject $mockServerObject -Name 'master' -Force } | - Should -Throw -ExpectedMessage '*Cannot remove system database*' + Should -Throw -ExpectedMessage ('*{0}*' -f $expectedMessage) -ErrorId 'RSDD0001,Remove-SqlDscDatabase' } } @@ -121,8 +129,12 @@ Describe 'Remove-SqlDscDatabase' -Tag 'Public' { return $mockParent } -Force + $expectedMessage = InModuleScope -ScriptBlock { + $script:localizedData.Database_CannotRemoveSystem -f 'master' + } + { Remove-SqlDscDatabase -DatabaseObject $mockSystemDatabaseObject -Force } | - Should -Throw -ExpectedMessage '*Cannot remove system database*' + Should -Throw -ExpectedMessage ('*{0}*' -f $expectedMessage) -ErrorId 'RSDD0001,Remove-SqlDscDatabase' } } diff --git a/tests/Unit/Public/Set-SqlDscDatabase.Tests.ps1 b/tests/Unit/Public/Set-SqlDscDatabase.Tests.ps1 index de4dacf02e..73aae8d937 100644 --- a/tests/Unit/Public/Set-SqlDscDatabase.Tests.ps1 +++ b/tests/Unit/Public/Set-SqlDscDatabase.Tests.ps1 @@ -108,7 +108,7 @@ Describe 'Set-SqlDscDatabase' -Tag 'Public' { It 'Should throw error when database does not exist' { { Set-SqlDscDatabase -ServerObject $mockServerObject -Name 'NonExistentDatabase' -RecoveryModel 'Simple' -Force } | - Should -Throw -ExpectedMessage '*not found*' + Should -Throw -ExpectedMessage '*not found*' -ErrorId 'SSDD0001,Set-SqlDscDatabase' } } @@ -166,7 +166,7 @@ Describe 'Set-SqlDscDatabase' -Tag 'Public' { It 'Should throw error when CompatibilityLevel is invalid for SQL Server version' { { Set-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDatabase' -CompatibilityLevel 'Version80' -Force } | - Should -Throw -ExpectedMessage '*not a valid compatibility level*' + Should -Throw -ExpectedMessage '*not a valid compatibility level*' -ErrorId 'SSDD0002,Set-SqlDscDatabase' } It 'Should allow valid CompatibilityLevel for SQL Server version' { @@ -220,7 +220,7 @@ Describe 'Set-SqlDscDatabase' -Tag 'Public' { It 'Should throw error when Collation is invalid' { { Set-SqlDscDatabase -ServerObject $mockServerObject -Name 'TestDatabase' -Collation 'InvalidCollation' -Force } | - Should -Throw -ExpectedMessage '*not a valid collation*' + Should -Throw -ExpectedMessage '*not a valid collation*' -ErrorId 'SSDD0003,Set-SqlDscDatabase' } It 'Should allow valid Collation' { @@ -279,6 +279,33 @@ Describe 'Set-SqlDscDatabase' -Tag 'Public' { } } + Context 'When database modification fails' { + BeforeAll { + $mockDatabaseObject = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Database' + $mockDatabaseObject | Add-Member -MemberType 'NoteProperty' -Name 'Name' -Value 'TestDatabase' -Force + $mockDatabaseObject | Add-Member -MemberType 'NoteProperty' -Name 'RecoveryModel' -Value 'Full' -Force + $mockDatabaseObject | Add-Member -MemberType 'ScriptProperty' -Name 'Parent' -Value { + $mockParent = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Server' + $mockParent | Add-Member -MemberType 'NoteProperty' -Name 'InstanceName' -Value 'TestInstance' -Force + $mockParent | Add-Member -MemberType 'NoteProperty' -Name 'VersionMajor' -Value 15 -Force + $mockParent | Add-Member -MemberType 'ScriptMethod' -Name 'EnumCollations' -Value { + return @( + @{ Name = 'SQL_Latin1_General_CP1_CI_AS' } + ) + } -Force + return $mockParent + } -Force + $mockDatabaseObject | Add-Member -MemberType 'ScriptMethod' -Name 'Alter' -Value { + throw 'Simulated Alter() failure' + } -Force + } + + It 'Should throw terminating error when Alter() fails' { + { Set-SqlDscDatabase -DatabaseObject $mockDatabaseObject -RecoveryModel 'Simple' -Force } | + Should -Throw -ExpectedMessage '*Failed to set properties*' -ErrorId 'SSDD0004,Set-SqlDscDatabase' + } + } + Context 'Parameter validation' { It 'Should have the correct parameters in parameter set ServerObject' -ForEach @( @{ diff --git a/tests/Unit/Stubs/SMO.cs b/tests/Unit/Stubs/SMO.cs index 828252f523..d10401ebef 100644 --- a/tests/Unit/Stubs/SMO.cs +++ b/tests/Unit/Stubs/SMO.cs @@ -163,6 +163,17 @@ public enum TerminationClause : int CloseAllConnectionsImmediately = 2 } + // TypeName: Microsoft.SqlServer.Management.Smo.DatabaseUserAccess + // Used by: + // New-SqlDscDatabase.Tests.ps1 + // Set-SqlDscDatabase.Tests.ps1 + public enum DatabaseUserAccess : int + { + Multiple = 0, + Single = 1, + Restricted = 2 + } + #endregion Public Enums #region Public Classes