Skip to content

Commit 80abe5c

Browse files
committed
Add FileGroup parameter to New-SqlDscDatabaseSnapshot and update tests
1 parent 103a80b commit 80abe5c

File tree

5 files changed

+247
-12
lines changed

5 files changed

+247
-12
lines changed

azure-pipelines.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ stages:
340340
'tests/Integration/Commands/Get-SqlDscDatabase.Integration.Tests.ps1'
341341
'tests/Integration/Commands/ConvertFrom-SqlDscDatabasePermission.Integration.Tests.ps1'
342342
'tests/Integration/Commands/New-SqlDscDatabase.Integration.Tests.ps1'
343+
343344
'tests/Integration/Commands/Get-SqlDscCompatibilityLevel.Integration.Tests.ps1'
344345
'tests/Integration/Commands/Set-SqlDscDatabaseProperty.Integration.Tests.ps1'
345346
'tests/Integration/Commands/Set-SqlDscDatabaseOwner.Integration.Tests.ps1'

source/Public/New-SqlDscDatabaseSnapshot.ps1

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
Specifies the source database object to snapshot. This parameter can be
2424
provided via pipeline and is used in the DatabaseObject parameter set.
2525
26+
.PARAMETER FileGroup
27+
Specifies an array of DatabaseFileGroupSpec objects that define the file groups
28+
and data files for the database snapshot. Each DatabaseFileGroupSpec contains the
29+
file group name and an array of DatabaseFileSpec objects for the sparse files.
30+
When not specified, SQL Server will create sparse files in the default data
31+
directory with automatically generated names.
32+
2633
.PARAMETER Force
2734
Specifies that the snapshot should be created without any confirmation.
2835
@@ -55,6 +62,18 @@
5562
Creates a new database snapshot named **MyDB_Snap** from the source database
5663
**MyDatabase** without prompting for confirmation.
5764
65+
.EXAMPLE
66+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
67+
$sourceDb = $serverObject.Databases['MyDatabase']
68+
69+
$dataFile = New-SqlDscDataFile -Name 'MyDatabase_Data' -FileName 'C:\Snapshots\MyDatabase_Data.ss' -AsSpec
70+
$fileGroup = New-SqlDscFileGroup -Name 'PRIMARY' -Files @($dataFile) -AsSpec
71+
72+
$serverObject | New-SqlDscDatabaseSnapshot -Name 'MyDB_Snap' -DatabaseName 'MyDatabase' -FileGroup @($fileGroup) -Force
73+
74+
Creates a new database snapshot named **MyDB_Snap** from the source database
75+
**MyDatabase** with a specified sparse file location without prompting for confirmation.
76+
5877
.INPUTS
5978
`[Microsoft.SqlServer.Management.Smo.Server]`
6079
@@ -73,6 +92,7 @@
7392
#>
7493
function New-SqlDscDatabaseSnapshot
7594
{
95+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is used in New-SqlDscDatabase')]
7696
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the rule does not yet support parsing the code when a parameter type is not available. The ScriptAnalyzer rule UseSyntacticallyCorrectExamples will always error in the editor due to https://github.com/indented-automation/Indented.ScriptAnalyzerRules/issues/8.')]
7797
[OutputType([Microsoft.SqlServer.Management.Smo.Database])]
7898
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
@@ -96,6 +116,10 @@ function New-SqlDscDatabaseSnapshot
96116
[System.String]
97117
$DatabaseName,
98118

119+
[Parameter()]
120+
[DatabaseFileGroupSpec[]]
121+
$FileGroup,
122+
99123
[Parameter()]
100124
[System.Management.Automation.SwitchParameter]
101125
$Force,
@@ -170,22 +194,17 @@ function New-SqlDscDatabaseSnapshot
170194
Name = $Name
171195
DatabaseSnapshotBaseName = $DatabaseName
172196
Force = $Force
197+
WhatIf = $WhatIfPreference
173198
}
174199

175-
if ($PSCmdlet.ParameterSetName -eq 'ServerObject' -and $Refresh.IsPresent)
176-
{
177-
$newSqlDscDatabaseParameters['Refresh'] = $true
178-
}
179-
180-
# Use WhatIf and Confirm from the current cmdlet context
181-
if ($PSBoundParameters.ContainsKey('WhatIf'))
200+
if ($PSBoundParameters.ContainsKey('FileGroup'))
182201
{
183-
$newSqlDscDatabaseParameters['WhatIf'] = $WhatIf
202+
$newSqlDscDatabaseParameters['FileGroup'] = $FileGroup
184203
}
185204

186-
if ($PSBoundParameters.ContainsKey('Confirm'))
205+
if ($PSCmdlet.ParameterSetName -eq 'ServerObject' -and $Refresh.IsPresent)
187206
{
188-
$newSqlDscDatabaseParameters['Confirm'] = $Confirm
207+
$newSqlDscDatabaseParameters['Refresh'] = $true
189208
}
190209

191210
$snapshotDatabaseObject = New-SqlDscDatabase @newSqlDscDatabaseParameters
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')]
2+
param ()
3+
4+
BeforeDiscovery {
5+
try
6+
{
7+
if (-not (Get-Module -Name 'DscResource.Test'))
8+
{
9+
# Assumes dependencies have been resolved, so if this module is not available, run 'noop' task.
10+
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
11+
{
12+
# Redirect all streams to $null, except the error stream (stream 2)
13+
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null
14+
}
15+
16+
# If the dependencies have not been resolved, this will throw an error.
17+
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
18+
}
19+
}
20+
catch [System.IO.FileNotFoundException]
21+
{
22+
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks noop" first.'
23+
}
24+
}
25+
26+
BeforeAll {
27+
$script:moduleName = 'SqlServerDsc'
28+
29+
Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop'
30+
}
31+
32+
Describe 'New-SqlDscDatabaseSnapshot' -Tag @('Integration_SQL2017', 'Integration_SQL2019', 'Integration_SQL2022') {
33+
BeforeAll {
34+
$script:mockInstanceName = 'DSCSQLTEST'
35+
$script:mockComputerName = Get-ComputerName
36+
37+
$mockSqlAdministratorUserName = 'SqlAdmin' # Using computer name as NetBIOS name throw exception.
38+
$mockSqlAdministratorPassword = ConvertTo-SecureString -String 'P@ssw0rd1' -AsPlainText -Force
39+
40+
$script:mockSqlAdminCredential = [System.Management.Automation.PSCredential]::new($mockSqlAdministratorUserName, $mockSqlAdministratorPassword)
41+
42+
$script:serverObject = Connect-SqlDscDatabaseEngine -InstanceName $script:mockInstanceName -Credential $script:mockSqlAdminCredential -ErrorAction 'Stop'
43+
44+
# Source database names - using the persistent database created by New-SqlDscDatabase integration tests
45+
$script:persistentSourceDatabase = 'SqlDscIntegrationTestDatabase_Persistent'
46+
47+
# Snapshot names
48+
$script:testSnapshotName = 'SqlDscTestSnapshot_' + (Get-Random)
49+
$script:testSnapshotNameWithFileGroup = 'SqlDscTestSnapshotFG_' + (Get-Random)
50+
$script:testSnapshotNameFromDbObject = 'SqlDscTestSnapshotDbObj_' + (Get-Random)
51+
52+
# Verify the persistent database exists before proceeding
53+
$sourceDb = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:persistentSourceDatabase -ErrorAction 'SilentlyContinue'
54+
55+
if (-not $sourceDb)
56+
{
57+
throw "The source database '$script:persistentSourceDatabase' does not exist. Please ensure New-SqlDscDatabase integration tests have run successfully."
58+
}
59+
}
60+
61+
AfterAll {
62+
# Clean up test snapshots
63+
$testSnapshotsToRemove = @(
64+
$script:testSnapshotName,
65+
$script:testSnapshotNameWithFileGroup,
66+
$script:testSnapshotNameFromDbObject
67+
)
68+
69+
foreach ($snapshotName in $testSnapshotsToRemove)
70+
{
71+
$existingSnapshot = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $snapshotName -ErrorAction 'SilentlyContinue'
72+
73+
if ($existingSnapshot)
74+
{
75+
$null = Remove-SqlDscDatabase -DatabaseObject $existingSnapshot -Force -ErrorAction 'Stop'
76+
}
77+
}
78+
79+
Disconnect-SqlDscDatabaseEngine -ServerObject $script:serverObject
80+
}
81+
82+
Context 'When creating a database snapshot using ServerObject parameter set' {
83+
It 'Should create a database snapshot successfully with minimal parameters' {
84+
$result = New-SqlDscDatabaseSnapshot -ServerObject $script:serverObject -Name $script:testSnapshotName -DatabaseName $script:persistentSourceDatabase -Force -ErrorAction 'Stop'
85+
86+
$result | Should -Not -BeNullOrEmpty
87+
$result.Name | Should -Be $script:testSnapshotName
88+
$result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Database'
89+
$result.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
90+
91+
# Verify the snapshot exists
92+
$createdSnapshot = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testSnapshotName -Refresh -ErrorAction 'Stop'
93+
$createdSnapshot | Should -Not -BeNullOrEmpty
94+
$createdSnapshot.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
95+
}
96+
97+
It 'Should throw error when trying to create a snapshot that already exists' {
98+
{ New-SqlDscDatabaseSnapshot -ServerObject $script:serverObject -Name $script:testSnapshotName -DatabaseName $script:persistentSourceDatabase -Force -ErrorAction 'Stop' } |
99+
Should -Throw
100+
}
101+
}
102+
103+
Context 'When creating a database snapshot using DatabaseObject parameter set' {
104+
BeforeAll {
105+
# Get the source database object
106+
$script:sourceDatabaseObject = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:persistentSourceDatabase -ErrorAction 'Stop'
107+
}
108+
109+
It 'Should create a database snapshot from DatabaseObject successfully' {
110+
$result = New-SqlDscDatabaseSnapshot -DatabaseObject $script:sourceDatabaseObject -Name $script:testSnapshotNameFromDbObject -Force -ErrorAction 'Stop'
111+
112+
$result | Should -Not -BeNullOrEmpty
113+
$result.Name | Should -Be $script:testSnapshotNameFromDbObject
114+
$result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Database'
115+
$result.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
116+
117+
# Verify the snapshot exists
118+
$createdSnapshot = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testSnapshotNameFromDbObject -Refresh -ErrorAction 'Stop'
119+
$createdSnapshot | Should -Not -BeNullOrEmpty
120+
$createdSnapshot.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
121+
}
122+
}
123+
124+
Context 'When creating a database snapshot with custom file groups' {
125+
BeforeAll {
126+
# Get the default data directory from the server
127+
$script:dataDirectory = $script:serverObject.Settings.DefaultFile
128+
129+
if (-not $script:dataDirectory)
130+
{
131+
$script:dataDirectory = $script:serverObject.Information.MasterDBPath
132+
}
133+
134+
# Ensure the directory exists
135+
if (-not (Test-Path -Path $script:dataDirectory))
136+
{
137+
$null = New-Item -Path $script:dataDirectory -ItemType Directory -Force
138+
}
139+
140+
# Get the source database for file group creation
141+
$script:sourceDatabaseForFG = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:persistentSourceDatabase -ErrorAction 'Stop'
142+
}
143+
144+
It 'Should create a database snapshot with custom sparse file location' {
145+
# Create PRIMARY filegroup with sparse file using -AsSpec
146+
$sparseFilePath = Join-Path -Path $script:dataDirectory -ChildPath ($script:testSnapshotNameWithFileGroup + '_Sparse.ss')
147+
$dataFileSpec = New-SqlDscDataFile -Name ($script:testSnapshotNameWithFileGroup + '_Sparse') -FileName $sparseFilePath -AsSpec
148+
$primaryFileGroupSpec = New-SqlDscFileGroup -Name 'PRIMARY' -Files @($dataFileSpec) -AsSpec
149+
150+
# Create snapshot with custom file group
151+
$result = New-SqlDscDatabaseSnapshot -ServerObject $script:serverObject -Name $script:testSnapshotNameWithFileGroup -DatabaseName $script:persistentSourceDatabase -FileGroup @($primaryFileGroupSpec) -Force -ErrorAction 'Stop'
152+
153+
$result | Should -Not -BeNullOrEmpty
154+
$result.Name | Should -Be $script:testSnapshotNameWithFileGroup
155+
$result | Should -BeOfType 'Microsoft.SqlServer.Management.Smo.Database'
156+
$result.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
157+
158+
# Verify the snapshot exists with correct file configuration
159+
$createdSnapshot = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:testSnapshotNameWithFileGroup -Refresh -ErrorAction 'Stop'
160+
$createdSnapshot | Should -Not -BeNullOrEmpty
161+
$createdSnapshot.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
162+
163+
# Verify the sparse file was created
164+
$createdSnapshot.FileGroups['PRIMARY'] | Should -Not -BeNullOrEmpty
165+
$createdSnapshot.FileGroups['PRIMARY'].Files.Count | Should -BeGreaterThan 0
166+
}
167+
}
168+
169+
Context 'When using the Refresh parameter' {
170+
BeforeAll {
171+
$script:refreshTestSnapshotName = 'SqlDscTestSnapshotRefresh_' + (Get-Random)
172+
}
173+
174+
AfterAll {
175+
# Clean up the refresh test snapshot
176+
$snapshotToRemove = Get-SqlDscDatabase -ServerObject $script:serverObject -Name $script:refreshTestSnapshotName -ErrorAction 'SilentlyContinue'
177+
if ($snapshotToRemove)
178+
{
179+
$null = Remove-SqlDscDatabase -DatabaseObject $snapshotToRemove -Force -ErrorAction 'Stop'
180+
}
181+
}
182+
183+
It 'Should refresh the database collection before creating snapshot' {
184+
$result = New-SqlDscDatabaseSnapshot -ServerObject $script:serverObject -Name $script:refreshTestSnapshotName -DatabaseName $script:persistentSourceDatabase -Refresh -Force -ErrorAction 'Stop'
185+
186+
$result | Should -Not -BeNullOrEmpty
187+
$result.Name | Should -Be $script:refreshTestSnapshotName
188+
$result.DatabaseSnapshotBaseName | Should -Be $script:persistentSourceDatabase
189+
}
190+
}
191+
}

tests/Integration/Commands/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Revoke-SqlDscServerPermission | 4 | 4 (New-SqlDscLogin), 1 (Install-SqlDscServer
9191
Get-SqlDscDatabase | 4 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
9292
ConvertFrom-SqlDscDatabasePermission | 4 | 0 (Prerequisites) | - | -
9393
New-SqlDscDatabase | 4 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | SqlDscIntegrationTestDatabase_Persistent database
94+
New-SqlDscDatabaseSnapshot | 5 | 4 (New-SqlDscDatabase), 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
9495
Get-SqlDscCompatibilityLevel | 4 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
9596
Set-SqlDscDatabaseProperty | 4 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -
9697
Set-SqlDscDatabaseOwner | 4 | 1 (Install-SqlDscServer), 0 (Prerequisites) | DSCSQLTEST | -

tests/Unit/Public/New-SqlDscDatabaseSnapshot.Tests.ps1

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,29 @@ Describe 'New-SqlDscDatabaseSnapshot' -Tag 'Public' {
105105
$Refresh -eq $true
106106
} -Exactly -Times 1
107107
}
108+
109+
It 'Should pass FileGroup parameter when specified' {
110+
InModuleScope -Parameters @{
111+
mockServerObject = $mockServerObject
112+
} -ScriptBlock {
113+
# Create a mock DatabaseFileGroupSpec
114+
$mockDataFileSpec = [DatabaseFileSpec]@{
115+
Name = 'TestData'
116+
FileName = 'C:\Snapshots\TestData.ss'
117+
}
118+
119+
$mockFileGroupSpec = [DatabaseFileGroupSpec]@{
120+
Name = 'PRIMARY'
121+
Files = @($mockDataFileSpec)
122+
}
123+
124+
$result = New-SqlDscDatabaseSnapshot -ServerObject $mockServerObject -Name 'TestSnapshot' -DatabaseName 'SourceDatabase' -FileGroup @($mockFileGroupSpec) -Force
125+
126+
Should -Invoke -CommandName 'New-SqlDscDatabase' -ParameterFilter {
127+
$FileGroup -and $FileGroup.Count -eq 1 -and $FileGroup[0].Name -eq 'PRIMARY'
128+
} -Exactly -Times 1
129+
}
130+
}
108131
}
109132

110133
Context 'When creating a database snapshot using DatabaseObject parameter set' {
@@ -235,7 +258,7 @@ Describe 'New-SqlDscDatabaseSnapshot' -Tag 'Public' {
235258
It 'Should have the correct parameters in parameter set ServerObject' -ForEach @(
236259
@{
237260
ExpectedParameterSetName = 'ServerObject'
238-
ExpectedParameters = '-ServerObject <Server> -Name <string> -DatabaseName <string> [-Force] [-Refresh] [-WhatIf] [-Confirm] [<CommonParameters>]'
261+
ExpectedParameters = '-ServerObject <Server> -Name <string> -DatabaseName <string> [-FileGroup <DatabaseFileGroupSpec[]>] [-Force] [-Refresh] [-WhatIf] [-Confirm] [<CommonParameters>]'
239262
}
240263
) {
241264
$result = (Get-Command -Name 'New-SqlDscDatabaseSnapshot').ParameterSets |
@@ -252,7 +275,7 @@ Describe 'New-SqlDscDatabaseSnapshot' -Tag 'Public' {
252275
It 'Should have the correct parameters in parameter set DatabaseObject' -ForEach @(
253276
@{
254277
ExpectedParameterSetName = 'DatabaseObject'
255-
ExpectedParameters = '-DatabaseObject <Database> -Name <string> [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]'
278+
ExpectedParameters = '-DatabaseObject <Database> -Name <string> [-FileGroup <DatabaseFileGroupSpec[]>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]'
256279
}
257280
) {
258281
$result = (Get-Command -Name 'New-SqlDscDatabaseSnapshot').ParameterSets |

0 commit comments

Comments
 (0)