Skip to content

Commit 0b3fedd

Browse files
committed
Backup-SqlDscDatabase: Add new command for database backups
1 parent d621ea4 commit 0b3fedd

8 files changed

Lines changed: 1045 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
### Added
99

10+
- Added public command `Backup-SqlDscDatabase` to perform database backups using
11+
SMO's `Microsoft.SqlServer.Management.Smo.Backup` class. Supports full,
12+
differential, and transaction log backups with options for compression,
13+
copy-only, checksum, and retention. Accepts both Server and Database objects
14+
via pipeline
15+
([issue #2365](https://github.com/dsccommunity/SqlServerDsc/issues/2365)).
1016
- `SqlPermission`
1117
- Added integration tests for server role permissions to complement the
1218
existing login permission tests.

azure-pipelines.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ stages:
341341
'tests/Integration/Commands/ConvertFrom-SqlDscDatabasePermission.Integration.Tests.ps1'
342342
'tests/Integration/Commands/New-SqlDscDatabase.Integration.Tests.ps1'
343343
'tests/Integration/Commands/New-SqlDscDatabaseSnapshot.Integration.Tests.ps1'
344+
'tests/Integration/Commands/Backup-SqlDscDatabase.Integration.Tests.ps1'
344345
'tests/Integration/Commands/Resume-SqlDscDatabase.Integration.Tests.ps1'
345346
'tests/Integration/Commands/Suspend-SqlDscDatabase.Integration.Tests.ps1'
346347
'tests/Integration/Commands/Get-SqlDscCompatibilityLevel.Integration.Tests.ps1'
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
<#
2+
.SYNOPSIS
3+
Performs a backup of a SQL Server database.
4+
5+
.DESCRIPTION
6+
This command performs a backup of a SQL Server database using SQL Server
7+
Management Objects (SMO). It supports full, differential, and transaction
8+
log backups with options for compression, verification, and copy-only
9+
backups.
10+
11+
.PARAMETER ServerObject
12+
Specifies the current server connection object.
13+
14+
.PARAMETER DatabaseObject
15+
Specifies a database object to backup.
16+
17+
.PARAMETER Name
18+
Specifies the name of the database to backup.
19+
20+
.PARAMETER BackupFile
21+
Specifies the full path to the backup file. For full and differential
22+
backups, use the .bak extension. For transaction log backups, use the
23+
.trn extension.
24+
25+
.PARAMETER BackupType
26+
Specifies the type of backup to perform. Valid values are 'Full',
27+
'Differential', and 'Log'. Default value is 'Full'.
28+
29+
.PARAMETER CopyOnly
30+
Specifies that a copy-only backup should be performed. Copy-only backups
31+
do not affect the sequence of conventional backups and are useful for
32+
taking backups for special purposes without disrupting the normal backup
33+
chain. This is particularly useful in AlwaysOn Availability Group scenarios.
34+
35+
.PARAMETER Compress
36+
Specifies that the backup should be compressed. Backup compression requires
37+
SQL Server 2008 Enterprise or later, or SQL Server 2008 R2 Standard or later.
38+
39+
.PARAMETER Checksum
40+
Specifies that checksums should be calculated and verified during the
41+
backup operation to help detect backup media errors.
42+
43+
.PARAMETER Description
44+
Specifies a description for the backup set. This description is stored
45+
in the backup media and can be useful for identifying backups.
46+
47+
.PARAMETER RetainDays
48+
Specifies the number of days that must elapse before the backup media
49+
can be overwritten. This provides protection against accidental overwrites.
50+
51+
.PARAMETER Initialize
52+
Specifies that the backup media should be initialized (overwritten) rather
53+
than appending to existing backup sets. Use with caution as this will
54+
destroy any existing backups on the media.
55+
56+
.PARAMETER Refresh
57+
Specifies that the **ServerObject**'s databases should be refreshed before
58+
accessing the database. This is helpful when databases could have been
59+
modified outside of the **ServerObject**, for example through T-SQL. But
60+
on instances with a large amount of databases it might be better to make
61+
sure the **ServerObject** is recent enough, or pass in **DatabaseObject**.
62+
63+
.PARAMETER Force
64+
Specifies that the backup should be performed without any confirmation.
65+
66+
.EXAMPLE
67+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
68+
$serverObject | Backup-SqlDscDatabase -Name 'MyDatabase' -BackupFile 'C:\Backups\MyDatabase.bak'
69+
70+
Performs a full backup of the database named **MyDatabase** to the specified
71+
backup file.
72+
73+
.EXAMPLE
74+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
75+
$databaseObject = $serverObject | Get-SqlDscDatabase -Name 'MyDatabase'
76+
$databaseObject | Backup-SqlDscDatabase -BackupFile 'C:\Backups\MyDatabase.bak' -Force
77+
78+
Performs a full backup of the database named **MyDatabase** using a
79+
database object from the pipeline, without prompting for confirmation.
80+
81+
.EXAMPLE
82+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
83+
$serverObject | Backup-SqlDscDatabase -Name 'MyDatabase' -BackupFile 'C:\Backups\MyDatabase_Diff.bak' -BackupType 'Differential'
84+
85+
Performs a differential backup of the database named **MyDatabase**.
86+
87+
.EXAMPLE
88+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
89+
$serverObject | Backup-SqlDscDatabase -Name 'MyDatabase' -BackupFile 'C:\Backups\MyDatabase.trn' -BackupType 'Log'
90+
91+
Performs a transaction log backup of the database named **MyDatabase**.
92+
93+
.EXAMPLE
94+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
95+
$serverObject | Backup-SqlDscDatabase -Name 'MyDatabase' -BackupFile 'C:\Backups\MyDatabase_CopyOnly.bak' -CopyOnly
96+
97+
Performs a copy-only full backup of the database named **MyDatabase**.
98+
This backup does not affect the normal backup chain.
99+
100+
.EXAMPLE
101+
$serverObject = Connect-SqlDscDatabaseEngine -InstanceName 'MyInstance'
102+
$serverObject | Backup-SqlDscDatabase -Name 'MyDatabase' -BackupFile 'C:\Backups\MyDatabase.bak' -Compress -Checksum
103+
104+
Performs a compressed full backup with checksum verification of the
105+
database named **MyDatabase**.
106+
107+
.INPUTS
108+
`Microsoft.SqlServer.Management.Smo.Server`
109+
110+
Server object accepted from the pipeline (ServerObject parameter set).
111+
112+
.INPUTS
113+
`Microsoft.SqlServer.Management.Smo.Database`
114+
115+
Database object accepted from the pipeline (DatabaseObject parameter set).
116+
117+
.OUTPUTS
118+
None.
119+
#>
120+
function Backup-SqlDscDatabase
121+
{
122+
[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.')]
123+
[OutputType()]
124+
[CmdletBinding(DefaultParameterSetName = 'ServerObject', SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
125+
param
126+
(
127+
[Parameter(ParameterSetName = 'ServerObject', Mandatory = $true, ValueFromPipeline = $true)]
128+
[Microsoft.SqlServer.Management.Smo.Server]
129+
$ServerObject,
130+
131+
[Parameter(ParameterSetName = 'DatabaseObject', Mandatory = $true, ValueFromPipeline = $true)]
132+
[Microsoft.SqlServer.Management.Smo.Database]
133+
$DatabaseObject,
134+
135+
[Parameter(ParameterSetName = 'ServerObject', Mandatory = $true)]
136+
[ValidateNotNullOrEmpty()]
137+
[System.String]
138+
$Name,
139+
140+
[Parameter(Mandatory = $true)]
141+
[ValidateNotNullOrEmpty()]
142+
[System.String]
143+
$BackupFile,
144+
145+
[Parameter()]
146+
[ValidateSet('Full', 'Differential', 'Log')]
147+
[System.String]
148+
$BackupType = 'Full',
149+
150+
[Parameter()]
151+
[System.Management.Automation.SwitchParameter]
152+
$CopyOnly,
153+
154+
[Parameter()]
155+
[System.Management.Automation.SwitchParameter]
156+
$Compress,
157+
158+
[Parameter()]
159+
[System.Management.Automation.SwitchParameter]
160+
$Checksum,
161+
162+
[Parameter()]
163+
[ValidateNotNullOrEmpty()]
164+
[System.String]
165+
$Description,
166+
167+
[Parameter()]
168+
[ValidateRange(0, 99999)]
169+
[System.Int32]
170+
$RetainDays,
171+
172+
[Parameter()]
173+
[System.Management.Automation.SwitchParameter]
174+
$Initialize,
175+
176+
[Parameter(ParameterSetName = 'ServerObject')]
177+
[System.Management.Automation.SwitchParameter]
178+
$Refresh,
179+
180+
[Parameter()]
181+
[System.Management.Automation.SwitchParameter]
182+
$Force
183+
)
184+
185+
begin
186+
{
187+
if ($Force.IsPresent -and -not $Confirm)
188+
{
189+
$ConfirmPreference = 'None'
190+
}
191+
}
192+
193+
process
194+
{
195+
if ($PSCmdlet.ParameterSetName -eq 'ServerObject')
196+
{
197+
if ($Refresh.IsPresent)
198+
{
199+
# Refresh the server object's databases collection
200+
$ServerObject.Databases.Refresh()
201+
}
202+
203+
# Get the database object
204+
$DatabaseObject = $ServerObject.Databases[$Name]
205+
206+
if (-not $DatabaseObject)
207+
{
208+
$errorMessage = $script:localizedData.Backup_SqlDscDatabase_NotFound -f $Name
209+
210+
$PSCmdlet.ThrowTerminatingError(
211+
[System.Management.Automation.ErrorRecord]::new(
212+
[System.Management.Automation.ItemNotFoundException]::new($errorMessage),
213+
'BSDD0001', # cspell: disable-line
214+
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
215+
$Name
216+
)
217+
)
218+
}
219+
}
220+
else
221+
{
222+
$Name = $DatabaseObject.Name
223+
$ServerObject = $DatabaseObject.Parent
224+
}
225+
226+
# Validate that log backups are only performed on databases with FULL or BULK_LOGGED recovery model
227+
if ($BackupType -eq 'Log')
228+
{
229+
$recoveryModel = $DatabaseObject.RecoveryModel
230+
231+
if ($recoveryModel -eq [Microsoft.SqlServer.Management.Smo.RecoveryModel]::Simple)
232+
{
233+
$errorMessage = $script:localizedData.Database_Backup_LogBackupSimpleRecoveryModel -f $Name
234+
235+
$PSCmdlet.ThrowTerminatingError(
236+
[System.Management.Automation.ErrorRecord]::new(
237+
[System.InvalidOperationException]::new($errorMessage),
238+
'BSDD0002', # cspell: disable-line
239+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
240+
$DatabaseObject
241+
)
242+
)
243+
}
244+
}
245+
246+
# Validate that the database is online
247+
if ($DatabaseObject.Status -ne [Microsoft.SqlServer.Management.Smo.DatabaseStatus]::Normal)
248+
{
249+
$errorMessage = $script:localizedData.Database_Backup_DatabaseNotOnline -f $Name, $DatabaseObject.Status
250+
251+
$PSCmdlet.ThrowTerminatingError(
252+
[System.Management.Automation.ErrorRecord]::new(
253+
[System.InvalidOperationException]::new($errorMessage),
254+
'BSDD0003', # cspell: disable-line
255+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
256+
$DatabaseObject
257+
)
258+
)
259+
}
260+
261+
# Determine the backup type description for messages
262+
$backupTypeDescription = switch ($BackupType)
263+
{
264+
'Full' { 'full' }
265+
'Differential' { 'differential' }
266+
'Log' { 'transaction log' }
267+
}
268+
269+
$descriptionMessage = $script:localizedData.Database_Backup_ShouldProcessVerboseDescription -f $backupTypeDescription, $Name, $BackupFile
270+
$confirmationMessage = $script:localizedData.Database_Backup_ShouldProcessVerboseWarning -f $backupTypeDescription, $Name
271+
$captionMessage = $script:localizedData.Database_Backup_ShouldProcessCaption
272+
273+
if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage))
274+
{
275+
Write-Verbose -Message ($script:localizedData.Database_Backup_BackingUp -f $backupTypeDescription, $Name, $BackupFile)
276+
277+
try
278+
{
279+
# Create the backup object
280+
$backup = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.Backup'
281+
282+
# Set the database name
283+
$backup.Database = $Name
284+
285+
# Set the backup action type based on BackupType parameter
286+
switch ($BackupType)
287+
{
288+
'Full'
289+
{
290+
$backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Database
291+
$backup.Incremental = $false
292+
}
293+
294+
'Differential'
295+
{
296+
$backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Database
297+
$backup.Incremental = $true
298+
}
299+
300+
'Log'
301+
{
302+
$backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Log
303+
}
304+
}
305+
306+
# Create and add the backup device
307+
$backupDevice = New-Object -TypeName 'Microsoft.SqlServer.Management.Smo.BackupDeviceItem' -ArgumentList $BackupFile, 'File'
308+
$backup.Devices.Add($backupDevice)
309+
310+
# Set optional properties
311+
if ($CopyOnly.IsPresent)
312+
{
313+
$backup.CopyOnly = $true
314+
}
315+
316+
if ($Compress.IsPresent)
317+
{
318+
$backup.CompressionOption = [Microsoft.SqlServer.Management.Smo.BackupCompressionOptions]::On
319+
}
320+
321+
if ($Checksum.IsPresent)
322+
{
323+
$backup.Checksum = $true
324+
}
325+
326+
if ($PSBoundParameters.ContainsKey('Description'))
327+
{
328+
$backup.BackupSetDescription = $Description
329+
}
330+
331+
if ($PSBoundParameters.ContainsKey('RetainDays'))
332+
{
333+
$backup.RetainDays = $RetainDays
334+
}
335+
336+
if ($Initialize.IsPresent)
337+
{
338+
$backup.Initialize = $true
339+
}
340+
341+
# Perform the backup
342+
$backup.SqlBackup($ServerObject)
343+
344+
Write-Verbose -Message ($script:localizedData.Database_Backup_Success -f $backupTypeDescription, $Name)
345+
}
346+
catch
347+
{
348+
$errorMessage = $script:localizedData.Database_Backup_Failed -f $backupTypeDescription, $Name, $ServerObject.InstanceName
349+
350+
$PSCmdlet.ThrowTerminatingError(
351+
[System.Management.Automation.ErrorRecord]::new(
352+
[System.InvalidOperationException]::new($errorMessage, $_.Exception),
353+
'BSDD0004', # cspell: disable-line
354+
[System.Management.Automation.ErrorCategory]::InvalidOperation,
355+
$DatabaseObject
356+
)
357+
)
358+
}
359+
}
360+
}
361+
}

0 commit comments

Comments
 (0)