Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- SqlServerDsc
- Added class `ReportServerUri` to represent URLs returned by the
`GetReportServerUrls` CIM method on `MSReportServer_Instance`.
- Added public command `Get-SqlDscRSUrl` to get the Report Server URLs for
SQL Server Reporting Services or Power BI Report Server. This command
invokes the `GetReportServerUrls` CIM method on `MSReportServer_Instance`
and returns an array of `ReportServerUri` objects containing the instance
name, application name, and URL for each configured URL.
- Added public command `Get-SqlDscRSConfigFile` to get the RsReportServer.config
configuration file for SQL Server Reporting Services (SSRS) or Power BI
Report Server (PBIRS) as an XML document object. Supports three parameter
sets: `ByInstanceName` (looks up path via registry), `ByConfiguration`
(accepts pipeline input from `Get-SqlDscRSSetupConfiguration`), and `ByPath`
(reads from a direct file path). The returned XML object supports standard
XML navigation and XPath queries for accessing configuration settings.
- Added public command `Get-SqlDscRSLogPath` to get the log file folder path
for SQL Server Reporting Services or Power BI Report Server. Returns the
ErrorDumpDirectory from the instance's setup configuration, which can be
Expand Down Expand Up @@ -194,6 +209,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
for SQL Server Reporting Services or Power BI Report Server. Supports waiting
for dependent services, configurable wait time, and accepts pipeline input
from `Get-SqlDscRSConfiguration`.
- Added public command `New-SqlDscRSEncryptionKey` to delete and regenerate the
Reporting Services encryption key. Wraps the `DeleteEncryptionKey` CIM method.
Warning: This operation cannot be undone and renders all encrypted content
unreadable.
- Added public command `Remove-SqlDscRSEncryptionKey` to remove all encrypted
content from the report server database. Wraps the `DeleteEncryptionKey` CIM
method with `DeleteEncryptedContent` mode.
- Added public command `Remove-SqlDscRSEncryptedInformation` to remove all encrypted
information from the report server database, including stored credentials and
connection strings. Wraps the `DeleteEncryptedInformation` CIM method.
- Added public commands `Get-SqlDscRSServiceAccount` and
`Set-SqlDscRSServiceAccount` to get and set the Windows service account for
SQL Server Reporting Services or Power BI Report Server. `Set-SqlDscRSServiceAccount`
Expand All @@ -213,6 +238,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added wiki article `Troubleshooting-Report-Server` documenting how to
retrieve and analyze log files and Windows event logs for Power BI Report
Server and SQL Server Reporting Services.
- Updated wiki article `Change-Report-Server-Service-Account` with SQL Server
2017 specific workflow. SQL Server 2017 RS requires using
`Remove-SqlDscRSEncryptedInformation` and `Set-SqlDscRSDatabaseConnection`
instead of `Remove-SqlDscRSEncryptionKey` and `New-SqlDscRSEncryptionKey`
which fail with "rsCannotValidateEncryptedData" and "Keyset does not exist"
errors on SQL Server 2017.

### Changed

Expand Down
16 changes: 16 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,20 @@ stages:
$pesterConfig.Run.Path = '.\tests\QA'
$pesterConfig.Run.Throw = $true
$pesterConfig.Output.Verbosity = 'Detailed'
$pesterConfig.TestResult.Enabled = $true
$pesterConfig.TestResult.OutputFormat = 'NUnitXml'
$pesterConfig.TestResult.OutputPath = ".\$(buildFolderName)\$(testResultFolderName)\NUnit_QA.xml"

Invoke-Pester -Configuration $pesterConfig
name: qualityTest
displayName: 'Run SqlServerDsc QA Test'
- task: PublishTestResults@2
displayName: 'Publish Test Results'
condition: succeededOrFailed()
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit_QA.xml'
testRunTitle: 'QA'

- job: Test_HQRM
displayName: 'HQRM Test'
Expand Down Expand Up @@ -570,15 +580,18 @@ stages:
'tests/Integration/Commands/Restart-SqlDscRSService.Integration.Tests.ps1'
'tests/Integration/Commands/Test-SqlDscRSInitialized.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSLogPath.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSConfigFile.Integration.Tests.ps1'
# Group 4
'tests/Integration/Commands/Initialize-SqlDscRS.Integration.Tests.ps1'
# Group 5 - Post-initialization validation
'tests/Integration/Commands/Post.Initialization.RS.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSUrl.Integration.Tests.ps1'
# Group 6 - Service account change
'tests/Integration/Commands/Set-SqlDscRSServiceAccount.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSServiceAccount.Integration.Tests.ps1'
'tests/Integration/Commands/Post.DatabaseRights.RS.Integration.Tests.ps1'
'tests/Integration/Commands/Post.EncryptedInformation.RS.Integration.Tests.ps1'
'tests/Integration/Commands/Post.DatabaseConnection.RS.Integration.Tests.ps1'
'tests/Integration/Commands/Remove-SqlDscRSEncryptionKey.Integration.Tests.ps1'
'tests/Integration/Commands/New-SqlDscRSEncryptionKey.Integration.Tests.ps1'
'tests/Integration/Commands/Post.UrlReservationRecreate.RS.Integration.Tests.ps1'
Expand Down Expand Up @@ -668,10 +681,12 @@ stages:
'tests/Integration/Commands/Restart-SqlDscRSService.Integration.Tests.ps1'
'tests/Integration/Commands/Test-SqlDscRSInitialized.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSLogPath.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSConfigFile.Integration.Tests.ps1'
# Group 4
'tests/Integration/Commands/Initialize-SqlDscRS.Integration.Tests.ps1'
# Group 5 - Post-initialization validation
'tests/Integration/Commands/Post.Initialization.RS.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSUrl.Integration.Tests.ps1'
# Group 6 - Service account change
'tests/Integration/Commands/Set-SqlDscRSServiceAccount.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSServiceAccount.Integration.Tests.ps1'
Expand All @@ -684,6 +699,7 @@ stages:
# Group 8
'tests/Integration/Commands/Repair-SqlDscPowerBIReportServer.Integration.Tests.ps1'
'tests/Integration/Commands/Remove-SqlDscRSUrlReservation.Integration.Tests.ps1'
'tests/Integration/Commands/Remove-SqlDscRSEncryptedInformation.Integration.Tests.ps1'
# Group 9
'tests/Integration/Commands/Uninstall-SqlDscPowerBIReportServer.Integration.Tests.ps1'
)
Expand Down
50 changes: 50 additions & 0 deletions source/Classes/002.ReportServerUri.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<#
.SYNOPSIS
Represents a Reporting Services URL returned by the GetReportServerUrls
CIM method.

.DESCRIPTION
This class represents a URL for a Reporting Services application, including
the instance name, application name (such as ReportServerWebService or
ReportServerWebApp), and the URL itself.

.PARAMETER InstanceName
The name of the Reporting Services instance.

.PARAMETER ApplicationName
The name of the Reporting Services application. Common values include:
- ReportServerWebService
- ReportServerWebApp
- ReportManager (for older versions)

.PARAMETER Uri
The URL for accessing the Reporting Services application.

.EXAMPLE
[ReportServerUri]::new()

Creates a new empty ReportServerUri instance.

.EXAMPLE
$uri = [ReportServerUri]::new()
$uri.InstanceName = 'SSRS'
$uri.ApplicationName = 'ReportServerWebService'
$uri.Uri = 'http://localhost:80/ReportServer'

Creates a new ReportServerUri instance with property values.
#>
class ReportServerUri
{
[System.String]
$InstanceName

[System.String]
$ApplicationName

[System.String]
$Uri

ReportServerUri()
{
}
}
209 changes: 209 additions & 0 deletions source/Public/Get-SqlDscRSConfigFile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
<#
.SYNOPSIS
Gets the RsReportServer.config configuration file as an XML object.

.DESCRIPTION
Gets the RsReportServer.config configuration file for SQL Server
Reporting Services (SSRS) or Power BI Report Server (PBIRS) as an
XML document object. This allows programmatic access to configuration
settings using standard XML navigation or XPath queries.

The configuration file path is automatically determined from the
instance's setup configuration in the registry when using the
`InstanceName` or `SetupConfiguration` parameter. Alternatively, a direct
file path can be specified using the `Path` parameter.

.PARAMETER InstanceName
Specifies the name of the Reporting Services instance. This is typically
'SSRS' for SQL Server Reporting Services or 'PBIRS' for Power BI Report
Server. This parameter is mandatory when not passing a configuration object
or a direct path.

.PARAMETER SetupConfiguration
Specifies the configuration object from `Get-SqlDscRSSetupConfiguration`.
This can be piped from `Get-SqlDscRSSetupConfiguration`. The object must
have a `ConfigFilePath` property containing the path to the configuration
file. This parameter accepts pipeline input.

.PARAMETER Path
Specifies the direct path to the RsReportServer.config file. Use this
parameter to read configuration files from non-standard locations or
backup copies.

.EXAMPLE
Get-SqlDscRSConfigFile -InstanceName 'SSRS'

Returns the rsreportserver.config file content as an XML object for
the SSRS instance.

.EXAMPLE
Get-SqlDscRSConfigFile -InstanceName 'PBIRS'

Returns the rsreportserver.config file content as an XML object for
the Power BI Report Server instance.

.EXAMPLE
Get-SqlDscRSSetupConfiguration -InstanceName 'SSRS' | Get-SqlDscRSConfigFile

Gets the setup configuration for SSRS and pipes it to Get-SqlDscRSConfigFile
to retrieve the configuration file as XML.

.EXAMPLE
Get-SqlDscRSConfigFile -Path 'C:\Backup\rsreportserver.config'

Reads the configuration file from the specified path.

.EXAMPLE
$config = Get-SqlDscRSConfigFile -InstanceName 'SSRS'
$config.Configuration.Service.IsSchedulingService

Gets the config file and accesses the scheduling service setting directly
using dot notation.

.EXAMPLE
$config = Get-SqlDscRSConfigFile -InstanceName 'SSRS'
$config.SelectSingleNode('//Authentication/AuthenticationTypes')

Uses XPath to query the authentication types configuration section.

.EXAMPLE
$config = Get-SqlDscRSConfigFile -InstanceName 'SSRS'
$config.SelectNodes('//Extension[@Name]') | ForEach-Object { $_.Name }

Uses XPath to list all extension names defined in the configuration.

.EXAMPLE
$config = Get-SqlDscRSConfigFile -InstanceName 'SSRS'
$config.Configuration.URLReservations.Application |
Where-Object { $_.Name -eq 'ReportServerWebService' } |
Select-Object -ExpandProperty URLs

Gets the URL reservations for the Report Server Web Service application.

.EXAMPLE
$config = Get-SqlDscRSConfigFile -InstanceName 'SSRS'
$smtpServer = $config.SelectSingleNode('//RSEmailDPConfiguration/SMTPServer')
if ($smtpServer) { $smtpServer.InnerText }

Uses XPath to retrieve the SMTP server configuration for email delivery.

.INPUTS
`System.Object`

Accepts the setup configuration object from `Get-SqlDscRSSetupConfiguration` via
pipeline.

.OUTPUTS
`System.Xml.XmlDocument`

Returns the configuration file content as an XML document object.

.NOTES
For more information about the RsReportServer.config configuration file,
see the Microsoft documentation:
https://learn.microsoft.com/en-us/sql/reporting-services/report-server/rsreportserver-config-configuration-file
#>
function Get-SqlDscRSConfigFile
{
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the examples use pipeline input and XPath the rule cannot validate.')]
[CmdletBinding(DefaultParameterSetName = 'ByInstanceName')]
[OutputType([System.Xml.XmlDocument])]
param
(
[Parameter(Mandatory = $true, ParameterSetName = 'ByInstanceName')]
[System.String]
$InstanceName,

[Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'ByConfiguration')]
[System.Object]
$SetupConfiguration,

[Parameter(Mandatory = $true, ParameterSetName = 'ByPath')]
[System.String]
$Path
)

process
{
$configFilePath = $null

switch ($PSCmdlet.ParameterSetName)
{
'ByInstanceName'
{
Write-Verbose -Message ($script:localizedData.Get_SqlDscRSConfigFile_GettingConfigFile -f $InstanceName)

$setupConfiguration = Get-SqlDscRSSetupConfiguration -InstanceName $InstanceName

if (-not $setupConfiguration)
{
$errorMessage = $script:localizedData.Get_SqlDscRSConfigFile_InstanceNotFound -f $InstanceName

$errorRecord = New-ErrorRecord -Exception (New-InvalidOperationException -Message $errorMessage -PassThru) -ErrorId 'GSRSCF0001' -ErrorCategory 'ObjectNotFound' -TargetObject $InstanceName

$PSCmdlet.ThrowTerminatingError($errorRecord)
}

if ([System.String]::IsNullOrEmpty($setupConfiguration.ConfigFilePath))
{
$errorMessage = $script:localizedData.Get_SqlDscRSConfigFile_ConfigFilePathNotFound -f $InstanceName

$errorRecord = New-ErrorRecord -Exception (New-InvalidOperationException -Message $errorMessage -PassThru) -ErrorId 'GSRSCF0002' -ErrorCategory 'ObjectNotFound' -TargetObject $InstanceName

$PSCmdlet.ThrowTerminatingError($errorRecord)
}

$configFilePath = $setupConfiguration.ConfigFilePath
}

'ByConfiguration'
{
Write-Verbose -Message ($script:localizedData.Get_SqlDscRSConfigFile_GettingConfigFile -f $SetupConfiguration.InstanceName)

if ([System.String]::IsNullOrEmpty($SetupConfiguration.ConfigFilePath))
{
$errorMessage = $script:localizedData.Get_SqlDscRSConfigFile_ConfigFilePathNotFound -f $SetupConfiguration.InstanceName

$errorRecord = New-ErrorRecord -Exception (New-InvalidOperationException -Message $errorMessage -PassThru) -ErrorId 'GSRSCF0002' -ErrorCategory 'ObjectNotFound' -TargetObject $SetupConfiguration.InstanceName

$PSCmdlet.ThrowTerminatingError($errorRecord)
}

$configFilePath = $SetupConfiguration.ConfigFilePath
}

'ByPath'
{
Write-Verbose -Message ($script:localizedData.Get_SqlDscRSConfigFile_ReadingFromPath -f $Path)

if (-not (Test-Path -Path $Path -PathType 'Leaf'))
{
$errorMessage = $script:localizedData.Get_SqlDscRSConfigFile_FileNotFound -f $Path

$errorRecord = New-ErrorRecord -Exception (New-InvalidOperationException -Message $errorMessage -PassThru) -ErrorId 'GSRSCF0004' -ErrorCategory 'ObjectNotFound' -TargetObject $Path

$PSCmdlet.ThrowTerminatingError($errorRecord)
}

$configFilePath = $Path
}
}

Write-Verbose -Message ($script:localizedData.Get_SqlDscRSConfigFile_FoundConfigFile -f $configFilePath)

try
{
[xml] $configXml = Get-Content -Path $configFilePath -Raw -Force -ErrorAction 'Stop'
}
catch
{
$errorMessage = $script:localizedData.Get_SqlDscRSConfigFile_FailedToReadConfigFile -f $configFilePath, $_.Exception.Message

$errorRecord = New-ErrorRecord -Exception (New-InvalidOperationException -Message $errorMessage -ErrorRecord $_ -PassThru) -ErrorId 'GSRSCF0003' -ErrorCategory 'ReadError' -TargetObject $configFilePath

$PSCmdlet.ThrowTerminatingError($errorRecord)
}

return $configXml
}
}
Loading
Loading