Skip to content

Commit 27d73c1

Browse files
authored
Add backup and restore Report Server encryption key (dsccommunity#2413)
1 parent f583104 commit 27d73c1

14 files changed

+1495
-34
lines changed

CHANGELOG.md

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

88
### Added
99

10-
- Added public commands `Start-SqlDscRSWindowsService`, `Stop-SqlDscRSWindowsService`, `Start-SqlDscRSWebService`, and `Stop-SqlDscRSWebService` to manage Reporting Services Windows and web services using the `SetServiceState` WMI method.
10+
- Added public commands `Start-SqlDscRSWindowsService`, `Stop-SqlDscRSWindowsService`,
11+
`Start-SqlDscRSWebService`, and `Stop-SqlDscRSWebService` to manage Reporting
12+
Services Windows and web services using the `SetServiceState` WMI method.
1113
- SqlServerDsc
1214
- Added class `ReportServerUri` to represent URLs returned by the
1315
`GetReportServerUrls` CIM method on `MSReportServer_Instance`.
@@ -222,6 +224,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
222224
wrap the `ListSSLCertificateBindings`, `CreateSSLCertificateBinding`, and
223225
`RemoveSSLCertificateBinding` CIM methods. The `Set-SqlDscRSSslCertificateBinding`
224226
command provides a declarative approach to set SSL bindings to an exact list.
227+
- Added public commands `Backup-SqlDscRSEncryptionKey` and
228+
`Restore-SqlDscRSEncryptionKey` to backup and restore Reporting Services
229+
encryption keys. These commands wrap the `BackupEncryptionKey` and
230+
`RestoreEncryptionKey` CIM methods and require a password to secure the key.
225231
- Added public command `New-SqlDscRSEncryptionKey` to delete and regenerate the
226232
Reporting Services encryption key. Wraps the `DeleteEncryptionKey` CIM method.
227233
Warning: This operation cannot be undone and renders all encrypted content

azure-pipelines.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,13 @@ stages:
798798
'tests/Integration/Commands/Set-SqlDscRSUnattendedExecutionAccount.Integration.Tests.ps1'
799799
'tests/Integration/Commands/Set-SqlDscRSSmtpConfiguration.Integration.Tests.ps1'
800800
'tests/Integration/Commands/Remove-SqlDscRSUnattendedExecutionAccount.Integration.Tests.ps1'
801+
# Group 8 - Service account change with encryption key backup/restore
802+
'tests/Integration/Commands/Pre.ServiceAccountChange.Secure.RS.Integration.Tests.ps1'
803+
'tests/Integration/Commands/Backup-SqlDscRSEncryptionKey.Integration.Tests.ps1'
804+
'tests/Integration/Commands/Set-SqlDscRSServiceAccount.Integration.Tests.ps1'
805+
'tests/Integration/Commands/Mid.ServiceAccountChange.Secure.RS.Integration.Tests.ps1'
806+
'tests/Integration/Commands/Restore-SqlDscRSEncryptionKey.Integration.Tests.ps1'
807+
'tests/Integration/Commands/Post.ServiceAccountChange.Secure.RS.Integration.Tests.ps1'
801808
)
802809
name: test
803810
displayName: 'Run Integration Test'
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<#
2+
.SYNOPSIS
3+
Backs up the encryption key for SQL Server Reporting Services.
4+
5+
.DESCRIPTION
6+
Backs up the encryption key for SQL Server Reporting Services or
7+
Power BI Report Server by calling the `BackupEncryptionKey` method
8+
on the `MSReportServer_ConfigurationSetting` CIM instance.
9+
10+
The encryption key is essential for decrypting stored credentials
11+
and connection strings in the report server database. This backup
12+
should be stored securely and is required for disaster recovery
13+
or migration scenarios.
14+
15+
The configuration CIM instance can be obtained using the
16+
`Get-SqlDscRSConfiguration` command and passed via the pipeline.
17+
18+
.PARAMETER Configuration
19+
Specifies the `MSReportServer_ConfigurationSetting` CIM instance for
20+
the Reporting Services instance. This can be obtained using the
21+
`Get-SqlDscRSConfiguration` command. This parameter accepts pipeline
22+
input.
23+
24+
.PARAMETER Path
25+
Specifies the full path where the encryption key backup file will
26+
be saved. The file extension should be .snk (Strong Name Key).
27+
28+
.PARAMETER Password
29+
Specifies the password to protect the encryption key backup file.
30+
This password will be required when restoring the encryption key.
31+
32+
.PARAMETER Credential
33+
Specifies the credentials to use when accessing a UNC path. Use this
34+
parameter when the Path is a network share that requires authentication.
35+
36+
.PARAMETER DriveName
37+
Specifies the name of the temporary PSDrive to create when accessing
38+
a UNC path with credentials. Defaults to 'RSKeyBackup'. This parameter
39+
can only be used together with the Credential parameter.
40+
41+
.PARAMETER PassThru
42+
If specified, returns the configuration CIM instance after backing
43+
up the encryption key.
44+
45+
.PARAMETER Force
46+
If specified, suppresses the confirmation prompt.
47+
48+
.EXAMPLE
49+
$password = Read-Host -AsSecureString -Prompt 'Enter backup password'
50+
Get-SqlDscRSConfiguration -InstanceName 'SSRS' | Backup-SqlDscRSEncryptionKey -Path 'C:\Backup\RSKey.snk' -Password $password
51+
52+
Backs up the encryption key to a local file.
53+
54+
.EXAMPLE
55+
$password = ConvertTo-SecureString -String 'MyP@ssw0rd' -AsPlainText -Force
56+
$config = Get-SqlDscRSConfiguration -InstanceName 'SSRS'
57+
Backup-SqlDscRSEncryptionKey -Configuration $config -Path '\\Server\Share\RSKey.snk' -Password $password -Credential (Get-Credential) -Force
58+
59+
Backs up the encryption key to a UNC path with credentials and
60+
without confirmation.
61+
62+
.EXAMPLE
63+
$password = Read-Host -AsSecureString -Prompt 'Enter backup password'
64+
Get-SqlDscRSConfiguration -InstanceName 'SSRS' | Backup-SqlDscRSEncryptionKey -Path 'C:\Backup\RSKey.snk' -Password $password -PassThru
65+
66+
Backs up the encryption key and returns the configuration CIM instance.
67+
68+
.INPUTS
69+
`Microsoft.Management.Infrastructure.CimInstance`
70+
71+
Accepts MSReportServer_ConfigurationSetting CIM instance via pipeline.
72+
73+
.OUTPUTS
74+
None. By default, this command does not generate any output.
75+
76+
.OUTPUTS
77+
`Microsoft.Management.Infrastructure.CimInstance`
78+
79+
When PassThru is specified, returns the MSReportServer_ConfigurationSetting
80+
CIM instance.
81+
82+
.NOTES
83+
Store the backup file and password securely. They are required to
84+
restore the encryption key in disaster recovery scenarios or when
85+
migrating to a new server.
86+
87+
.LINK
88+
https://docs.microsoft.com/en-us/sql/reporting-services/wmi-provider-library-reference/configurationsetting-method-backupencryptionkey
89+
#>
90+
function Backup-SqlDscRSEncryptionKey
91+
{
92+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the examples use pipeline input the rule cannot validate.')]
93+
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
94+
[OutputType([Microsoft.Management.Infrastructure.CimInstance])]
95+
param
96+
(
97+
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
98+
[System.Object]
99+
$Configuration,
100+
101+
[Parameter(Mandatory = $true)]
102+
[System.String]
103+
$Path,
104+
105+
[Parameter(Mandatory = $true)]
106+
[System.Security.SecureString]
107+
$Password,
108+
109+
[Parameter(ParameterSetName = 'ByCredential')]
110+
[System.Management.Automation.PSCredential]
111+
$Credential,
112+
113+
[Parameter(ParameterSetName = 'ByCredential')]
114+
[ValidateNotNullOrEmpty()]
115+
[System.String]
116+
$DriveName = 'RSKeyBackup',
117+
118+
[Parameter()]
119+
[System.Management.Automation.SwitchParameter]
120+
$PassThru,
121+
122+
[Parameter()]
123+
[System.Management.Automation.SwitchParameter]
124+
$Force
125+
)
126+
127+
process
128+
{
129+
if ($Force.IsPresent -and -not $Confirm)
130+
{
131+
$ConfirmPreference = 'None'
132+
}
133+
134+
$instanceName = $Configuration.InstanceName
135+
136+
Write-Verbose -Message ($script:localizedData.Backup_SqlDscRSEncryptionKey_BackingUp -f $instanceName, $Path)
137+
138+
$descriptionMessage = $script:localizedData.Backup_SqlDscRSEncryptionKey_ShouldProcessDescription -f $instanceName, $Path
139+
$confirmationMessage = $script:localizedData.Backup_SqlDscRSEncryptionKey_ShouldProcessConfirmation -f $instanceName
140+
$captionMessage = $script:localizedData.Backup_SqlDscRSEncryptionKey_ShouldProcessCaption
141+
142+
if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage))
143+
{
144+
# Convert SecureString to plain text for the WMI method
145+
$passwordBstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
146+
147+
try
148+
{
149+
$passwordPlainText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($passwordBstr)
150+
151+
$invokeRsCimMethodParameters = @{
152+
CimInstance = $Configuration
153+
MethodName = 'BackupEncryptionKey'
154+
Arguments = @{
155+
Password = $passwordPlainText
156+
}
157+
}
158+
159+
$result = Invoke-RsCimMethod @invokeRsCimMethodParameters -ErrorAction 'Stop'
160+
161+
# The WMI method returns the key as a byte array in the KeyFile property
162+
$keyData = $result.KeyFile
163+
164+
# Handle UNC path with credentials
165+
$psDriveCreated = $false
166+
$targetPath = $Path
167+
168+
if ($PSBoundParameters.ContainsKey('Credential'))
169+
{
170+
$parentPath = Split-Path -Path $Path -Parent
171+
172+
if ($parentPath -match '^\\\\')
173+
{
174+
New-PSDrive -Name $DriveName -PSProvider 'FileSystem' -Root $parentPath -Credential $Credential -ErrorAction 'Stop' | Out-Null
175+
176+
$psDriveCreated = $true
177+
$fileName = Split-Path -Path $Path -Leaf
178+
$targetPath = (Resolve-Path -LiteralPath "${DriveName}:\$fileName").ProviderPath
179+
}
180+
}
181+
182+
try
183+
{
184+
# Write the key data to file
185+
[System.IO.File]::WriteAllBytes($targetPath, $keyData)
186+
}
187+
finally
188+
{
189+
if ($psDriveCreated)
190+
{
191+
Remove-PSDrive -Name $DriveName -Force -ErrorAction 'SilentlyContinue'
192+
}
193+
}
194+
}
195+
catch
196+
{
197+
$errorMessage = $script:localizedData.Backup_SqlDscRSEncryptionKey_FailedToBackup -f $instanceName
198+
199+
$exception = New-Exception -Message $errorMessage -ErrorRecord $_
200+
201+
$errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'BSRSEK0001' -ErrorCategory 'InvalidOperation' -TargetObject $Configuration
202+
203+
$PSCmdlet.ThrowTerminatingError($errorRecord)
204+
}
205+
finally
206+
{
207+
# Clear the plain text password from memory
208+
[System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordBstr)
209+
}
210+
}
211+
212+
if ($PassThru.IsPresent)
213+
{
214+
return $Configuration
215+
}
216+
}
217+
}

0 commit comments

Comments
 (0)