Skip to content

Commit b9d7f8f

Browse files
authored
Add functions to manage report server database creation and permissions (#2389)
1 parent a2b0d40 commit b9d7f8f

34 files changed

Lines changed: 2433 additions & 172 deletions

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@
107107
"varchar",
108108
"maxdop",
109109
"hotfixes",
110-
"checkpointing"
110+
"checkpointing",
111+
"HRESULT"
111112
],
112113
"cSpell.ignorePaths": [
113114
".git"

CHANGELOG.md

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

88
### Added
99

10+
- Added public command `Request-SqlDscRSDatabaseScript` to generate T-SQL scripts
11+
for creating report server databases. Wraps the `GenerateDatabaseCreationScript`
12+
CIM method and supports configuring database name, language (LCID), and
13+
SharePoint mode ([issue #2017](https://github.com/dsccommunity/SqlServerDsc/issues/2017)).
14+
- Added public command `Request-SqlDscRSDatabaseRightsScript` to generate T-SQL
15+
scripts for granting permissions on report server databases. Wraps the
16+
`GenerateDatabaseRightsScript` CIM method and supports configuring database
17+
name, user name, remote connections, and Windows/SQL authentication types
18+
([issue #2019](https://github.com/dsccommunity/SqlServerDsc/issues/2019)).
19+
- Added public command `Set-SqlDscRSDatabaseConnection` to set
20+
the report server database connection for SQL Server Reporting Services or
21+
Power BI Report Server. Wraps the `SetDatabaseConnection` CIM method and
22+
supports Windows, SQL Server, and Service Account authentication types
23+
([issue #2021](https://github.com/dsccommunity/SqlServerDsc/issues/2021)).
1024
- Added public command `Set-SqlDscRSVirtualDirectory` to set the virtual directory
1125
for Reporting Services applications. Wraps the `SetVirtualDirectory` CIM method
1226
and supports ReportServerWebService, ReportServerWebApp, and ReportManager
@@ -47,6 +61,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4761
Services configuration instances with consistent error handling. This function
4862
is used by `Enable-SqlDscRsSecureConnection`, `Disable-SqlDscRsSecureConnection`,
4963
and the `SqlRS` resource.
64+
- Added private function `Get-HResultMessage` to translate common Windows HRESULT
65+
error codes into human-readable messages. Used by `Invoke-RsCimMethod` to
66+
provide actionable error messages when Reporting Services CIM methods fail
67+
without detailed error information.
5068
- `Invoke-ReportServerSetupAction`
5169
- Now uses `Format-Path` with `-ExpandEnvironmentVariable` to expand environment
5270
variables in all path parameters (`MediaPath`, `LogPath`, `InstallFolder`)
@@ -185,6 +203,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
185203
- Refactored to use the public command `Set-SqlDscRSVirtualDirectory` for
186204
setting virtual directories instead of calling the CIM method directly
187205
([issue #2015](https://github.com/dsccommunity/SqlServerDsc/issues/2015)).
206+
- Refactored to use the public commands `Request-SqlDscRSDatabaseScript`,
207+
`Request-SqlDscRSDatabaseRightsScript`, and `Set-SqlDscRSDatabaseConnection`
208+
for creating and configuring the report server database instead of calling
209+
the CIM methods directly
210+
([issue #2017](https://github.com/dsccommunity/SqlServerDsc/issues/2017))
211+
([issue #2019](https://github.com/dsccommunity/SqlServerDsc/issues/2019))
212+
([issue #2021](https://github.com/dsccommunity/SqlServerDsc/issues/2021)).
188213
- `Assert-SetupActionProperties`
189214
- Refactored to use the command `Get-FileVersion` from the DscResource.Common
190215
module instead of the private function `Get-FileVersionInformation`
@@ -201,6 +226,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
201226

202227
### Fixed
203228

229+
- `Invoke-RsCimMethod`
230+
- Enhanced error messages to include human-readable translations of common
231+
HRESULT error codes. When Reporting Services CIM methods fail without
232+
detailed error information, the error message now includes actionable
233+
guidance based on the HRESULT code (e.g., service not running, access
234+
denied, logon type not granted).
235+
- Fixed error handling to properly surface error details. Previously, when
236+
the `ExtendedErrors` property existed but was empty, the error message
237+
would show an empty error description. Now it correctly falls back to
238+
the `Error` property and provides a descriptive fallback message if
239+
neither property contains error details.
240+
- Prerequisites Integration Tests
241+
- Fixed missing RS (Reporting Services) integration test tags on Context blocks
242+
that create local Windows users, service accounts, and groups. Added tags
243+
`Integration_SQL2017_RS`, `Integration_SQL2019_RS`, and `Integration_SQL2022_RS`
244+
to ensure these prerequisites run before Reporting Services integration tests.
245+
- `New-SqlDscFileGroup`
246+
- Fixed comment-based help example formatting by moving inline comment
247+
to the description text.
248+
- QA Tests
249+
- Added new test to detect comments within multi-line example code blocks
250+
in comment-based help. Comments in the code portion of `.EXAMPLE` blocks
251+
cause PlatyPS documentation generation to fail with "Expect Heading" errors.
252+
- Added new test to detect blank lines within multi-line example code blocks
253+
in comment-based help. Blank lines within the code portion of `.EXAMPLE`
254+
blocks cause similar issues with documentation generation.
255+
- `Deny-SqlDscServerPermission`
256+
- Fixed comment-based help example formatting by removing blank lines
257+
within code blocks that would cause documentation generation issues.
258+
- `Get-SqlDscServerPermission`
259+
- Fixed comment-based help example formatting by removing blank lines
260+
within code blocks that would cause documentation generation issues.
261+
- `Grant-SqlDscServerPermission`
262+
- Fixed comment-based help example formatting by removing blank lines
263+
within code blocks that would cause documentation generation issues.
264+
- `New-SqlDscDatabase`
265+
- Fixed comment-based help example formatting by removing blank lines
266+
within code blocks.
267+
- `New-SqlDscDatabaseSnapshot`
268+
- Fixed comment-based help example formatting by removing blank lines
269+
within code blocks.
270+
- `Revoke-SqlDscServerPermission`
271+
- Fixed comment-based help example formatting by removing blank lines
272+
within code blocks.
273+
- `Set-SqlDscDatabasePermission`
274+
- Fixed comment-based help example formatting by removing blank lines
275+
within code blocks.
276+
- `Set-SqlDscServerPermission`
277+
- Fixed comment-based help example formatting by removing blank lines
278+
within code blocks.
279+
- `Test-SqlDscServerPermission`
280+
- Fixed comment-based help example formatting by removing blank lines
281+
within code blocks.
204282
- Unit Tests
205283
- Fixed PowerShell class type identity issues that caused "Cannot convert
206284
'Type' to 'Type'" errors when running multiple test files in the same
@@ -260,9 +338,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
260338
- `Set-SqlDscDatabaseProperty`
261339
- Updated comment-based help to reference correct enum values.
262340
- Added SQL Server version requirements to version-specific parameter help.
263-
264-
### Fixed
265-
266341
- `DatabasePermission`
267342
- Fixed `Equals()` method to compare both `State` and `Permission` properties.
268343
Previously, the method incorrectly referenced a non-existent `Grant` property,

azure-pipelines.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ stages:
530530
# Run the integration tests in a specific group order.
531531
# Group 0
532532
'tests/Integration/Commands/Prerequisites.Integration.Tests.ps1'
533+
'tests/Integration/Commands/Prerequisites.RSDB.Integration.Tests.ps1'
533534
'tests/Integration/Commands/Save-SqlDscSqlServerMediaFile.Integration.Tests.ps1'
534535
'tests/Integration/Commands/Import-SqlDscPreferredModule.Integration.Tests.ps1'
535536
# Group 1
@@ -539,6 +540,8 @@ stages:
539540
'tests/Integration/Commands/Get-SqlDscRSPackage.Integration.Tests.ps1'
540541
'tests/Integration/Commands/Get-SqlDscRSSetupConfiguration.Integration.Tests.ps1'
541542
'tests/Integration/Commands/Test-SqlDscRSInstalled.Integration.Tests.ps1'
543+
'tests/Integration/Commands/Request-SqlDscRSDatabaseScript.Integration.Tests.ps1'
544+
'tests/Integration/Commands/Request-SqlDscRSDatabaseRightsScript.Integration.Tests.ps1'
542545
# Group 3
543546
'tests/Integration/Commands/Get-SqlDscRSConfiguration.Integration.Tests.ps1'
544547
'tests/Integration/Commands/Enable-SqlDscRsSecureConnection.Integration.Tests.ps1'
@@ -548,6 +551,7 @@ stages:
548551
'tests/Integration/Commands/Add-SqlDscRSUrlReservation.Integration.Tests.ps1'
549552
'tests/Integration/Commands/Remove-SqlDscRSUrlReservation.Integration.Tests.ps1'
550553
'tests/Integration/Commands/Set-SqlDscRSUrlReservation.Integration.Tests.ps1'
554+
'tests/Integration/Commands/Set-SqlDscRSDatabaseConnection.Integration.Tests.ps1'
551555
# Group 8
552556
'tests/Integration/Commands/Repair-SqlDscReportingService.Integration.Tests.ps1'
553557
# Group 9
@@ -605,6 +609,7 @@ stages:
605609
# Run the integration tests in a specific group order.
606610
# Group 0
607611
'tests/Integration/Commands/Prerequisites.Integration.Tests.ps1'
612+
'tests/Integration/Commands/Prerequisites.RSDB.Integration.Tests.ps1'
608613
'tests/Integration/Commands/Save-SqlDscSqlServerMediaFile.Integration.Tests.ps1'
609614
'tests/Integration/Commands/Import-SqlDscPreferredModule.Integration.Tests.ps1'
610615
# Group 1
@@ -614,6 +619,8 @@ stages:
614619
'tests/Integration/Commands/Get-SqlDscRSPackage.Integration.Tests.ps1'
615620
'tests/Integration/Commands/Get-SqlDscRSSetupConfiguration.Integration.Tests.ps1'
616621
'tests/Integration/Commands/Test-SqlDscRSInstalled.Integration.Tests.ps1'
622+
'tests/Integration/Commands/Request-SqlDscRSDatabaseScript.Integration.Tests.ps1'
623+
'tests/Integration/Commands/Request-SqlDscRSDatabaseRightsScript.Integration.Tests.ps1'
617624
# Group 3
618625
'tests/Integration/Commands/Get-SqlDscRSConfiguration.Integration.Tests.ps1'
619626
'tests/Integration/Commands/Enable-SqlDscRsSecureConnection.Integration.Tests.ps1'
@@ -623,6 +630,7 @@ stages:
623630
'tests/Integration/Commands/Add-SqlDscRSUrlReservation.Integration.Tests.ps1'
624631
'tests/Integration/Commands/Remove-SqlDscRSUrlReservation.Integration.Tests.ps1'
625632
'tests/Integration/Commands/Set-SqlDscRSUrlReservation.Integration.Tests.ps1'
633+
'tests/Integration/Commands/Set-SqlDscRSDatabaseConnection.Integration.Tests.ps1'
626634
# Group 8
627635
'tests/Integration/Commands/Repair-SqlDscPowerBIReportServer.Integration.Tests.ps1'
628636
# Group 9

source/DSCResources/DSC_SqlRS/DSC_SqlRS.psm1

Lines changed: 16 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -396,37 +396,14 @@ function Set-TargetResource
396396

397397
Write-Verbose -Message "Generate database creation script on $DatabaseServerName\$DatabaseInstanceName for database '$reportingServicesDatabaseName'."
398398

399-
$invokeRsCimMethodParameters = @{
400-
CimInstance = $rsConfiguration
401-
MethodName = 'GenerateDatabaseCreationScript'
402-
Arguments = @{
403-
DatabaseName = $reportingServicesDatabaseName
404-
IsSharePointMode = $false
405-
Lcid = $language
406-
}
407-
}
399+
$reportingServicesDatabaseScript = $rsConfiguration | Request-SqlDscRSDatabaseScript -DatabaseName $reportingServicesDatabaseName -Lcid $language -ErrorAction 'Stop'
408400

409-
$reportingServicesDatabaseScript = Invoke-RsCimMethod @invokeRsCimMethodParameters -ErrorAction 'Stop'
410-
411-
# Determine RS service account
412-
$reportingServicesServiceAccountUserName = (Get-CimInstance -ClassName Win32_Service | Where-Object -FilterScript {
413-
$_.Name -eq $reportingServicesServiceName
414-
}).StartName
401+
# The WindowsServiceIdentityActual property contains the actual account name actively used by the service.
402+
$reportingServicesServiceAccountUserName = $rsConfiguration.WindowsServiceIdentityActual
415403

416404
Write-Verbose -Message "Generate database rights script on $DatabaseServerName\$DatabaseInstanceName for database '$reportingServicesDatabaseName' and user '$reportingServicesServiceAccountUserName'."
417405

418-
$invokeRsCimMethodParameters = @{
419-
CimInstance = $rsConfiguration
420-
MethodName = 'GenerateDatabaseRightsScript'
421-
Arguments = @{
422-
DatabaseName = $reportingServicesDatabaseName
423-
UserName = $reportingServicesServiceAccountUserName
424-
IsRemote = $false
425-
IsWindowsUser = $true
426-
}
427-
}
428-
429-
$reportingServicesDatabaseRightsScript = Invoke-RsCimMethod @invokeRsCimMethodParameters -ErrorAction 'Stop'
406+
$reportingServicesDatabaseRightsScript = $rsConfiguration | Request-SqlDscRSDatabaseRightsScript -DatabaseName $reportingServicesDatabaseName -UserName $reportingServicesServiceAccountUserName -ErrorAction 'Stop'
430407

431408
Import-SqlDscPreferredModule
432409

@@ -444,49 +421,25 @@ function Set-TargetResource
444421
$invokeSqlDscQueryParameters.Encrypt = $true
445422
}
446423

447-
Invoke-SqlDscQuery @invokeSqlDscQueryParameters -Query $reportingServicesDatabaseScript.Script
448-
Invoke-SqlDscQuery @invokeSqlDscQueryParameters -Query $reportingServicesDatabaseRightsScript.Script
424+
Invoke-SqlDscQuery @invokeSqlDscQueryParameters -Query $reportingServicesDatabaseScript
425+
Invoke-SqlDscQuery @invokeSqlDscQueryParameters -Query $reportingServicesDatabaseRightsScript
449426

450427
Write-Verbose -Message "Set database connection on $DatabaseServerName\$DatabaseInstanceName to database '$reportingServicesDatabaseName'."
451428

452-
if ( $DatabaseInstanceName -eq 'MSSQLSERVER' )
453-
{
454-
$reportingServicesConnection = $DatabaseServerName
429+
$setSqlDscRSDatabaseConnectionParameters = @{
430+
ServerName = $DatabaseServerName
431+
DatabaseName = $reportingServicesDatabaseName
432+
Type = 'ServiceAccount'
433+
Force = $true
434+
ErrorAction = 'Stop'
455435
}
456-
else
436+
437+
if ($DatabaseInstanceName -ne 'MSSQLSERVER')
457438
{
458-
$reportingServicesConnection = "$DatabaseServerName\$DatabaseInstanceName"
459-
}
460-
461-
$invokeRsCimMethodParameters = @{
462-
CimInstance = $rsConfiguration
463-
MethodName = 'SetDatabaseConnection'
464-
Arguments = @{
465-
Server = $reportingServicesConnection
466-
DatabaseName = $reportingServicesDatabaseName
467-
Username = ''
468-
Password = ''
469-
470-
<#
471-
Can be set to either:
472-
0 = Windows
473-
1 = Sql Server
474-
2 = Windows Service (Integrated Security)
475-
476-
When set to 2 the Reporting Server Web service will use
477-
either the ASP.NET account or an application pool’s account
478-
and the Windows service account to access the report server
479-
database.
480-
481-
See more in the article
482-
https://docs.microsoft.com/en-us/sql/reporting-services/wmi-provider-library-reference/configurationsetting-method-setdatabaseconnection#remarks
483-
484-
#>
485-
CredentialsType = 2
486-
}
439+
$setSqlDscRSDatabaseConnectionParameters.InstanceName = $DatabaseInstanceName
487440
}
488441

489-
Invoke-RsCimMethod @invokeRsCimMethodParameters -ErrorAction 'Stop'
442+
$rsConfiguration | Set-SqlDscRSDatabaseConnection @setSqlDscRSDatabaseConnectionParameters
490443

491444
<#
492445
When initializing SSRS 2019, the call to InitializeReportServer
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<#
2+
.SYNOPSIS
3+
Gets a human-readable message for a given HRESULT code.
4+
5+
.DESCRIPTION
6+
Translates common Windows HRESULT error codes into human-readable
7+
messages. This is particularly useful when CIM methods return an
8+
HRESULT code without detailed error information in ExtendedErrors
9+
or Error properties.
10+
11+
.PARAMETER HResult
12+
The HRESULT code to translate. This is typically a 32-bit signed
13+
integer returned from a Windows API or CIM method call.
14+
15+
.OUTPUTS
16+
`System.String`
17+
18+
Returns a descriptive message for known HRESULT codes, or a generic
19+
message with the hexadecimal code for unknown values.
20+
21+
.EXAMPLE
22+
Get-HResultMessage -HResult -2147023181
23+
24+
Returns: The account has not been granted the requested logon type at
25+
this computer. Verify that the service account has the required
26+
permissions to interact with the Reporting Services WMI provider.
27+
28+
.EXAMPLE
29+
Get-HResultMessage -HResult -2147024891
30+
31+
Returns: Access is denied. Verify that the current user has administrator
32+
rights on the Reporting Services instance.
33+
34+
.NOTES
35+
This function is used internally by other commands to provide actionable
36+
error messages when Reporting Services CIM methods fail without detailed
37+
error information. These codes have not been verified against any official
38+
Microsoft documentation, and based on the common HRESULT values in
39+
https://learn.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values.
40+
#>
41+
function Get-HResultMessage
42+
{
43+
[CmdletBinding()]
44+
[OutputType([System.String])]
45+
param
46+
(
47+
[Parameter(Mandatory = $true)]
48+
[System.Int32]
49+
$HResult
50+
)
51+
52+
<#
53+
HRESULT values are 32-bit signed integers. Negative values indicate
54+
errors. The HRESULT is composed of:
55+
- Bit 31: Severity (0 = success, 1 = error)
56+
- Bits 16-30: Facility code
57+
- Bits 0-15: Error code
58+
59+
Common HRESULT values are documented at:
60+
https://learn.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values
61+
#>
62+
$hResultMessages = @{
63+
# cSpell: ignore ACCESSDENIED LOGON
64+
# E_ACCESSDENIED (0x80070005) - General access denied error
65+
-2147024891 = $script:localizedData.HResult_AccessDenied
66+
67+
# ERROR_LOGON_TYPE_NOT_GRANTED (0x80070533) - Account lacks logon rights
68+
-2147023181 = $script:localizedData.HResult_LogonTypeNotGranted
69+
70+
# E_FAIL (0x80004005) - Unspecified failure
71+
-2147467259 = $script:localizedData.HResult_UnspecifiedFailure
72+
73+
# E_INVALIDARG (0x80070057) - One or more arguments are invalid
74+
-2147024809 = $script:localizedData.HResult_InvalidArgument
75+
76+
# E_OUTOFMEMORY (0x8007000E) - Out of memory
77+
-2147024882 = $script:localizedData.HResult_OutOfMemory
78+
79+
# RPC_E_DISCONNECTED (0x80010108) - The object invoked has disconnected
80+
-2147417848 = $script:localizedData.HResult_RpcDisconnected
81+
82+
# RPC_S_SERVER_UNAVAILABLE (0x800706BA) - The RPC server is unavailable
83+
-2147023174 = $script:localizedData.HResult_RpcServerUnavailable
84+
85+
# ERROR_SERVICE_NOT_ACTIVE (0x80070426) - The service has not been started
86+
-2147023834 = $script:localizedData.HResult_ServiceNotActive
87+
}
88+
89+
if ($hResultMessages.ContainsKey($HResult))
90+
{
91+
return $hResultMessages[$HResult]
92+
}
93+
94+
<#
95+
Return a generic message with the hexadecimal representation for unknown codes.
96+
Convert to hex using bitwise operation to handle negative values that would
97+
overflow when casting directly to UInt32 (e.g., Int32.MinValue = -2147483648).
98+
#>
99+
$hexValue = '0x{0:X8}' -f ($HResult -band 0xFFFFFFFF)
100+
101+
return ($script:localizedData.HResult_Unknown -f $hexValue)
102+
}

0 commit comments

Comments
 (0)