diff --git a/CHANGELOG.md b/CHANGELOG.md index 536bd780a..7dfe0e8df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SqlReplication - The resource are now using the helper function `Get-SqlInstanceMajorVersion` ([issue #1408](https://github.com/dsccommunity/SqlServerDsc/issues/1408)). +- SqlDatabase + - Support enabling or disabling snapshot isolation + ([issue #845](https://github.com/dsccommunity/SqlServerDsc/issues/845)). ### Fixed - SqlDatabaseRole diff --git a/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.psm1 b/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.psm1 index 2a15bdced..1f1cf804b 100644 --- a/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.psm1 +++ b/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.psm1 @@ -74,6 +74,7 @@ function Get-TargetResource CompatibilityLevel = $null RecoveryModel = $null OwnerName = $null + SnapshotIsolation = $null } $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName @@ -90,6 +91,7 @@ function Get-TargetResource $returnValue['CompatibilityLevel'] = $sqlDatabaseObject.CompatibilityLevel $returnValue['RecoveryModel'] = $sqlDatabaseObject.RecoveryModel $returnValue['OwnerName'] = $sqlDatabaseObject.Owner + $returnValue['SnapshotIsolation'] = $sqlDatabaseObject.SnapshotIsolationState -eq 'Enabled' Write-Verbose -Message ( $script:localizedData.DatabasePresent -f $Name @@ -137,6 +139,9 @@ function Get-TargetResource .PARAMETER OwnerName Specifies the name of the login that should be the owner of the database. + + .PARAMETER SnapshotIsolation + Specifics whether snapshot isolation should be enabled for the new database. #> function Set-TargetResource { @@ -181,7 +186,11 @@ function Set-TargetResource [Parameter()] [System.String] - $OwnerName + $OwnerName, + + [Parameter()] + [System.Boolean] + $SnapshotIsolation ) $sqlServerObject = Connect-SQL -ServerName $ServerName -InstanceName $InstanceName @@ -275,6 +284,24 @@ function Set-TargetResource $wasUpdate = $true } + if ($PSBoundParameters.ContainsKey('SnapshotIsolation')) + { + Write-Verbose -Message ( + $script:localizedData.UpdatingSnapshotIsolation -f $SnapshotIsolation + ) + + try + { + $sqlDatabaseObject.SetSnapshotIsolation($SnapshotIsolation) + } + catch + { + $errorMessage = $script:localizedData.FailedToUpdateSnapshotIsolation -f $SnapshotIsolation, $Name + + New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ + } + } + try { if ($wasUpdate) @@ -438,7 +465,11 @@ function Test-TargetResource [Parameter()] [System.String] - $OwnerName + $OwnerName, + + [Parameter()] + [System.Boolean] + $SnapshotIsolation ) Write-Verbose -Message ( @@ -516,6 +547,15 @@ function Test-TargetResource $isDatabaseInDesiredState = $false } + + if ($PSBoundParameters.ContainsKey('SnapshotIsolation') -and $getTargetResourceResult.SnapshotIsolation -ne $SnapshotIsolation) + { + Write-Verbose -Message ( + $script:localizedData.SnapshotIsolationWrong -f $Name, $getTargetResourceResult.SnapshotIsolation, $SnapshotIsolation + ) + + $isDatabaseInDesiredState = $false + } } } } diff --git a/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.schema.mof b/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.schema.mof index 787d01618..e9f49deed 100644 --- a/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.schema.mof +++ b/source/DSCResources/DSC_SqlDatabase/DSC_SqlDatabase.schema.mof @@ -9,4 +9,5 @@ class DSC_SqlDatabase : OMI_BaseResource [Write, Description("Specifies the version of the _SQL Database Compatibility Level_ to use for the specified database."), ValueMap{"Version80","Version90","Version100","Version110","Version120","Version130","Version140","Version150"}, Values{"Version80","Version90","Version100","Version110","Version120","Version130","Version140","Version150"}] String CompatibilityLevel; [Write, Description("The recovery model for the specified database."), ValueMap{"Simple","Full","BulkLogged"}, Values{"Simple","Full","BulkLogged"}] String RecoveryModel; [Write, Description("Specifies the name of the login that should be the owner of the database.")] String OwnerName; + [Write, Description("Enables snapshot isolation for the specified database.")] Boolean SnapshotIsolation; }; diff --git a/source/DSCResources/DSC_SqlDatabase/en-US/DSC_SqlDatabase.strings.psd1 b/source/DSCResources/DSC_SqlDatabase/en-US/DSC_SqlDatabase.strings.psd1 index 0ab2d79f8..89b93a775 100644 --- a/source/DSCResources/DSC_SqlDatabase/en-US/DSC_SqlDatabase.strings.psd1 +++ b/source/DSCResources/DSC_SqlDatabase/en-US/DSC_SqlDatabase.strings.psd1 @@ -22,4 +22,7 @@ ConvertFrom-StringData @' OwnerNameWrong = The database '{0}' exist and has the owner '{1}', but expected it to have the owner '{2}'. UpdatingOwner = Changing the database owner to '{0}'. FailedToUpdateOwner = Failed changing to owner to '{0}' for the database '{1}'. + UpdatingSnapshotIsolation = Updating snapshot isolation to '{0}'. + FailedToUpdateSnapshotIsolation = Failed changing snapshot isolation to '{0}' for the database '{1}'. + SnapshotIsolationWrong = The database '{0}' exists and has snapshot isolation set to '{1}', but expected it to have snapshot isolation set to '{2}'. '@ diff --git a/tests/Integration/DSC_SqlDatabase.Integration.Tests.ps1 b/tests/Integration/DSC_SqlDatabase.Integration.Tests.ps1 index a606a5f91..2f1415307 100644 --- a/tests/Integration/DSC_SqlDatabase.Integration.Tests.ps1 +++ b/tests/Integration/DSC_SqlDatabase.Integration.Tests.ps1 @@ -287,6 +287,56 @@ try } } + $configurationName = "$($script:dscResourceName)_AddDatabase6_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.DatabaseName6 + $resourceCurrentState.ServerName | Should -Be $ConfigurationData.AllNodes.ServerName + $resourceCurrentState.InstanceName | Should -Be $ConfigurationData.AllNodes.InstanceName + $resourceCurrentState.SnapshotIsolation | Should -Be $ConfigurationData.AllNodes.SnapshotIsolation + } + + 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) { @@ -333,6 +383,7 @@ try $resourceCurrentState.OwnerName | Should -BeNullOrEmpty $resourceCurrentState.RecoveryModel | Should -BeNullOrEmpty $resourceCurrentState.CompatibilityLevel | Should -BeNullOrEmpty + $resourceCurrentState.SnapshotIsolation | Should -BeNullOrEmpty } It 'Should return $true when Test-DscConfiguration is run' { diff --git a/tests/Integration/DSC_SqlDatabase.config.ps1 b/tests/Integration/DSC_SqlDatabase.config.ps1 index 77d455b47..55b681d7a 100644 --- a/tests/Integration/DSC_SqlDatabase.config.ps1 +++ b/tests/Integration/DSC_SqlDatabase.config.ps1 @@ -30,11 +30,13 @@ else DatabaseName3 = 'Database3' DatabaseName4 = 'Database4' DatabaseName5 = 'Database5' + DatabaseName6 = 'Database6' Collation = 'SQL_Latin1_General_Pref_CP850_CI_AS' CompatibilityLevel = 'Version120' RecoveryModel = 'Simple' OwnerName = 'sa' + SnapshotIsolation = $true } ) } @@ -164,6 +166,31 @@ Configuration DSC_SqlDatabase_AddDatabase5_Config } } +<# + .SYNOPSIS + Creates a database with a specific snapshot isolation. +#> +Configuration DSC_SqlDatabase_AddDatabase6_Config +{ + Import-DscResource -ModuleName 'SqlServerDsc' + + node $AllNodes.NodeName + { + SqlDatabase 'Integration_Test' + { + Ensure = 'Present' + ServerName = $Node.ServerName + InstanceName = $Node.InstanceName + Name = $Node.DatabaseName6 + SnapshotIsolation = $Node.SnapshotIsolation + + PsDscRunAsCredential = New-Object ` + -TypeName System.Management.Automation.PSCredential ` + -ArgumentList @($Node.Username, (ConvertTo-SecureString -String $Node.Password -AsPlainText -Force)) + } + } +} + <# .SYNOPSIS Removes the second databases that was created.