Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
86fb81a
Add GitHub Copilot instructions for developing PowerShell commands an…
johlju Mar 8, 2025
eb6ca4b
Add TODO note to refactor Get-RegistryPropertyValue to use Get-ItemPr…
johlju Mar 8, 2025
e479f46
Add Get-SqlDscInstalledInstance command to retrieve installed SQL ins…
johlju Mar 8, 2025
e9ec150
more instructions
johlju Mar 8, 2025
084dc6d
Fix instructions
johlju Mar 8, 2025
abab349
Fix instructions
johlju Mar 8, 2025
bde7459
Fix instructions
johlju Mar 8, 2025
b2db22b
FIx instructions
johlju Mar 8, 2025
dbe7756
Add Get-SqlDscRSSetupConfiguration function to retrieve SQL Server Re…
johlju Mar 8, 2025
9759285
Fix changelog
johlju Mar 8, 2025
8dd0952
Update changelog
johlju Mar 8, 2025
a4f525c
Fix integration tests
johlju Mar 8, 2025
744d5f7
Fix integration tests
johlju Mar 8, 2025
14a4faa
Fix comment-based help
johlju Mar 8, 2025
21ca51b
Fix unit test
johlju Mar 8, 2025
5104307
Fix integ test
johlju Mar 9, 2025
bca7a9c
Fix integ test
johlju Mar 9, 2025
cdc1d81
Fix pipeline
johlju Mar 9, 2025
8c6ad24
Fix pipeline
johlju Mar 9, 2025
99b8acc
Fix integ
johlju Mar 9, 2025
e78ba14
Fix integ
johlju Mar 9, 2025
63b03f3
Use Get-RegistryPropertyValue
johlju Mar 9, 2025
234adc6
Fix Test-PendingRestart
johlju Mar 9, 2025
abb1503
Revert function, but renamed
johlju Mar 10, 2025
d883b7b
Fix unit test
johlju Mar 10, 2025
f254bf6
Merge branch 'main' into f/issue#2072
johlju Mar 11, 2025
22ad4b5
Revert command name
johlju Mar 11, 2025
efd00f4
Revert Test-PendingRestart
johlju Mar 11, 2025
3fb4b2c
Merge branch 'main' into f/issue#2072
johlju Mar 11, 2025
c1f71f4
fix integ
johlju Mar 11, 2025
ab66036
fix integ
johlju Mar 11, 2025
0ea1fd2
Fix integ test
johlju Mar 12, 2025
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
175 changes: 175 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
# Specific instructions for the PowerShell module project SqlServerDsc

Assume that the word "command" references to a public command, and the word
"function" references to a private function.

PowerShell commands that should be public should always have its separate
script file and the the command name as the file name with the .ps1 extension,
these files shall always be placed in the folder source/Public.

Public commands may use private functions to move out logic that can be
reused by other public commands, so move out any logic that can be deemed
reusable. Private functions should always have its separate script file and
the the function name as the file name with the .ps1 extension, these files
shall always be placed in the folder source/Private.

Comment-based help should be added to each public command and private functions.
The comment-based help should always be before the function-statement. Each
comment-based help keyword should be indented with 4 spaces and each keywords
text should be indented 8 spaces. The text for keyword .DESCRIPTION should
be descriptive and must have a length greater than 40 characters. A comment-based
help must have at least one example, but preferably more examples to showcase
all possible parameter sets and different parameter combinations.

All message strings for Write-Debug, Write-Verbose, Write-Error, Write-Warning
and other error messages in public commands and private functions should be
localized using localized string keys. You should always add all localized
strings for public commands and private functions in the source/en-US/SqlServerDsc.strings.psd1
file, re-use the same pattern for new string keys. Localized string key names
should always be prefixed with the function name but use underscore as word
separator. Always assume that all localized string keys have already been
assigned to the variable $script:localizedData.

All tests should use the Pester framework and use Pester v5.0 syntax.

Never test, mock or use `Should -Invoke` for `Write-Verbose` and `Write-Debug`
regardless of other instructions.

Test code should never be added outside of the `Describe` block.

Unit tests should be added for all public commands and private functions.
The unit tests for public command should be placed in the folder tests/Unit/Public
and the unit tests for private functions should be placed in the folder
tests/Unit/Private. The unit tests should be named after the public command
or private function they are testing, but should have the suffix .Tests.ps1.
The unit tests should be written to cover all possible scenarios and code paths,
ensuring that both edge cases and common use cases are tested.

There should only be one Pester `Describe` block per test file, and the name of
the `Describe` block should be the same as the name of the public command or
private function being tested. Each scenario or code path being tested should
have its own Pester `Context` block that starts with the phrase 'When'. Use
nested `Context` blocks to split up test cases and improve tests readability.
Pester `It` block descriptions should start with the phrase 'Should'. `It`
blocks must always call the command or function being tested and result and
outcomes should be kept in the same `It` block. `BeforeAll` and `BeforeEach`
blocks should never call the command or function being tested.

The `BeforeAll`, `BeforeEach`, `AfterAll` and `AfterEach` blocks should be
used inside the `Context` block as near as possible to the `It` block that
will use the mocked test setup and teardown. The `BeforeAll` block should
be used to set up any necessary test data or mocking, and the `AfterAll`
block can be used to clean up any test data. The `BeforeEach` and `AfterEach`
blocks should be used sparingly. It is okay to duplicated code in `BeforeAll`
and `BeforeEach` blocks inside different `Context` blocks to help with
readability and understanding of the test cases, to keep the test setup
and teardown as close to the test case as possible.

Use localized strings in the tests only when necessary. You can assign the
localized string to a mock variable by and get the localized string key
from the $script:localizedData variable inside a `InModuleScope` block.
An example to get a localized string key from the $script:localizedData variable:

```powershell
$mockLocalizedStringText = InModuleScope -ScriptBlock { $script:localizedData.LocalizedStringKey }
```

Files that need to be mocked should be created in Pesters test drive. The
variable `$TestDrive` holds the path to the test drive. The `$TestDrive` is a
temporary drive that is created for each test run and is automatically
cleaned up after the test run is complete.

All unit tests should should use this code block prior to the `Describe` block
which will set up the test environment and load the correct module being tested:

```powershell
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
param ()
BeforeDiscovery {
try
{
if (-not (Get-Module -Name 'DscResource.Test'))
{
# Assumes dependencies has been resolved, so if this module is not available, run 'noop' task.
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
{
# Redirect all streams to $null, except the error stream (stream 2)
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null
}
# If the dependencies has not been resolved, this will throw an error.
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
}
}
catch [System.IO.FileNotFoundException]
{
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.'
}
}
BeforeAll {
$script:dscModuleName = 'SqlServerDsc'
$env:SqlServerDscCI = $true
Import-Module -Name $script:dscModuleName
$PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName
$PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName
$PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName
}
AfterAll {
$PSDefaultParameterValues.Remove('InModuleScope:ModuleName')
$PSDefaultParameterValues.Remove('Mock:ModuleName')
$PSDefaultParameterValues.Remove('Should:ModuleName')
# Unload the module being tested so that it doesn't impact any other tests.
Get-Module -Name $script:dscModuleName -All | Remove-Module -Force
Remove-Item -Path 'env:SqlServerDscCI'
}
```

Integration tests should be added for all public commands. Integration must
never mock any command but run the command in a real environment. The integration
tests should be placed in the folder tests/Integration/Commands and the
integration tests should be named after the public command they are testing,
but should have the suffix .Integration.Tests.ps1. The integration tests should be
written to cover all possible scenarios and code paths, ensuring that both
edge cases and common use cases are tested. The integration tests should
also be written to test the command in a real environment, using real
resources and dependencies.

The module being tested should not be imported in the integration tests.
All integration tests should should use this code block prior to the `Describe`
block which will set up the test environment and will make sure the correct
module is available for testing:

```powershell
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'Suppressing this rule because Script Analyzer does not understand Pester syntax.')]
param ()
BeforeDiscovery {
try
{
if (-not (Get-Module -Name 'DscResource.Test'))
{
# Assumes dependencies has been resolved, so if this module is not available, run 'noop' task.
if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable))
{
# Redirect all streams to $null, except the error stream (stream 2)
& "$PSScriptRoot/../../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null
}
# If the dependencies has not been resolved, this will throw an error.
Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop'
}
}
catch [System.IO.FileNotFoundException]
{
throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.'
}
}
```
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,6 @@
"pester.suppressCodeLensNotice": true,
"markdownlint.ignore": [
".github/CODEOWNERS"
]
],
"github.copilot.chat.codeGeneration.useInstructionFiles": true
}
27 changes: 20 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Public commands:
- `Install-SqlDscReportingService`
- `Install-SqlDscBIReportServer`
- `Repair-SqlDscReportingService`
- `Repair-SqlDscBIReportServer`
- `Uninstall-SqlDscReportingService`
- `Uninstall-SqlDscBIReportServer`
- `Get-SqlDscInstalledInstance` to retrieve installed SQL instances.
- `Get-SqlDscRSSetupConfiguration` to retrieve the setup configuration of
SQL Server Reporting Services or Power BI Report Server ([issue #2072](https://github.com/dsccommunity/SqlServerDsc/issues/2072)).
- `Install-SqlDscReportingService` to install SQL Server Reporting Services
([issue #2010](https://github.com/dsccommunity/SqlServerDsc/issues/2010)).
- `Install-SqlDscBIReportServer` to install SQL Server BI Report Server.
([issue #2010](https://github.com/dsccommunity/SqlServerDsc/issues/2010)).
- `Repair-SqlDscReportingService` to repair an already installed SQL Server
Reporting Services ([issue #2064](https://github.com/dsccommunity/SqlServerDsc/issues/2064)).
- `Repair-SqlDscBIReportServer` to repair an already installed SQL Server
BI Report Server ([issue #2064](https://github.com/dsccommunity/SqlServerDsc/issues/2064)).
- `Uninstall-SqlDscReportingService` to uninstall SQL Server Reporting
Services ([issue #2065](https://github.com/dsccommunity/SqlServerDsc/issues/2065)).
- `Uninstall-SqlDscBIReportServer` to uninstall SQL Server BI Report Server
([issue #2065](https://github.com/dsccommunity/SqlServerDsc/issues/2065)).
- Private function:
- `Invoke-ReportServerSetupAction`
- `Invoke-ReportServerSetupAction` to run setup actions for Reporting
Services and Power BI Report Server.
- Added new instructions for GitHub Copilot that might assist when developing
command and private functions in the module. More instructions should be
added as needed to help generated code and tests.

### Changed

Expand Down
20 changes: 14 additions & 6 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ stages:
testResultsFormat: 'NUnit'
testResultsFiles: '$(buildFolderName)/$(testResultFolderName)/NUnit*.xml'
testRunTitle: 'HQRM'

- job: Test_Unit
displayName: 'Unit'
pool:
Expand Down Expand Up @@ -155,6 +156,7 @@ stages:
targetPath: '$(buildFolderName)/$(testResultFolderName)/'
artifactName: $(testArtifactName)
parallel: true

- job: Code_Coverage
displayName: 'Publish Code Coverage'
dependsOn: Test_Unit
Expand Down Expand Up @@ -277,22 +279,22 @@ stages:
matrix:
SQL2017_WIN2019:
JOB_VMIMAGE: 'windows-2019'
TEST_CONFIGURATION: 'Integration_SQL2017'
TEST_CONFIGURATION: 'Integration_SQL2017_RS'
SQL2017_WIN2022:
JOB_VMIMAGE: 'windows-2022'
TEST_CONFIGURATION: 'Integration_SQL2017'
TEST_CONFIGURATION: 'Integration_SQL2017_RS'
SQL2019_WIN2019:
JOB_VMIMAGE: 'windows-2019'
TEST_CONFIGURATION: 'Integration_SQL2019'
TEST_CONFIGURATION: 'Integration_SQL2019_RS'
SQL2019_WIN2022:
JOB_VMIMAGE: 'windows-2022'
TEST_CONFIGURATION: 'Integration_SQL2019'
TEST_CONFIGURATION: 'Integration_SQL2019_RS'
SQL2022_WIN2019:
JOB_VMIMAGE: 'windows-2019'
TEST_CONFIGURATION: 'Integration_SQL2022'
TEST_CONFIGURATION: 'Integration_SQL2022_RS'
SQL2022_WIN2022:
JOB_VMIMAGE: 'windows-2022'
TEST_CONFIGURATION: 'Integration_SQL2022'
TEST_CONFIGURATION: 'Integration_SQL2022_RS'
pool:
vmImage: $(JOB_VMIMAGE)
timeoutInMinutes: 0
Expand Down Expand Up @@ -323,6 +325,9 @@ stages:
'tests/Integration/Commands/Prerequisites.Integration.Tests.ps1'
# Group 1
'tests/Integration/Commands/Install-SqlDscReportingService.Integration.Tests.ps1'
# Group 2
'tests/Integration/Commands/Get-SqlDscInstalledInstance.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSSetupConfiguration.Integration.Tests.ps1'
# Group 8
'tests/Integration/Commands/Repair-SqlDscReportingService.Integration.Tests.ps1'
# Group 9
Expand Down Expand Up @@ -382,6 +387,9 @@ stages:
'tests/Integration/Commands/Prerequisites.Integration.Tests.ps1'
# Group 1
'tests/Integration/Commands/Install-SqlDscBIReportServer.Integration.Tests.ps1'
# Group 2
'tests/Integration/Commands/Get-SqlDscInstalledInstance.Integration.Tests.ps1'
'tests/Integration/Commands/Get-SqlDscRSSetupConfiguration.Integration.Tests.ps1'
# Group 8
'tests/Integration/Commands/Repair-SqlDscBIReportServer.Integration.Tests.ps1'
# Group 9
Expand Down
99 changes: 99 additions & 0 deletions source/Public/Get-SqlDscInstalledInstance.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<#
.SYNOPSIS
Returns the installed instances on the current node.

.DESCRIPTION
Returns the installed instances on the current node.

.PARAMETER InstanceName
Specifies the instance name to return instances for.

.PARAMETER ServiceType
Specifies the service type to filter instances by. Valid values are
'DatabaseEngine', 'AnalysisServices', and 'ReportingServices'.

.OUTPUTS
`[System.Object[]]`

.EXAMPLE
Get-SqlDscInstalledInstance

Returns all installed instances.
#>
function Get-SqlDscInstalledInstance
{
[CmdletBinding()]
[OutputType([System.Object[]])]
param
(
[Parameter()]
[System.String]
$InstanceName,

[Parameter()]
[ValidateSet('DatabaseEngine', 'AnalysisServices', 'ReportingServices')]
[System.String[]]
$ServiceType
)

if ($PSBoundParameters.ContainsKey('InstanceName'))
{
$InstanceName = $InstanceName.ToUpper()
}

$instances = @()

$installedServiceType = Get-ChildItem -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names' -ErrorAction 'SilentlyContinue'

foreach ($currentServiceType in $installedServiceType)
{
$serviceTypeName = switch ($currentServiceType.PSChildName)
{
'OLAP'
{
'AnalysisServices'

break
}

'SQL'
{
'DatabaseEngine'

break
}

'RS'
{
'ReportingServices'

break
}
}

if ($PSBoundParameters.ContainsKey('ServiceType') -and $serviceTypeName -notin $ServiceType)
{
continue
}

$instanceNames = $currentServiceType.GetValueNames()

foreach ($currentInstanceName in $instanceNames)
{
if ($PSBoundParameters.ContainsKey('InstanceName') -and $currentInstanceName -ne $InstanceName)
{
continue
}

$foundInstance = [PSCustomObject] @{
ServiceType = $serviceTypeName
InstanceName = $currentInstanceName
InstanceId = $currentServiceType.GetValue($currentInstanceName)
}

$instances += $foundInstance
}
}

return $instances
}
Loading
Loading