From a3d2fe9f7d58b4a52ba797300aed235dffe1e599 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:58:50 +0100 Subject: [PATCH 01/44] Initial config and setup --- .vscode/settings.json | 12 +- RequiredModules.psd1 | 3 +- azure-pipelines.yml | 2 +- build.yaml | 65 ++- .../MSFT_xRDCertificateConfiguration.psm1 | 13 +- .../MSFT_xRDConnectionBrokerHAMode.psm1 | 13 +- .../MSFT_xRDGatewayConfiguration.psm1 | 12 +- .../MSFT_xRDLicenseConfiguration.psm1 | 10 +- .../MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 | 14 +- .../MSFT_xRDServer/MSFT_xRDServer.psm1 | 11 +- .../MSFT_xRDSessionCollection.psm1 | 10 +- ...SFT_xRDSessionCollectionConfiguration.psm1 | 14 +- .../MSFT_xRDSessionDeployment.psm1 | 9 +- ...emoteDesktopSessionHostCommon.strings.psd1 | 4 - ...Get-xRemoteDesktopSessionHostOsVersion.ps1 | 4 + ...xRemoteDesktopSessionHostOsRequirement.ps1 | 4 + ...moteDesktopSessionHost.Common.strings.psd1 | 4 + .../prefix.ps1 | 5 + .../xRemoteDesktopSessionHost.Common.psd1} | 10 +- .../xRemoteDesktopSessionHostCommon.psm1 | 70 --- .../xRemoteDesktopSessionHost.strings.psd1 | 4 + ...MSFT_xRDCertificateConfiguration.Tests.ps1 | 343 +++++++++++++ ...MSFT_xRDCertificateConfiguration.tests.ps1 | 261 ---------- .../MSFT_xRDConnectionBrokerHAMode.Tests.ps1 | 109 +++++ .../MSFT_xRDConnectionBrokerHAMode.tests.ps1 | 109 ----- .../MSFT_xRDGatewayConfiguration.Tests.ps1 | 454 ++++++++++++++++++ .../MSFT_xRDGatewayConfiguration.tests.ps1 | 454 ------------------ .../MSFT_xRDLicenseConfiguration.Tests.ps1 | 110 +++++ .../MSFT_xRDLicenseConfiguration.tests.ps1 | 110 ----- tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 | 287 +++++++++++ tests/Unit/MSFT_xRDRemoteApp.tests.ps1 | 287 ----------- tests/Unit/MSFT_xRDServer.Tests.ps1 | 145 ++++++ tests/Unit/MSFT_xRDServer.tests.ps1 | 145 ------ .../Unit/MSFT_xRDSessionCollection.Tests.ps1 | 427 ++++++++++++++++ .../Unit/MSFT_xRDSessionCollection.tests.ps1 | 427 ---------------- ...RDSessionCollectionConfiguration.Tests.ps1 | 340 +++++++++++++ ...RDSessionCollectionConfiguration.tests.ps1 | 340 ------------- .../Unit/MSFT_xRDSessionDeployment.Tests.ps1 | 405 ++++++++++++++++ .../Unit/MSFT_xRDSessionDeployment.tests.ps1 | 405 ---------------- ...eDesktopSessionHostOsRequirement.Tests.ps1 | 91 ++++ .../Unit/xRemoteDesktopSessionHost.tests.ps1 | 58 --- 41 files changed, 2870 insertions(+), 2730 deletions(-) delete mode 100644 source/Modules/en-US/xRemoteDesktopSessionHostCommon.strings.psd1 create mode 100644 source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 create mode 100644 source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 create mode 100644 source/Modules/xRemoteDesktopSessionHost.Common/en-US/xRemoteDesktopSessionHost.Common.strings.psd1 create mode 100644 source/Modules/xRemoteDesktopSessionHost.Common/prefix.ps1 rename source/Modules/{xRemoteDesktopSessionHostCommon.psd1 => xRemoteDesktopSessionHost.Common/xRemoteDesktopSessionHost.Common.psd1} (93%) delete mode 100644 source/Modules/xRemoteDesktopSessionHostCommon.psm1 create mode 100644 source/en-US/xRemoteDesktopSessionHost.strings.psd1 create mode 100644 tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDCertificateConfiguration.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDConnectionBrokerHAMode.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDGatewayConfiguration.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDLicenseConfiguration.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDRemoteApp.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDServer.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDServer.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDSessionCollection.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDSessionCollectionConfiguration.tests.ps1 create mode 100644 tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 delete mode 100644 tests/Unit/MSFT_xRDSessionDeployment.tests.ps1 create mode 100644 tests/Unit/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.Tests.ps1 delete mode 100644 tests/Unit/xRemoteDesktopSessionHost.tests.ps1 diff --git a/.vscode/settings.json b/.vscode/settings.json index d8a38e7..3fc5381 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -44,5 +44,15 @@ "[markdown]": { "files.trimTrailingWhitespace": true, "files.encoding": "utf8" - } + }, + "powershell.pester.useLegacyCodeLens": false, + "pester.testFilePath": [ + "[tT]ests/[qQ][aA]/*.[tT]ests.[pP][sS]1", + "[tT]ests/[uU]nit/**/*.[tT]ests.[pP][sS]1", + "[tT]ests/[uU]nit/*.[tT]ests.[pP][sS]1" + ], + "pester.runTestsInNewProcess": true, + "pester.pesterModulePath": "./output/RequiredModules/Pester", + "powershell.pester.codeLens": true, + "pester.suppressCodeLensNotice": true, } diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index af4f121..4468378 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -9,7 +9,7 @@ InvokeBuild = 'latest' PSScriptAnalyzer = 'latest' - Pester = '4.10.1' + Pester = 'latest' Plaster = 'latest' ModuleBuilder = 'latest' ChangelogManagement = 'latest' @@ -24,6 +24,7 @@ # Analyzer rules 'DscResource.AnalyzerRules' = 'latest' + 'Indented.ScriptAnalyzerRules' = 'latest' # Prerequisite modules for documentation. 'DscResource.DocGenerator' = 'latest' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 70d0af6..b4e9678 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -110,7 +110,7 @@ stages: inputs: filePath: './build.ps1' arguments: "-Tasks test -PesterScript 'tests/Unit'" - pwsh: false + pwsh: true - task: PublishTestResults@2 displayName: 'Publish Test Results' diff --git a/build.yaml b/build.yaml index f96b0a4..88a0a16 100644 --- a/build.yaml +++ b/build.yaml @@ -13,13 +13,20 @@ VersionedOutputDirectory: true #################################################### # ModuleBuilder Submodules Configuration # #################################################### - NestedModule: DscResource.Common: CopyOnly: true Path: ./output/RequiredModules/DscResource.Common AddToManifest: false Exclude: PSGetModuleInfo.xml + xRemoteDesktopSessionHost.Common: + Prefix: prefix.ps1 + VersionedOutputDirectory: false + CopyPaths: + - en-US + Encoding: UTF8 + AddToManifest: false + Exclude: PSGetModuleInfo.xml #################################################### # Sampler Pipeline Configuration # @@ -48,11 +55,12 @@ BuildWorkflow: - package_module_nupkg hqrmtest: - - DscResource_Tests_Stop_On_Fail + - Invoke_HQRM_Tests_Stop_On_Fail test: - Pester_Tests_Stop_On_Fail - - Pester_if_Code_Coverage_Under_Threshold + - Convert_Pester_Coverage + - Pester_If_Code_Coverage_Under_Threshold merge: - Merge_CodeCoverage_Files @@ -66,16 +74,23 @@ BuildWorkflow: # PESTER Configuration # #################################################### Pester: - OutputFormat: NUnitXML + Configuration: + Run: + Path: + - tests/Unit + Output: + Verbosity: Detailed + StackTraceVerbosity: Full + CIFormat: Auto + CodeCoverage: + CoveragePercentTarget: 85 + OutputEncoding: ascii + UseBreakpoints: false + TestResult: + OutputFormat: NUnitXML + OutputEncoding: ascii ExcludeFromCodeCoverage: - Modules/DscResource.Common - Script: - - tests/Unit - ExcludeTag: - Tag: - CodeCoverageThreshold: 85 - CodeCoverageOutputFileEncoding: ascii - #################################################### # Code Coverage Configuration # @@ -91,13 +106,27 @@ CodeCoverage: # Pester Configuration (DscResource.Test) # #################################################### DscTest: - ExcludeTag: - - "Common Tests - New Error-Level Script Analyzer Rules" - Tag: - ExcludeSourceFile: - - output - ExcludeModuleFile: - - Modules/DscResource.Common + Pester: + Configuration: + Filter: + ExcludeTag: + - 'Common Tests - New Error-Level Script Analyzer Rules' + Output: + Verbosity: Detailed + CIFormat: Auto + TestResult: + Enabled: true + OutputFormat: NUnitXML + OutputEncoding: ascii + OutputPath: ./output/testResults/NUnitXml_HQRM_Tests.xml + Script: + ExcludeSourceFile: + - output + ExcludeModuleFile: + - Modules/DscResource.Common + # Must exclude built module file because it should not be tested like MOF-based resources + - xRemoteDesktopSessionHost.psm1 + MainGitBranch: master ModuleBuildTasks: Sampler: diff --git a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 index ccb20a2..17473cd 100644 --- a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 @@ -1,10 +1,17 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop -Global -$script:localizedData = Get-LocalizedData -ResourceName 'MSFT_xRDCertificateConfiguration' + +# Assert-Module -ModuleName 'RemoteDesktop' + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 index dec6819..f06a942 100644 --- a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 +++ b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 @@ -1,9 +1,16 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module -Name RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' + $localhost = [System.Net.Dns]::GetHostByName((hostname)).HostName ####################################################################### @@ -31,6 +38,7 @@ function Get-TargetResource [ValidateLength(1, 256)] [string] $ClientAccessName ) + Write-Verbose -Message ($script:localizedData.VerboseGetHAMode -f $ConnectionBroker, $ClientAccessName) if ([string]::IsNullOrWhiteSpace($ConnectionBroker)) @@ -126,6 +134,7 @@ function Test-TargetResource [ValidateLength(1, 256)] [string] $ClientAccessName ) + Write-Verbose ($script:localizedData.VerboseTestHAMode -f $ConnectionBroker, $ClientAccessName) if ([string]::IsNullOrWhiteSpace($ConnectionBroker)) diff --git a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 index 5d81c9e..f13deca 100644 --- a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 @@ -1,9 +1,15 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' function ValidateCustomModeParameters { @@ -197,7 +203,7 @@ function Set-TargetResource Write-Verbose "Starting RD Gateway configuration for the RD deployment at broker '$ConnectionBroker'..." # validate parameters - ValidateCustomModeParameters $GatewayMode $ExternalFqdn $LogonMethod $UseCachedCredentials $BypassLocal + ValidateCustomModeParameters -mode $GatewayMode -ExternalFqdn $ExternalFqdn -LogonMethod $LogonMethod -UseCachedCredentials $UseCachedCredentials -BypassLocal $BypassLocal if ($GatewayServer) { diff --git a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 index bd72dda..9975664 100644 --- a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 @@ -1,9 +1,15 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 index 7a5430d..bbd3e98 100644 --- a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 +++ b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 @@ -1,17 +1,15 @@ -$resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent -$modulesFolderPath = Join-Path -Path $resourceModulePath -ChildPath 'Modules' +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' -$rdCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'xRemoteDesktopSessionHostCommon.psm1' -Import-Module -Name $rdCommonModulePath - -$dscResourceCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'DscResource.Common' -Import-Module -Name $dscResourceCommonModulePath +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index ab4a869..eae1838 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -1,9 +1,16 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' + $localhost = [System.Net.Dns]::GetHostByName((hostname)).HostName function ValidateCustomModeParameters diff --git a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 index c6b90d0..0ed2e9d 100644 --- a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 @@ -1,9 +1,15 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') + if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 index 46e5f5a..5244936 100644 --- a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 @@ -1,17 +1,15 @@ -$resourceModulePath = Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent -$modulesFolderPath = Join-Path -Path $resourceModulePath -ChildPath 'Modules' +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' -$rdCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'xRemoteDesktopSessionHostCommon.psm1' -Import-Module -Name $rdCommonModulePath - -$dscResourceCommonModulePath = Join-Path -Path $modulesFolderPath -ChildPath 'DscResource.Common' -Import-Module -Name $dscResourceCommonModulePath +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 index bb54e2f..3f72900 100644 --- a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 +++ b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 @@ -1,10 +1,15 @@ -Import-Module -Name "$PSScriptRoot\..\..\Modules\xRemoteDesktopSessionHostCommon.psm1" +$modulePath = Join-Path -Path (Split-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -Parent) -ChildPath 'Modules' + +# Import the Common Modules +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'xRemoteDesktopSessionHost.Common') +Import-Module -Name (Join-Path -Path $modulePath -ChildPath 'DscResource.Common') if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) { throw 'The minimum OS requirement was not met.' } -Import-Module RemoteDesktop + +Assert-Module -ModuleName 'RemoteDesktop' ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/Modules/en-US/xRemoteDesktopSessionHostCommon.strings.psd1 b/source/Modules/en-US/xRemoteDesktopSessionHostCommon.strings.psd1 deleted file mode 100644 index 093a536..0000000 --- a/source/Modules/en-US/xRemoteDesktopSessionHostCommon.strings.psd1 +++ /dev/null @@ -1,4 +0,0 @@ -# Localized resources for xRemoteDesktopSessionHostCommon - -ConvertFrom-StringData @' -'@ diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 new file mode 100644 index 0000000..928cf38 --- /dev/null +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 @@ -0,0 +1,4 @@ +function Get-xRemoteDesktopSessionHostOsVersion +{ + return [Environment]::OSVersion.Version +} diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 new file mode 100644 index 0000000..64792e9 --- /dev/null +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 @@ -0,0 +1,4 @@ +function Test-xRemoteDesktopSessionHostOsRequirement +{ + return (Get-xRemoteDesktopSessionHostOsVersion) -ge (New-Object 'Version' 6, 2, 9200, 0) +} diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/en-US/xRemoteDesktopSessionHost.Common.strings.psd1 b/source/Modules/xRemoteDesktopSessionHost.Common/en-US/xRemoteDesktopSessionHost.Common.strings.psd1 new file mode 100644 index 0000000..2072807 --- /dev/null +++ b/source/Modules/xRemoteDesktopSessionHost.Common/en-US/xRemoteDesktopSessionHost.Common.strings.psd1 @@ -0,0 +1,4 @@ +# Localized resources for xRemoteDesktopSessionHost.Common + +ConvertFrom-StringData @' +'@ diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/prefix.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/prefix.ps1 new file mode 100644 index 0000000..0d47385 --- /dev/null +++ b/source/Modules/xRemoteDesktopSessionHost.Common/prefix.ps1 @@ -0,0 +1,5 @@ +$script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath '../DscResource.Common' + +Import-Module -Name $script:dscResourceCommonModulePath + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' diff --git a/source/Modules/xRemoteDesktopSessionHostCommon.psd1 b/source/Modules/xRemoteDesktopSessionHost.Common/xRemoteDesktopSessionHost.Common.psd1 similarity index 93% rename from source/Modules/xRemoteDesktopSessionHostCommon.psd1 rename to source/Modules/xRemoteDesktopSessionHost.Common/xRemoteDesktopSessionHost.Common.psd1 index 1bf0d58..04ebf49 100644 --- a/source/Modules/xRemoteDesktopSessionHostCommon.psd1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/xRemoteDesktopSessionHost.Common.psd1 @@ -1,5 +1,5 @@ # -# Module manifest for module 'xRemoteDesktopSessionHostCommon' +# Module manifest for module 'xRemoteDesktopSessionHost.Common' # # Generated by: DSC Community # @@ -9,7 +9,7 @@ @{ # Script module or binary module file associated with this manifest. - RootModule = 'xRemoteDesktopSessionHostCommon.psm1' + RootModule = 'xRemoteDesktopSessionHost.Common.psm1' # Version number of this module. ModuleVersion = '1.0.0' @@ -69,11 +69,7 @@ # NestedModules = @() # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = @( - 'Get-LocalizedData' - 'Get-xRemoteDesktopSessionHostOsVersion' - 'Test-xRemoteDesktopSessionHostOsRequirement' - ) + FunctionsToExport = @() # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. CmdletsToExport = @() diff --git a/source/Modules/xRemoteDesktopSessionHostCommon.psm1 b/source/Modules/xRemoteDesktopSessionHostCommon.psm1 deleted file mode 100644 index 249fe0f..0000000 --- a/source/Modules/xRemoteDesktopSessionHostCommon.psm1 +++ /dev/null @@ -1,70 +0,0 @@ -function Test-xRemoteDesktopSessionHostOsRequirement -{ - return (Get-xRemoteDesktopSessionHostOsVersion) -ge (New-Object 'Version' 6, 2, 9200, 0) -} - -function Get-xRemoteDesktopSessionHostOsVersion -{ - return [Environment]::OSVersion.Version -} - -<# - .SYNOPSIS - Retrieves the localized string data based on the machine's culture. - Falls back to en-US strings if the machine's culture is not supported. - .PARAMETER ResourceName - The name of the resource as it appears before '.strings.psd1' of the localized string file. - For example: - For WindowsOptionalFeature: MSFT_WindowsOptionalFeature - For Service: MSFT_ServiceResource - For Registry: MSFT_RegistryResource - For Helper: SqlServerDscHelper - .PARAMETER ScriptRoot - Optional. The root path where to expect to find the culture folder. This is only needed - for localization in helper modules. This should not normally be used for resources. - .NOTES - To be able to use localization in the helper function, this function must - be first in the file, before Get-LocalizedData is used by itself to load - localized data for this helper module (see directly after this function). -#> -function Get-LocalizedData -{ - [CmdletBinding()] - param - ( - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [System.String] - $ResourceName, - - [Parameter()] - [ValidateNotNullOrEmpty()] - [System.String] - $ScriptRoot - ) - - if (-not $ScriptRoot) - { - $dscResourcesFolder = Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'DSCResources' - $resourceDirectory = Join-Path -Path $dscResourcesFolder -ChildPath $ResourceName - } - else - { - $resourceDirectory = $ScriptRoot - } - - $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath $PSUICulture - - if (-not (Test-Path -Path $localizedStringFileLocation)) - { - # Fallback to en-US - $localizedStringFileLocation = Join-Path -Path $resourceDirectory -ChildPath 'en-US' - } - - Import-LocalizedData ` - -BindingVariable 'localizedData' ` - -FileName "$ResourceName.strings.psd1" ` - -BaseDirectory $localizedStringFileLocation - - return $localizedData -} diff --git a/source/en-US/xRemoteDesktopSessionHost.strings.psd1 b/source/en-US/xRemoteDesktopSessionHost.strings.psd1 new file mode 100644 index 0000000..5869ec8 --- /dev/null +++ b/source/en-US/xRemoteDesktopSessionHost.strings.psd1 @@ -0,0 +1,4 @@ +# Localized resources for xRemoteDesktopSessionHost + +ConvertFrom-StringData @' +'@ diff --git a/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 new file mode 100644 index 0000000..5bc9f39 --- /dev/null +++ b/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 @@ -0,0 +1,343 @@ +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDCertificateConfiguration' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the system is in the desired state' { + BeforeAll { + Mock -CommandName Get-RDCertificate -MockWith { + @{ + Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'testdrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'testdrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + $result = Get-TargetResource @testParams + + $result.Role | Should -Be $testParams.Role + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.ImportPath | Should -Be $testParams.ImportPath + $result.Credential.UserName | Should -Be $testParams.Credential.UserName + } + } + } + + Context 'When the system is not in the desired state' { + BeforeAll { + Mock -CommandName Get-RDCertificate + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'testdrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + $result = Get-TargetResource @testParams + + # Key properties should still be returned when the resource is not present... + # $result.Role | Should -Be $testParams.Role + # $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.Role | Should -BeNullOrEmpty + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.ImportPath | Should -BeNullOrEmpty + $result.Credential.UserName | Should -BeNullOrEmpty + } + } + } +} + +# Describe 'Testing MSFT_xRDCertificateConfiguration' { + +# Mock -CommandName Set-RDCertificate + +# Context 'When a certificate is not configured' { + +# Mock -CommandName Get-RDCertificate -MockWith { +# [pscustomobject]@{ +# Thumbprint = $null +# Role = 'RDPublishing' +# } +# } -ParameterFilter { $Role -eq 'RDPublishing' } + +# Mock -CommandName Get-PfxData -MockWith { +# [pscustomobject]@{ +# EndEntityCertificates = [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' +# } +# } +# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDPublishing.pfx' } + +# $resourceNotConfiguredSplat = @{ +# Role = 'RDPublishing' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDPublishing.pfx' +# Credential = [pscredential]::new( +# 'Test', +# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) +# ) +# } + +# It 'Get-TargetResource returns no thumbprint' { +# (Get-TargetResource @resourceNotConfiguredSplat).Thumbprint | Should -BeNullOrEmpty +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse +# } + +# It 'Set-TargetResource runs Set-RDCertificate' { +# Set-TargetResource @resourceNotConfiguredSplat +# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { +# $Role -eq $resourceNotConfiguredSplat.Role -and +# $ConnectionBroker -eq $resourceNotConfiguredSplat.ConnectionBroker -and +# $ImportPath -eq $resourceNotConfiguredSplat.ImportPath -and +# $Password -eq $resourceNotConfiguredSplat.Credential.Password -and +# $Force -eq $true +# } +# } +# } + +# Context 'When the proper certificate is configured' { + +# Mock -CommandName Get-RDCertificate -MockWith { +# [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' +# Role = 'RDRedirector' +# } +# } -ParameterFilter { $Role -eq 'RDRedirector' } + +# Mock -CommandName Get-PfxData -MockWith { +# [pscustomobject]@{ +# EndEntityCertificates = [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' +# } +# } +# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDRedirector.pfx' } + +# $resourceConfiguredSplat = @{ +# Role = 'RDRedirector' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDRedirector.pfx' +# Credential = [pscredential]::new( +# 'Test', +# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) +# ) +# } + +# It 'Get-TargetResource returns the correct thumbprint' { +# (Get-TargetResource @resourceConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' +# } + +# It 'Test-TargetResource returns true' { +# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue +# } +# } + +# Context 'When a wrong certificate is configured' { + +# Mock -CommandName Get-RDCertificate -MockWith { +# [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' +# Role = 'RDGateway' +# } +# } -ParameterFilter { $Role -eq 'RDGateway' } + +# Mock -CommandName Get-PfxData -MockWith { +# [pscustomobject]@{ +# EndEntityCertificates = [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' +# } +# } +# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } + +# $resourceWrongConfiguredSplat = @{ +# Role = 'RDGateway' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDGateway.pfx' +# Credential = [pscredential]::new( +# 'Test', +# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) +# ) +# } + +# It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { +# (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse +# } + +# It 'Set-TargetResource runs Set-RDCertificate' { +# Set-TargetResource @resourceWrongConfiguredSplat +# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { +# $Role -eq $resourceWrongConfiguredSplat.Role -and +# $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and +# $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and +# $Password -eq $resourceWrongConfiguredSplat.Credential.Password -and +# $Force -eq $true +# } +# } +# } + +# Context 'When a wrong certificate is configured and the PFX file is protected based on group membership (ProtectTo)' { +# Mock -CommandName Get-RDCertificate -MockWith { +# [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' +# Role = 'RDGateway' +# } +# } -ParameterFilter { $Role -eq 'RDGateway' } + +# Mock -CommandName Get-PfxData -MockWith { +# [pscustomobject]@{ +# EndEntityCertificates = [pscustomobject]@{ +# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' +# } +# } +# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } + +# $resourceWrongConfiguredSplat = @{ +# Role = 'RDGateway' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDGateway.pfx' +# } + +# It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { +# (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse +# } + +# It 'Set-TargetResource runs Set-RDCertificate without password' { +# Set-TargetResource @resourceWrongConfiguredSplat +# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { +# $Role -eq $resourceWrongConfiguredSplat.Role -and +# $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and +# $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and +# $Force -eq $true +# } +# } +# } + +# Context 'When a certificate fails to test' { +# Mock Get-RDCertificate +# Mock Get-PfxData -MockWith { +# throw 'Cannot import PFX file' +# } + +# $resourceWrongConfiguredSplat = @{ +# Role = 'RDGateway' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDGateway.pfx' +# } + +# It 'Test-TargetResource displays a warning when a certificate fails to test' { +# $message = Test-TargetResource @resourceWrongConfiguredSplat 3>&1 +# $message | Should -Not -BeNullOrEmpty +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse +# } +# } + +# Context 'When a certificate fails to set' { + +# Mock Set-RDCertificate -MockWith { +# throw 'Failed to apply certificate' +# } + +# $resourceWrongConfiguredSplat = @{ +# Role = 'RDGateway' +# ConnectionBroker = 'connectionbroker.lan' +# ImportPath = 'testdrive:\RDGateway.pfx' +# } + +# It 'Set-TargetResource returns an error when the certificate could not be applied' { +# { Set-TargetResource @resourceWrongConfiguredSplat -ErrorAction Stop } | +# Should -Throw 'Failed to apply certificate' +# } +# } +# } diff --git a/tests/Unit/MSFT_xRDCertificateConfiguration.tests.ps1 b/tests/Unit/MSFT_xRDCertificateConfiguration.tests.ps1 deleted file mode 100644 index 444f0de..0000000 --- a/tests/Unit/MSFT_xRDCertificateConfiguration.tests.ps1 +++ /dev/null @@ -1,261 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDCertificateConfiguration' - -#region HEADER - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDCertificateConfiguration' - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "Testing $($script:DSCResourceName)" { - - Mock -CommandName Set-RDCertificate - - Context 'When a certificate is not configured' { - - Mock -CommandName Get-RDCertificate -MockWith { - [pscustomobject]@{ - Thumbprint = $null - Role = 'RDPublishing' - } - } -ParameterFilter { $Role -eq 'RDPublishing' } - - Mock -CommandName Get-PfxData -MockWith { - [pscustomobject]@{ - EndEntityCertificates = [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' - } - } - } -ParameterFilter { $ImportPath -eq 'testdrive:\RDPublishing.pfx' } - - $resourceNotConfiguredSplat = @{ - Role = 'RDPublishing' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDPublishing.pfx' - Credential = [pscredential]::new( - 'Test', - (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) - ) - } - - It 'Get-TargetResource returns no thumbprint' { - (Get-TargetResource @resourceNotConfiguredSplat).Thumbprint | Should -BeNullOrEmpty - } - - It 'Test-TargetResource returns false' { - Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse - } - - It 'Set-TargetResource runs Set-RDCertificate' { - Set-TargetResource @resourceNotConfiguredSplat - Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { - $Role -eq $resourceNotConfiguredSplat.Role -and - $ConnectionBroker -eq $resourceNotConfiguredSplat.ConnectionBroker -and - $ImportPath -eq $resourceNotConfiguredSplat.ImportPath -and - $Password -eq $resourceNotConfiguredSplat.Credential.Password -and - $Force -eq $true - } - } - } - - Context 'When the proper certificate is configured' { - - Mock -CommandName Get-RDCertificate -MockWith { - [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' - Role = 'RDRedirector' - } - } -ParameterFilter { $Role -eq 'RDRedirector' } - - Mock -CommandName Get-PfxData -MockWith { - [pscustomobject]@{ - EndEntityCertificates = [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' - } - } - } -ParameterFilter { $ImportPath -eq 'testdrive:\RDRedirector.pfx' } - - $resourceConfiguredSplat = @{ - Role = 'RDRedirector' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDRedirector.pfx' - Credential = [pscredential]::new( - 'Test', - (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) - ) - } - - It 'Get-TargetResource returns the correct thumbprint' { - (Get-TargetResource @resourceConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' - } - - It 'Test-TargetResource returns true' { - Test-TargetResource @resourceConfiguredSplat | Should -BeTrue - } - } - - Context 'When a wrong certificate is configured' { - - Mock -CommandName Get-RDCertificate -MockWith { - [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' - Role = 'RDGateway' - } - } -ParameterFilter { $Role -eq 'RDGateway' } - - Mock -CommandName Get-PfxData -MockWith { - [pscustomobject]@{ - EndEntityCertificates = [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' - } - } - } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } - - $resourceWrongConfiguredSplat = @{ - Role = 'RDGateway' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDGateway.pfx' - Credential = [pscredential]::new( - 'Test', - (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) - ) - } - - It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { - (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' - } - - It 'Test-TargetResource returns false' { - Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse - } - - It 'Set-TargetResource runs Set-RDCertificate' { - Set-TargetResource @resourceWrongConfiguredSplat - Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { - $Role -eq $resourceWrongConfiguredSplat.Role -and - $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and - $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and - $Password -eq $resourceWrongConfiguredSplat.Credential.Password -and - $Force -eq $true - } - } - } - - Context 'When a wrong certificate is configured and the PFX file is protected based on group membership (ProtectTo)' { - Mock -CommandName Get-RDCertificate -MockWith { - [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' - Role = 'RDGateway' - } - } -ParameterFilter { $Role -eq 'RDGateway' } - - Mock -CommandName Get-PfxData -MockWith { - [pscustomobject]@{ - EndEntityCertificates = [pscustomobject]@{ - Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' - } - } - } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } - - $resourceWrongConfiguredSplat = @{ - Role = 'RDGateway' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDGateway.pfx' - } - - It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { - (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' - } - - It 'Test-TargetResource returns false' { - Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse - } - - It 'Set-TargetResource runs Set-RDCertificate without password' { - Set-TargetResource @resourceWrongConfiguredSplat - Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { - $Role -eq $resourceWrongConfiguredSplat.Role -and - $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and - $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and - $Force -eq $true - } - } - } - - Context 'When a certificate fails to test' { - Mock Get-RDCertificate - Mock Get-PfxData -MockWith { - throw 'Cannot import PFX file' - } - - $resourceWrongConfiguredSplat = @{ - Role = 'RDGateway' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDGateway.pfx' - } - - It 'Test-TargetResource displays a warning when a certificate fails to test' { - $message = Test-TargetResource @resourceWrongConfiguredSplat 3>&1 - $message | Should -Not -BeNullOrEmpty - } - - It 'Test-TargetResource returns false' { - Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse - } - } - - Context 'When a certificate fails to set' { - - Mock Set-RDCertificate -MockWith { - throw 'Failed to apply certificate' - } - - $resourceWrongConfiguredSplat = @{ - Role = 'RDGateway' - ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDGateway.pfx' - } - - It 'Set-TargetResource returns an error when the certificate could not be applied' { - { Set-TargetResource @resourceWrongConfiguredSplat -ErrorAction Stop } | - Should -Throw 'Failed to apply certificate' - } - } - } - } -} -finally -{ - #region FOOTER - Invoke-TestCleanup - #endregion -} diff --git a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 new file mode 100644 index 0000000..4893890 --- /dev/null +++ b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 @@ -0,0 +1,109 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' + +# #region HEADER + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' + +# #region Function Get-TargetResource +# Describe "Testing $($script:DSCResourceName)" { +# Mock -CommandName Import-Module -MockWith {} -ParameterFilter { $Name -eq 'RemoteDesktop' } + +# Mock -CommandName Set-RDConnectionBrokerHighAvailability -ParameterFilter { $ClientAccessName -eq 'rdsfarm.contoso.com' } + +# Context 'Connection Broker not in HA mode' { + +# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'RDCB1' +# ActiveManagementServer = '' +# ClientAccessName = '' +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# } +# } + +# $resourceNotConfiguredSplat = @{ +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1.contoso.com;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# ClientAccessName = 'rdsfarm.contoso.com' +# DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' +# } + +# It 'Get-TargetResource returns no active management server' { +# (Get-TargetResource @resourceNotConfiguredSplat).ActiveManagementServer | Should -BeNullOrEmpty +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse +# } + +# It 'Set-TargetResource runs Set-RDConnectionBrokerHighAvailability' { +# Set-TargetResource @resourceNotConfiguredSplat +# Assert-MockCalled -CommandName Set-RDConnectionBrokerHighAvailability -Times 1 -Exactly -ParameterFilter { +# $DatabaseConnectionString -eq 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -and +# $ClientAccessName -eq 'rdsfarm.contoso.com' +# } +# } +# } + +# Context 'Connection Broker in HA mode' { + +# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'RDCB1' +# ActiveManagementServer = 'RDCB1' +# ClientAccessName = 'rdsfarm.contoso.com' +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# } +# } + +# $resourceConfiguredSplat = @{ +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# ClientAccessName = 'rdsfarm.contoso.com' +# } + +# It 'Get-TargetResource returns an active management server' { +# (Get-TargetResource @resourceConfiguredSplat).ActiveManagementServer | Should -Be 'RDCB1' +# } + +# It 'Test-TargetResource returns true' { +# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue +# } +# } +# } +# } +# } +# finally +# { +# #region FOOTER +# Invoke-TestCleanup +# #endregion +# } diff --git a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.tests.ps1 b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.tests.ps1 deleted file mode 100644 index 9788bc9..0000000 --- a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.tests.ps1 +++ /dev/null @@ -1,109 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' - -#region HEADER - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' - - #region Function Get-TargetResource - Describe "Testing $($script:DSCResourceName)" { - Mock -CommandName Import-Module -MockWith {} -ParameterFilter { $Name -eq 'RemoteDesktop' } - - Mock -CommandName Set-RDConnectionBrokerHighAvailability -ParameterFilter { $ClientAccessName -eq 'rdsfarm.contoso.com' } - - Context 'Connection Broker not in HA mode' { - - Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'RDCB1' - ActiveManagementServer = '' - ClientAccessName = '' - DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' - } - } - - $resourceNotConfiguredSplat = @{ - DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' - DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1.contoso.com;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' - ClientAccessName = 'rdsfarm.contoso.com' - DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' - } - - It 'Get-TargetResource returns no active management server' { - (Get-TargetResource @resourceNotConfiguredSplat).ActiveManagementServer | Should -BeNullOrEmpty - } - - It 'Test-TargetResource returns false' { - Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse - } - - It 'Set-TargetResource runs Set-RDConnectionBrokerHighAvailability' { - Set-TargetResource @resourceNotConfiguredSplat - Assert-MockCalled -CommandName Set-RDConnectionBrokerHighAvailability -Times 1 -Exactly -ParameterFilter { - $DatabaseConnectionString -eq 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -and - $ClientAccessName -eq 'rdsfarm.contoso.com' - } - } - } - - Context 'Connection Broker in HA mode' { - - Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'RDCB1' - ActiveManagementServer = 'RDCB1' - ClientAccessName = 'rdsfarm.contoso.com' - DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' - } - } - - $resourceConfiguredSplat = @{ - DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' - ClientAccessName = 'rdsfarm.contoso.com' - } - - It 'Get-TargetResource returns an active management server' { - (Get-TargetResource @resourceConfiguredSplat).ActiveManagementServer | Should -Be 'RDCB1' - } - - It 'Test-TargetResource returns true' { - Test-TargetResource @resourceConfiguredSplat | Should -BeTrue - } - } - } - } -} -finally -{ - #region FOOTER - Invoke-TestCleanup - #endregion -} diff --git a/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 new file mode 100644 index 0000000..2b48715 --- /dev/null +++ b/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 @@ -0,0 +1,454 @@ +# $script:dscModuleName = 'xRemoteDesktopSessionHost' +# $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' + +# #region HEADER + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'testbroker.fqdn' +# Gatewaymode = 'DoNotUse' +# } +# } + +# It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { +# (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# Gatewaymode = 'Custom' +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# ConnectionBroker = 'testbroker.fqdn' +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } +# } + +# $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn +# It 'Given a configured gateway, Get-TargetResource outputs property ' { +# param( +# [string]$Property +# ) + +# $getResult.$property | Should Not BeNullOrEmpty +# } -TestCases @( +# @{ +# Property = 'GatewayExternalFqdn' +# } +# @{ +# Property = 'BypassLocal' +# } +# @{ +# Property = 'LogonMethod' +# } +# @{ +# Property = 'UseCachedCredentials' +# } +# ) +# } + +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'testbroker.fqdn' +# Gatewaymode = 'DoNotUse' +# } +# } + +# $testSplat = @{ +# ConnectionBroker = 'testbroker.fqdn' +# GatewayMode = 'DoNotUse' +# ExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } + +# It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { +# Test-TargetResource @testSplat | Should be $true +# } + +# $testSplat.GatewayMode = 'Custom' +# It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# Gatewaymode = 'Custom' +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# ConnectionBroker = 'testbroker.fqdn' +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } +# } + +# $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.LogonMethod = 'Password' +# $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.ExternalFqdn = 'testgateway.external.fqdn' +# $testSplat.BypassLocal = $false +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.BypassLocal = $true +# $testSplat.UseCachedCredentials = $false +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.UseCachedCredentials = $true +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { +# Test-TargetResource @testSplat | Should be $true +# } +# } +# #endregion + + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when if GatewayMode is Custom and a parameter is missing.' { +# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | +# Should -Throw +# } +# } + +# $setSplat = @{ +# ConnectionBroker = 'testbroker.fqdn' +# GatewayServer = 'my.gateway.fqdn' +# GatewayMode = 'Custom' +# ExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } + +# Context 'Configuration changes performed by Set' { + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'my.gateway.fqdn' +# Roles = @( +# 'RDS-WEB-ACCESS', +# 'RDS-GATEWAY' +# ) +# } +# } +# Mock -CommandName Add-RDServer +# Mock -CommandName Set-RdDeploymentGatewayConfiguration + +# It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It +# } + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'testbroker.fqdn' +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } +# It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It +# } + +# It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { +# $ConnectionBroker -eq 'testbroker.fqdn' -and +# $GatewayMode -eq 'Custom' -and +# $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and +# $LogonMethod -eq 'Password' -and +# $UseCachedCredentials -eq $true -and +# $BypassLocal -eq $true -and +# $Force -eq $true +# } +# } + +# $setSplat.GatewayMode = 'DoNotUse' +# It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { +# $ConnectionBroker -eq 'testbroker.fqdn' -and +# $GatewayMode -eq 'DoNotUse' -and +# $Force -eq $true +# } +# } +# } +# } +# #endregion + +# } +# } +# finally +# { +# Invoke-TestCleanup +# } + +# #endregion HEADER + + +# try +# { +# Invoke-TestSetup + +# InModuleScope $script:DSCResourceName { +# $script:DSCResourceName = 'MSFT_xRDGatewayConfiguration' + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'testbroker.fqdn' +# Gatewaymode = 'DoNotUse' +# } +# } + +# It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { +# (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# Gatewaymode = 'Custom' +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# ConnectionBroker = 'testbroker.fqdn' +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } +# } + +# $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn +# It 'Given a configured gateway, Get-TargetResource outputs property ' { +# param( +# [string]$Property +# ) + +# $getResult.$property | Should Not BeNullOrEmpty +# } -TestCases @( +# @{ +# Property = 'GatewayExternalFqdn' +# } +# @{ +# Property = 'BypassLocal' +# } +# @{ +# Property = 'LogonMethod' +# } +# @{ +# Property = 'UseCachedCredentials' +# } +# ) +# } + +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'testbroker.fqdn' +# Gatewaymode = 'DoNotUse' +# } +# } + +# $testSplat = @{ +# ConnectionBroker = 'testbroker.fqdn' +# GatewayMode = 'DoNotUse' +# ExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } + +# It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { +# Test-TargetResource @testSplat | Should be $true +# } + +# $testSplat.GatewayMode = 'Custom' +# It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# Gatewaymode = 'Custom' +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# ConnectionBroker = 'testbroker.fqdn' +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } +# } + +# $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.LogonMethod = 'Password' +# $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.ExternalFqdn = 'testgateway.external.fqdn' +# $testSplat.BypassLocal = $false +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.BypassLocal = $true +# $testSplat.UseCachedCredentials = $false +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { +# Test-TargetResource @testSplat | Should be $false +# } + +# $testSplat.UseCachedCredentials = $true +# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { +# Test-TargetResource @testSplat | Should be $true +# } +# } +# #endregion + + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when if GatewayMode is Custom and a parameter is missing.' { +# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | +# Should -Throw +# } +# } + +# $setSplat = @{ +# ConnectionBroker = 'testbroker.fqdn' +# GatewayServer = 'my.gateway.fqdn' +# GatewayMode = 'Custom' +# ExternalFqdn = 'testgateway.external.fqdn' +# BypassLocal = $true +# LogonMethod = 'Password' +# UseCachedCredentials = $true +# } + +# Context 'Configuration changes performed by Set' { + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'my.gateway.fqdn' +# Roles = @( +# 'RDS-WEB-ACCESS', +# 'RDS-GATEWAY' +# ) +# } +# } +# Mock -CommandName Add-RDServer +# Mock -CommandName Set-RdDeploymentGatewayConfiguration + +# It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It +# } + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'testbroker.fqdn' +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } +# It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It +# } + +# It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { +# $ConnectionBroker -eq 'testbroker.fqdn' -and +# $GatewayMode -eq 'Custom' -and +# $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and +# $LogonMethod -eq 'Password' -and +# $UseCachedCredentials -eq $true -and +# $BypassLocal -eq $true -and +# $Force -eq $true +# } +# } + +# $setSplat.GatewayMode = 'DoNotUse' +# It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { +# Set-TargetResource @setSplat +# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { +# $ConnectionBroker -eq 'testbroker.fqdn' -and +# $GatewayMode -eq 'DoNotUse' -and +# $Force -eq $true +# } +# } +# } +# } +# #endregion + +# } +# } +# finally +# { +# #region FOOTER +# Invoke-TestCleanup +# #endregion +# } diff --git a/tests/Unit/MSFT_xRDGatewayConfiguration.tests.ps1 b/tests/Unit/MSFT_xRDGatewayConfiguration.tests.ps1 deleted file mode 100644 index f1406bb..0000000 --- a/tests/Unit/MSFT_xRDGatewayConfiguration.tests.ps1 +++ /dev/null @@ -1,454 +0,0 @@ -$script:dscModuleName = 'xRemoteDesktopSessionHost' -$script:dscResourceName = 'MSFT_xRDGatewayConfiguration' - -#region HEADER - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'testbroker.fqdn' - Gatewaymode = 'DoNotUse' - } - } - - It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { - (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - Gatewaymode = 'Custom' - GatewayExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - ConnectionBroker = 'testbroker.fqdn' - LogonMethod = 'Password' - UseCachedCredentials = $true - } - } - - $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn - It 'Given a configured gateway, Get-TargetResource outputs property ' { - param( - [string]$Property - ) - - $getResult.$property | Should Not BeNullOrEmpty - } -TestCases @( - @{ - Property = 'GatewayExternalFqdn' - } - @{ - Property = 'BypassLocal' - } - @{ - Property = 'LogonMethod' - } - @{ - Property = 'UseCachedCredentials' - } - ) - } - - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'testbroker.fqdn' - Gatewaymode = 'DoNotUse' - } - } - - $testSplat = @{ - ConnectionBroker = 'testbroker.fqdn' - GatewayMode = 'DoNotUse' - ExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - LogonMethod = 'Password' - UseCachedCredentials = $true - } - - It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { - Test-TargetResource @testSplat | Should be $true - } - - $testSplat.GatewayMode = 'Custom' - It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - Gatewaymode = 'Custom' - GatewayExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - ConnectionBroker = 'testbroker.fqdn' - LogonMethod = 'Password' - UseCachedCredentials = $true - } - } - - $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.LogonMethod = 'Password' - $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.ExternalFqdn = 'testgateway.external.fqdn' - $testSplat.BypassLocal = $false - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.BypassLocal = $true - $testSplat.UseCachedCredentials = $false - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.UseCachedCredentials = $true - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { - Test-TargetResource @testSplat | Should be $true - } - } - #endregion - - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when if GatewayMode is Custom and a parameter is missing.' { - { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | - Should -Throw - } - } - - $setSplat = @{ - ConnectionBroker = 'testbroker.fqdn' - GatewayServer = 'my.gateway.fqdn' - GatewayMode = 'Custom' - ExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - LogonMethod = 'Password' - UseCachedCredentials = $true - } - - Context 'Configuration changes performed by Set' { - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'my.gateway.fqdn' - Roles = @( - 'RDS-WEB-ACCESS', - 'RDS-GATEWAY' - ) - } - } - Mock -CommandName Add-RDServer - Mock -CommandName Set-RdDeploymentGatewayConfiguration - - It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It - } - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'testbroker.fqdn' - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It - } - - It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { - $ConnectionBroker -eq 'testbroker.fqdn' -and - $GatewayMode -eq 'Custom' -and - $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and - $LogonMethod -eq 'Password' -and - $UseCachedCredentials -eq $true -and - $BypassLocal -eq $true -and - $Force -eq $true - } - } - - $setSplat.GatewayMode = 'DoNotUse' - It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { - $ConnectionBroker -eq 'testbroker.fqdn' -and - $GatewayMode -eq 'DoNotUse' -and - $Force -eq $true - } - } - } - } - #endregion - - } -} -finally -{ - Invoke-TestCleanup -} - -#endregion HEADER - - -try -{ - Invoke-TestSetup - - InModuleScope $script:DSCResourceName { - $script:DSCResourceName = 'MSFT_xRDGatewayConfiguration' - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'testbroker.fqdn' - Gatewaymode = 'DoNotUse' - } - } - - It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { - (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - Gatewaymode = 'Custom' - GatewayExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - ConnectionBroker = 'testbroker.fqdn' - LogonMethod = 'Password' - UseCachedCredentials = $true - } - } - - $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn - It 'Given a configured gateway, Get-TargetResource outputs property ' { - param( - [string]$Property - ) - - $getResult.$property | Should Not BeNullOrEmpty - } -TestCases @( - @{ - Property = 'GatewayExternalFqdn' - } - @{ - Property = 'BypassLocal' - } - @{ - Property = 'LogonMethod' - } - @{ - Property = 'UseCachedCredentials' - } - ) - } - - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - ConnectionBroker = 'testbroker.fqdn' - Gatewaymode = 'DoNotUse' - } - } - - $testSplat = @{ - ConnectionBroker = 'testbroker.fqdn' - GatewayMode = 'DoNotUse' - ExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - LogonMethod = 'Password' - UseCachedCredentials = $true - } - - It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { - Test-TargetResource @testSplat | Should be $true - } - - $testSplat.GatewayMode = 'Custom' - It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - Gatewaymode = 'Custom' - GatewayExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - ConnectionBroker = 'testbroker.fqdn' - LogonMethod = 'Password' - UseCachedCredentials = $true - } - } - - $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.LogonMethod = 'Password' - $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.ExternalFqdn = 'testgateway.external.fqdn' - $testSplat.BypassLocal = $false - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.BypassLocal = $true - $testSplat.UseCachedCredentials = $false - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { - Test-TargetResource @testSplat | Should be $false - } - - $testSplat.UseCachedCredentials = $true - It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { - Test-TargetResource @testSplat | Should be $true - } - } - #endregion - - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when if GatewayMode is Custom and a parameter is missing.' { - { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | - Should -Throw - } - } - - $setSplat = @{ - ConnectionBroker = 'testbroker.fqdn' - GatewayServer = 'my.gateway.fqdn' - GatewayMode = 'Custom' - ExternalFqdn = 'testgateway.external.fqdn' - BypassLocal = $true - LogonMethod = 'Password' - UseCachedCredentials = $true - } - - Context 'Configuration changes performed by Set' { - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'my.gateway.fqdn' - Roles = @( - 'RDS-WEB-ACCESS', - 'RDS-GATEWAY' - ) - } - } - Mock -CommandName Add-RDServer - Mock -CommandName Set-RdDeploymentGatewayConfiguration - - It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It - } - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'testbroker.fqdn' - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It - } - - It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { - $ConnectionBroker -eq 'testbroker.fqdn' -and - $GatewayMode -eq 'Custom' -and - $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and - $LogonMethod -eq 'Password' -and - $UseCachedCredentials -eq $true -and - $BypassLocal -eq $true -and - $Force -eq $true - } - } - - $setSplat.GatewayMode = 'DoNotUse' - It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { - Set-TargetResource @setSplat - Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { - $ConnectionBroker -eq 'testbroker.fqdn' -and - $GatewayMode -eq 'DoNotUse' -and - $Force -eq $true - } - } - } - } - #endregion - - } -} -finally -{ - #region FOOTER - Invoke-TestCleanup - #endregion -} diff --git a/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 new file mode 100644 index 0000000..c566c55 --- /dev/null +++ b/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 @@ -0,0 +1,110 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { +# Mock Get-RDLicenseConfiguration -MockWith { return $null } +# It 'Should error if unable to get RD License config.' { +# { Get-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'NotConfigured' } | Should throw +# } +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# Mock -CommandName Get-TargetResource -MockWith { +# return @{ +# 'ConnectionBroker' = 'connectionbroker.lan' +# 'LicenseServer' = @('One', 'Two') +# 'LicenseMode' = 'PerUser' +# } +# } -ModuleName MSFT_xRDLicenseConfiguration + +# It "Should return false if there's a change in license servers." { +# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer 'One' | Should Be $false +# } + +# Mock Get-TargetResource -MockWith { +# return @{ +# 'ConnectionBroker' = 'connectionbroker.lan' +# 'LicenseServer' = @('One', 'Two') +# 'LicenseMode' = 'PerUser' +# } +# } + +# It "Should return false if there's a change in license mode." { +# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerDevice' -LicenseServer @('One', 'Two') | Should Be $false +# } + +# It 'Should return true if there are no changes in license mode.' { +# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer @('One', 'Two') | Should Be $true +# } +# } +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { + +# Context 'Configuration changes performed by Set' { + +# Mock -CommandName Set-RDLicenseConfiguration + +# It 'Given license servers, Set-RDLicenseConfiguration is called with LicenseServer parameter' { +# Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice -LicenseServer 'LicenseServer1' +# Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { +# $LicenseServer -eq 'LicenseServer1' +# } +# } + +# It 'Given no license servers, Set-RDLicenseConfiguration is called without LicenseServer parameter' { +# Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice +# Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { +# $LicenseServer -eq $null +# } -Scope It +# } +# } +# } +# #endregion +# } +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDLicenseConfiguration.tests.ps1 b/tests/Unit/MSFT_xRDLicenseConfiguration.tests.ps1 deleted file mode 100644 index 8923884..0000000 --- a/tests/Unit/MSFT_xRDLicenseConfiguration.tests.ps1 +++ /dev/null @@ -1,110 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - Mock Get-RDLicenseConfiguration -MockWith { return $null } - It 'Should error if unable to get RD License config.' { - { Get-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'NotConfigured' } | Should throw - } - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - Mock -CommandName Get-TargetResource -MockWith { - return @{ - 'ConnectionBroker' = 'connectionbroker.lan' - 'LicenseServer' = @('One', 'Two') - 'LicenseMode' = 'PerUser' - } - } -ModuleName MSFT_xRDLicenseConfiguration - - It "Should return false if there's a change in license servers." { - Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer 'One' | Should Be $false - } - - Mock Get-TargetResource -MockWith { - return @{ - 'ConnectionBroker' = 'connectionbroker.lan' - 'LicenseServer' = @('One', 'Two') - 'LicenseMode' = 'PerUser' - } - } - - It "Should return false if there's a change in license mode." { - Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerDevice' -LicenseServer @('One', 'Two') | Should Be $false - } - - It 'Should return true if there are no changes in license mode.' { - Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer @('One', 'Two') | Should Be $true - } - } - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - - Context 'Configuration changes performed by Set' { - - Mock -CommandName Set-RDLicenseConfiguration - - It 'Given license servers, Set-RDLicenseConfiguration is called with LicenseServer parameter' { - Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice -LicenseServer 'LicenseServer1' - Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { - $LicenseServer -eq 'LicenseServer1' - } - } - - It 'Given no license servers, Set-RDLicenseConfiguration is called without LicenseServer parameter' { - Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice - Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { - $LicenseServer -eq $null - } -Scope It - } - } - } - #endregion - } -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 new file mode 100644 index 0000000..c333d11 --- /dev/null +++ b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 @@ -0,0 +1,287 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDRemoteApp' + +# #region HEADER + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDRemoteApp' + +# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' +# $testInvalidCollectionSplat = @{ +# CollectionName = $testInvalidCollectionName +# DisplayName = 'name' +# FilePath = 'path' +# Alias = 'alias' +# } + +# $sourceRemoteAppValues = @{ +# CollectionName = 'TestCollection' +# DisplayName = 'MyCalc (1.0.0)' +# FilePath = 'c:\windows\system32\calc.exe' +# Alias = 'Test-MyCalc-(1.0.0)' +# Ensure = 'Present' +# FileVirtualPath = 'c:\windows\system32\calc.exe' +# FolderName = 'Test' +# CommandLineSetting = 'DoNotAllow' +# RequiredCommandLine = 'my-cmd' +# IconIndex = 0 +# IconPath = 'c:\windows\system32\calc.exe' +# UserGroups = 'DOMAIN\MyAppGroup_DLG' +# ShowInWebAccess = $true +# } + +# $xRDRemoteAppSplat = $sourceRemoteAppValues.Clone() + +# $getRDRemoteApp = $xRDRemoteAppSplat.Clone() +# $null = $getRDRemoteApp.Remove('Ensure') + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Get-TargetResource @testInvalidCollectionSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' +# It 'Should only accept valid values for CommandLineSetting' { +# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting +# } + +# Context 'When Get-TargetResource is called' { + +# Mock -CommandName Get-RDRemoteApp +# Mock -CommandName Get-RDSessionCollection + +# It 'Should return Ensure Absent, given the RemoteApp is not created yet' { +# (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Absent' +# } + +# Mock -CommandName Get-RDRemoteApp -MockWith { +# $getRDRemoteApp +# } + +# It 'Should return Ensure Present, given the RemoteApp is created' { +# (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Present' +# } + +# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') +# $xRDRemoteAppSplat.UserGroups = $userGroups +# It 'Should not generate an error, given that an array of UserGroups was specified' { +# { Get-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw +# } +# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups + +# [System.Array] $commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters +# $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + +# $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | +# Where-Object -FilterScript { $_ -notin $commonParameters } | +# ForEach-Object -Process { +# @{ +# Property = $_ +# Value = $xRDRemoteAppSplat[$_] +# } +# } + +# $getTargetResourceResult = Get-TargetResource @xRDRemoteAppSplat +# It 'Should return property with value in Get-TargetResource' { +# param( +# $Property, +# $Value +# ) + +# $getTargetResourceResult.$Property | Should Be $Value +# } -TestCases $allParameters + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# Write-Error 'Collection not found!' +# } + +# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { +# { Get-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | +# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' +# } +# } +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Set-TargetResource @testInvalidCollectionSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' +# It 'Should only accept valid values for CommandLineSetting' { +# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting +# } + +# Context 'When Set-TargetResource actions are performed' { + +# Mock -CommandName Get-RDSessionCollection +# Mock -CommandName Get-RDRemoteApp +# Mock -CommandName New-RDRemoteApp +# Mock -CommandName Remove-RDRemoteApp +# Mock -CommandName Set-RDRemoteApp + +# It 'Should call New-RDRemoteApp, given that the RemoteApp does not exist yet and Ensure is set to Present' { +# Set-TargetResource @xRDRemoteAppSplat +# Assert-MockCalled -CommandName New-RDRemoteApp -Times 1 -Scope It +# } + +# Mock -CommandName Get-RDRemoteApp -MockWith { $getRDRemoteApp } + +# It 'Should call Set-RDRemoteApp, given that the RemoteApp does exist and Ensure is set to Present' { +# Set-TargetResource @xRDRemoteAppSplat +# Assert-MockCalled -CommandName Set-RDRemoteApp -Times 1 -Scope It +# } + +# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') +# $xRDRemoteAppSplat.UserGroups = $userGroups +# It 'Should not generate an error, given that an array of UserGroups was specified' { +# { Set-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw +# } +# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups + +# $xRDRemoteAppSplat.Ensure = 'Absent' +# It 'Should call Remove-RDRemoteApp, given that the RemoteApp exists and Ensure is set to Absent' { +# Set-TargetResource @xRDRemoteAppSplat +# Assert-MockCalled -CommandName Remove-RDRemoteApp -Times 1 -Scope It +# } +# $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# Write-Error 'Collection not found!' +# } + +# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { +# { Set-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | +# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' +# } +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Test-TargetResource @testInvalidCollectionSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' +# It 'Should only accept valid values for CommandLineSetting' { +# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw +# } + +# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting +# } + +# Context 'Test output validation' { +# Mock -CommandName Get-RDSessionCollection +# Mock -CommandName Get-RDRemoteApp + +# It 'Should return false, given that the RemoteApp does not exist yet and Ensure is set to Present' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $false +# } + +# Mock -CommandName Get-RDRemoteApp -MockWith { +# $getRDRemoteApp +# } + +# It 'Should return true, given that the RemoteApp does exist and Ensure is set to Present' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $true +# } + +# $xRDRemoteAppSplat.Ensure = 'Absent' +# It 'Should return false, given that the RemoteApp exists and Ensure is set to Absent' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $false +# } +# $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure + +# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') +# $xRDRemoteAppSplat.UserGroups = $userGroups +# It 'Should not generate an error, given that an array of UserGroups was specified' { +# { Test-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw +# } + +# It 'Should return false, due to a mismatch in UserGroups' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $false +# } + +# $getRDRemoteApp.UserGroups = $userGroups +# Mock -CommandName Get-RDRemoteApp -MockWith { +# $getRDRemoteApp +# } + +# It 'Should return true, given that the comparison of UserGroups arrays succeeds' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $true +# } +# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups +# $getRDRemoteApp.UserGroups = $sourceRemoteAppValues.UserGroups + +# $getRDRemoteApp.CommandLineSetting = 'Allow' +# Mock -CommandName Get-RDRemoteApp -MockWith { +# $getRDRemoteApp +# } + +# It 'Should return false, given that the RemoteApp exists and Ensure is set to Present and a single setting is misconfigured' { +# Test-TargetResource @xRDRemoteAppSplat | Should Be $false +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# Write-Error 'Collection not found!' +# } + +# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { +# { Test-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | +# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' +# } +# } +# } +# #endregion +# } +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDRemoteApp.tests.ps1 b/tests/Unit/MSFT_xRDRemoteApp.tests.ps1 deleted file mode 100644 index 8038706..0000000 --- a/tests/Unit/MSFT_xRDRemoteApp.tests.ps1 +++ /dev/null @@ -1,287 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDRemoteApp' - -#region HEADER - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDRemoteApp' - - $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' - $testInvalidCollectionSplat = @{ - CollectionName = $testInvalidCollectionName - DisplayName = 'name' - FilePath = 'path' - Alias = 'alias' - } - - $sourceRemoteAppValues = @{ - CollectionName = 'TestCollection' - DisplayName = 'MyCalc (1.0.0)' - FilePath = 'c:\windows\system32\calc.exe' - Alias = 'Test-MyCalc-(1.0.0)' - Ensure = 'Present' - FileVirtualPath = 'c:\windows\system32\calc.exe' - FolderName = 'Test' - CommandLineSetting = 'DoNotAllow' - RequiredCommandLine = 'my-cmd' - IconIndex = 0 - IconPath = 'c:\windows\system32\calc.exe' - UserGroups = 'DOMAIN\MyAppGroup_DLG' - ShowInWebAccess = $true - } - - $xRDRemoteAppSplat = $sourceRemoteAppValues.Clone() - - $getRDRemoteApp = $xRDRemoteAppSplat.Clone() - $null = $getRDRemoteApp.Remove('Ensure') - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Get-TargetResource @testInvalidCollectionSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' - It 'Should only accept valid values for CommandLineSetting' { - { Get-TargetResource @xRDRemoteAppSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting - } - - Context 'When Get-TargetResource is called' { - - Mock -CommandName Get-RDRemoteApp - Mock -CommandName Get-RDSessionCollection - - It 'Should return Ensure Absent, given the RemoteApp is not created yet' { - (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Absent' - } - - Mock -CommandName Get-RDRemoteApp -MockWith { - $getRDRemoteApp - } - - It 'Should return Ensure Present, given the RemoteApp is created' { - (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Present' - } - - $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') - $xRDRemoteAppSplat.UserGroups = $userGroups - It 'Should not generate an error, given that an array of UserGroups was specified' { - { Get-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw - } - $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups - - [System.Array] $commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters - $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters - - $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | - Where-Object -FilterScript { $_ -notin $commonParameters } | - ForEach-Object -Process { - @{ - Property = $_ - Value = $xRDRemoteAppSplat[$_] - } - } - - $getTargetResourceResult = Get-TargetResource @xRDRemoteAppSplat - It 'Should return property with value in Get-TargetResource' { - param( - $Property, - $Value - ) - - $getTargetResourceResult.$Property | Should Be $Value - } -TestCases $allParameters - - Mock -CommandName Get-RDSessionCollection -MockWith { - Write-Error 'Collection not found!' - } - - It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { - { Get-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | - Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' - } - } - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Set-TargetResource @testInvalidCollectionSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' - It 'Should only accept valid values for CommandLineSetting' { - { Get-TargetResource @xRDRemoteAppSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting - } - - Context 'When Set-TargetResource actions are performed' { - - Mock -CommandName Get-RDSessionCollection - Mock -CommandName Get-RDRemoteApp - Mock -CommandName New-RDRemoteApp - Mock -CommandName Remove-RDRemoteApp - Mock -CommandName Set-RDRemoteApp - - It 'Should call New-RDRemoteApp, given that the RemoteApp does not exist yet and Ensure is set to Present' { - Set-TargetResource @xRDRemoteAppSplat - Assert-MockCalled -CommandName New-RDRemoteApp -Times 1 -Scope It - } - - Mock -CommandName Get-RDRemoteApp -MockWith { $getRDRemoteApp } - - It 'Should call Set-RDRemoteApp, given that the RemoteApp does exist and Ensure is set to Present' { - Set-TargetResource @xRDRemoteAppSplat - Assert-MockCalled -CommandName Set-RDRemoteApp -Times 1 -Scope It - } - - $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') - $xRDRemoteAppSplat.UserGroups = $userGroups - It 'Should not generate an error, given that an array of UserGroups was specified' { - { Set-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw - } - $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups - - $xRDRemoteAppSplat.Ensure = 'Absent' - It 'Should call Remove-RDRemoteApp, given that the RemoteApp exists and Ensure is set to Absent' { - Set-TargetResource @xRDRemoteAppSplat - Assert-MockCalled -CommandName Remove-RDRemoteApp -Times 1 -Scope It - } - $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure - - Mock -CommandName Get-RDSessionCollection -MockWith { - Write-Error 'Collection not found!' - } - - It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { - { Set-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | - Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' - } - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Test-TargetResource @testInvalidCollectionSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' - It 'Should only accept valid values for CommandLineSetting' { - { Get-TargetResource @xRDRemoteAppSplat } | Should Throw - } - - $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting - } - - Context 'Test output validation' { - Mock -CommandName Get-RDSessionCollection - Mock -CommandName Get-RDRemoteApp - - It 'Should return false, given that the RemoteApp does not exist yet and Ensure is set to Present' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $false - } - - Mock -CommandName Get-RDRemoteApp -MockWith { - $getRDRemoteApp - } - - It 'Should return true, given that the RemoteApp does exist and Ensure is set to Present' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $true - } - - $xRDRemoteAppSplat.Ensure = 'Absent' - It 'Should return false, given that the RemoteApp exists and Ensure is set to Absent' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $false - } - $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure - - $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') - $xRDRemoteAppSplat.UserGroups = $userGroups - It 'Should not generate an error, given that an array of UserGroups was specified' { - { Test-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw - } - - It 'Should return false, due to a mismatch in UserGroups' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $false - } - - $getRDRemoteApp.UserGroups = $userGroups - Mock -CommandName Get-RDRemoteApp -MockWith { - $getRDRemoteApp - } - - It 'Should return true, given that the comparison of UserGroups arrays succeeds' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $true - } - $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups - $getRDRemoteApp.UserGroups = $sourceRemoteAppValues.UserGroups - - $getRDRemoteApp.CommandLineSetting = 'Allow' - Mock -CommandName Get-RDRemoteApp -MockWith { - $getRDRemoteApp - } - - It 'Should return false, given that the RemoteApp exists and Ensure is set to Present and a single setting is misconfigured' { - Test-TargetResource @xRDRemoteAppSplat | Should Be $false - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - Write-Error 'Collection not found!' - } - - It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { - { Test-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | - Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' - } - } - } - #endregion - } -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/MSFT_xRDServer.Tests.ps1 b/tests/Unit/MSFT_xRDServer.Tests.ps1 new file mode 100644 index 0000000..f4ab68f --- /dev/null +++ b/tests/Unit/MSFT_xRDServer.Tests.ps1 @@ -0,0 +1,145 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDServer' + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDServer' + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'connectionbroker.lan' +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# } +# } + +# It 'Given a server that does not exist in the deployment, Get returns nothing' { +# Get-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should BeNullOrEmpty +# } + +# $getResult = Get-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker +# It 'Given a server connectionbroker.lan with the role RDS-CONNECTION-BROKER in the deployment, get returns property for this server' { +# param ( +# [string]$Property +# ) + +# $getResult.$Property | Should Not BeNullOrEmpty +# } -TestCases @( +# @{ +# Property = 'ConnectionBroker' +# } +# @{ +# Property = 'Server' +# } +# @{ +# Property = 'Role' +# } +# ) + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'connectionbroker.lan' +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# 'RDS-GATEWAY' +# ) +# } +# } + +# $getResult = Get-TargetResource -ConnectionBroker connectionbroker.lan -Server connectionbroker.lan -Role RDS-Gateway +# It 'Given a server with gateway role, the External Gateway FQDN is returned by Get' { +# $getResult.GatewayExternalFqdn | Should Be 'testgateway.external.fqdn' +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { +# Mock -CommandName Get-RDServer -MockWith { + +# if ($Role -eq 'RDS-Connection-Broker') +# { +# [pscustomobject]@{ +# Server = 'connectionbroker.lan' +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# } +# } + +# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { +# [pscustomobject]@{ +# GatewayExternalFqdn = 'testgateway.external.fqdn' +# } +# } + +# It 'Given a new server in the deployment, test returns false' { +# Test-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $false +# } + +# It 'Given an existing server in the deployment, but with an unassigned role, test returns false' { +# Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Gateway | Should be $false +# } + +# It 'Given an existing server in the deployment, with an existing role, test returns true' { +# Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $true +# } +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when if role is RDS-Gateway and GatewayExternalFqdn is missing.' { +# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -Server 'server1' -Role 'RDS-Gateway' } ` +# | Should throw +# } +# } +# } +# #endregion + +# } +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDServer.tests.ps1 b/tests/Unit/MSFT_xRDServer.tests.ps1 deleted file mode 100644 index 0604fa2..0000000 --- a/tests/Unit/MSFT_xRDServer.tests.ps1 +++ /dev/null @@ -1,145 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDServer' - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDServer' - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'connectionbroker.lan' - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - GatewayExternalFqdn = 'testgateway.external.fqdn' - } - } - - It 'Given a server that does not exist in the deployment, Get returns nothing' { - Get-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should BeNullOrEmpty - } - - $getResult = Get-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker - It 'Given a server connectionbroker.lan with the role RDS-CONNECTION-BROKER in the deployment, get returns property for this server' { - param ( - [string]$Property - ) - - $getResult.$Property | Should Not BeNullOrEmpty - } -TestCases @( - @{ - Property = 'ConnectionBroker' - } - @{ - Property = 'Server' - } - @{ - Property = 'Role' - } - ) - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'connectionbroker.lan' - Roles = @( - 'RDS-CONNECTION-BROKER' - 'RDS-GATEWAY' - ) - } - } - - $getResult = Get-TargetResource -ConnectionBroker connectionbroker.lan -Server connectionbroker.lan -Role RDS-Gateway - It 'Given a server with gateway role, the External Gateway FQDN is returned by Get' { - $getResult.GatewayExternalFqdn | Should Be 'testgateway.external.fqdn' - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - Mock -CommandName Get-RDServer -MockWith { - - if ($Role -eq 'RDS-Connection-Broker') - { - [pscustomobject]@{ - Server = 'connectionbroker.lan' - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - } - } - - Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { - [pscustomobject]@{ - GatewayExternalFqdn = 'testgateway.external.fqdn' - } - } - - It 'Given a new server in the deployment, test returns false' { - Test-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $false - } - - It 'Given an existing server in the deployment, but with an unassigned role, test returns false' { - Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Gateway | Should be $false - } - - It 'Given an existing server in the deployment, with an existing role, test returns true' { - Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $true - } - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when if role is RDS-Gateway and GatewayExternalFqdn is missing.' { - { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -Server 'server1' -Role 'RDS-Gateway' } ` - | Should throw - } - } - } - #endregion - - } -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 new file mode 100644 index 0000000..e697dca --- /dev/null +++ b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 @@ -0,0 +1,427 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDSessionCollection' + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDSessionCollection' + +# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' +# $testcollectionNameMulti = 'TestCollectionMulti' + +# $testCollection = @( +# @{ +# Name = 'TestCollection1' +# Description = 'Test Collection 1' +# } +# @{ +# Name = 'TestCollection2' +# Description = 'Test Collection 2' +# } +# ) + +# $testSessionHost = 'localhost' +# $testSessionHostMulti = 'rdsh1', 'rdsh2', 'rdsh3' +# $testConnectionBroker = 'localhost.fqdn' + +# $validTargetResourceCall = @{ +# CollectionName = $testCollection[0].Name +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } + +# $nonExistentTargetResourceCall1 = @{ +# CollectionName = 'TestCollection4' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } + +# $nonExistentTargetResourceCall2 = @{ +# CollectionName = 'TestCollection5' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } +# $validMultiTargetResourceCall = @{ +# CollectionName = $testcollectionNameMulti +# SessionHost = $testSessionHostMulti +# ConnectionBroker = $testConnectionBroker +# } +# $invalidMultiTargetResourceCall = @{ +# CollectionName = $testcollectionNameMulti +# SessionHost = $testSessionHostMulti | Select-Object -Skip 1 +# ConnectionBroker = $testConnectionBroker +# } + +# Import-Module RemoteDesktop -Force + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { +# Mock -CommandName Get-RDSessionCollection { +# return @( +# { +# CollectionName = $testCollection[0].Name +# CollectionDescription = 'Test Collection' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# }, +# { +# CollectionName = $testCollectionName2 +# CollectionDescription = 'Test Collection 2' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } +# ) +# } +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollection[0].Name +# SessionHost = $testSessionHost +# } +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $testSessionHostMulti +# } +# } + +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Get-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw +# } + +# Mock -CommandName Get-RDSessionCollection { +# $result = @() + +# foreach ($sessionCollection in $testCollection) +# { +# $result += New-Object -TypeName PSObject -Property @{ +# CollectionName = $sessionCollection.Name +# CollectionDescription = $sessionCollection.Description +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } +# } + +# return $result +# } + +# It 'Calls Get-RDSessionCollection with CollectionName and ConnectionBroker parameters' { +# Get-TargetResource @validTargetResourceCall +# Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope It -ParameterFilter { +# $CollectionName -eq $testCollection[0].Name -and +# $ConnectionBroker -eq $testConnectionBroker +# } +# } + +# It 'Calls Get-RDSessionHost' { +# Get-TargetResource @validTargetResourceCall +# Assert-MockCalled -CommandName Get-RDSessionHost -Times 1 -Scope It -ParameterFilter { +# $CollectionName -eq $testCollection[0].Name -and +# $ConnectionBroker -eq $testConnectionBroker +# } +# } +# } + +# Context 'Non-existent Session Collection requested (other session collections returned - Win2019 behaviour)' { + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection3' +# CollectionDescription = 'Test Collection 3' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } +# } + +# $result = Get-TargetResource @nonExistentTargetResourceCall1 +# It 'Should return return a hash table' { +# $result | Should -BeOfType System.Collections.Hashtable +# } + +# It 'Should return supplied session host, with other values being $null' { +# $result.ConnectionBroker = $null +# $result.CollectionName = $null +# $result.CollectionDescription = $null +# $result.SessionHost = $testSessionHost +# } +# } + +# Context 'Non-existent Session Collection requested (no session collections returned - normal behaviour)' { + +# Mock -CommandName Get-RDSessionCollection { +# return $null +# } + +# $result = Get-TargetResource @nonExistentTargetResourceCall2 +# It 'Should return return a hash table (Win 2019)' { +# $result | Should -BeOfType System.Collections.Hashtable +# } + +# It 'Should return supplied session host, with other values being $null' { +# $result.ConnectionBroker = $null +# $result.CollectionName = $null +# $result.CollectionDescription = $null +# $result.SessionHost = $testSessionHost +# } +# } + + +# Context 'Two Session Collections exist with same CollectionName' { +# Mock -CommandName Get-RDSessionCollection { +# $result = @() + +# foreach ($sessionCollection in $testCollection) +# { +# $result += New-Object -TypeName PSObject -Property @{ +# CollectionName = $testCollection[0].Name +# CollectionDescription = $sessionCollection.Description +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker +# } +# } + +# return $result +# } + +# It 'should throw exception' { +# { Get-TargetResource @validTargetResourceCall } | Should throw +# } +# } + +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { + +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Set-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw +# } +# } + +# Mock -CommandName Get-RDSessionCollection +# Mock -CommandName New-RDSessionCollection +# Mock -CommandName Compare-Object +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollection[0].Name +# SessionHost = $testSessionHost +# } +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $testSessionHostMulti +# } +# } + +# Context 'Validate Set-TargetResource actions' { +# It 'Given the configuration is applied, New-RDSessionCollection is called' { +# Mock -CommandName Test-TargetResource -MockWith { return $true } +# Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost -Verbose +# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context +# } +# } +# Context 'New-RDSessionCollection returns an exception, but creates the desired RDSessionCollection' { +# Mock -CommandName Test-TargetResource -MockWith { return $true } +# Mock -CommandName Compare-Object +# Mock -CommandName New-RDSessionCollection -MockWith { +# throw [Microsoft.PowerShell.Commands.WriteErrorException] 'The property EncryptionLevel is configured by using Group Policy settings. Use the Group Policy Management Console to configure this property.' +# } + +# It 'does not return an exception' { +# { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Not -Throw +# } + +# It 'calls New-RDSessionCollection' { +# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context +# } +# } + +# Context 'Get-RDSessionCollection returns an exception, without creating the desired RDSessionCollection' { +# Mock -CommandName New-RDSessionCollection -MockWith { +# throw [Microsoft.PowerShell.Commands.WriteErrorException] "A Remote Desktop Services deployment does not exist on $testConnectionBroker. This operation can be performed after creating a deployment. For information about creating a deployment, run `"Get-Help New-RDVirtualDesktopDeployment`" or `"Get-Help New-RDSessionDeployment`"" +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# $null +# } + +# It 'returns an exception' { +# { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Throw +# } + +# It 'calls New-RDSessionCollection and Get-RDSessionCollection' { +# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context +# Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope Describe +# } +# } + +# Context 'Session Collection exists, but list of session hosts is different' { +# Mock -CommandName Get-TargetResource -MockWith { +# @{ +# 'ConnectionBroker' = 'CB' +# 'CollectionDescription' = 'Description' +# 'CollectionName' = 'ExistingCollection' +# 'SessionHost' = 'SurplusHost' +# } +# } +# Mock -CommandName Compare-Object -MockWith { +# 'SurplusHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '<=' -PassThru +# 'MissingHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '=>' -PassThru +# } +# Mock -CommandName Add-RDSessionHost +# Mock -CommandName Remove-RDSessionHost + +# It 'calls Add and Remove-RDSessionHost' { +# Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true +# Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context +# Assert-MockCalled -CommandName Remove-RDSessionHost -Times 1 -Scope Context +# } + +# Mock -CommandName Get-TargetResource -MockWith { +# @{ +# 'ConnectionBroker' = 'CB' +# 'CollectionDescription' = 'Description' +# 'CollectionName' = 'ExistingCollection' +# 'SessionHost' = $null +# } +# } +# It 'calls Add-RDSessionHost if no session hosts exist' { +# Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true +# Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context +# } +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Test-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw +# } +# } + +# Context 'Validating Test-TargetResource output' { +# Mock -CommandName Get-RDSessionCollection +# Mock -CommandName Get-RDSessionHost + +# It 'Given Get-RDSessionCollection not returning a collection, test returns false' { +# Test-TargetResource @validTargetResourceCall | Should Be $false +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# [pscustomobject]@{ +# AutoAssignPersonalDesktop = $false +# CollectionAlias = $testCollection[0].Name +# CollectionDescription = 'Pester Test Collection Output' +# CollectionName = $testCollection[0].Name +# CollectionType = 'PooledUnmanaged' +# GrantAdministrativePrivilege = $false +# ResourceType = 'Remote Desktop' +# Size = 1 +# } +# } +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollection[0].Name +# SessionHost = $testSessionHost +# } +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $testSessionHostMulti +# } +# } + +# It 'Given Get-RDSessionCollection returning a collection, test returns true' { +# Test-TargetResource @validTargetResourceCall | Should Be $true +# } + +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $testSessionHostMulti +# } +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# [pscustomobject]@{ +# AutoAssignPersonalDesktop = $false +# CollectionAlias = $testCollectionNameMulti +# CollectionDescription = 'Pester Test Collection Output' +# CollectionName = $testCollectionNameMulti +# CollectionType = 'PooledUnmanaged' +# GrantAdministrativePrivilege = $false +# ResourceType = 'Remote Desktop' +# Size = 1 +# } +# } + +# It 'Given the incorrect number of session hosts it should return false' { +# Test-TargetResource @invalidMultiTargetResourceCall -Verbose -Force $true | Should Be $false +# } + +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $testSessionHostMulti +# } +# } + +# Mock -CommandName Get-RDSessionHost { +# return @{ +# CollectionName = $testCollection[0].Name +# } +# return @{ +# CollectionName = $testCollectionNameMulti +# SessionHost = $invalidTestSessionHostMulti +# } +# } + +# It 'Given an empty collection of session hosts without the force it should return true' { +# Test-TargetResource @invalidMultiTargetResourceCall -Verbose | Should Be $true +# } + +# It 'Given an empty collection of session hosts with the force it should return false' { +# Test-TargetResource @invalidMultiTargetResourceCall -Force $true -Verbose | Should Be $false +# } + +# It 'Given the list of session hosts is not equal, return false' { +# Test-TargetResource @validMultiTargetResourceCall -Force $true | Should Be $false +# } +# } +# } +# #endregion +# } +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDSessionCollection.tests.ps1 b/tests/Unit/MSFT_xRDSessionCollection.tests.ps1 deleted file mode 100644 index 808d820..0000000 --- a/tests/Unit/MSFT_xRDSessionCollection.tests.ps1 +++ /dev/null @@ -1,427 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDSessionCollection' - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDSessionCollection' - - $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' - $testcollectionNameMulti = 'TestCollectionMulti' - - $testCollection = @( - @{ - Name = 'TestCollection1' - Description = 'Test Collection 1' - } - @{ - Name = 'TestCollection2' - Description = 'Test Collection 2' - } - ) - - $testSessionHost = 'localhost' - $testSessionHostMulti = 'rdsh1', 'rdsh2', 'rdsh3' - $testConnectionBroker = 'localhost.fqdn' - - $validTargetResourceCall = @{ - CollectionName = $testCollection[0].Name - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - - $nonExistentTargetResourceCall1 = @{ - CollectionName = 'TestCollection4' - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - - $nonExistentTargetResourceCall2 = @{ - CollectionName = 'TestCollection5' - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - $validMultiTargetResourceCall = @{ - CollectionName = $testcollectionNameMulti - SessionHost = $testSessionHostMulti - ConnectionBroker = $testConnectionBroker - } - $invalidMultiTargetResourceCall = @{ - CollectionName = $testcollectionNameMulti - SessionHost = $testSessionHostMulti | Select-Object -Skip 1 - ConnectionBroker = $testConnectionBroker - } - - Import-Module RemoteDesktop -Force - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - Mock -CommandName Get-RDSessionCollection { - return @( - { - CollectionName = $testCollection[0].Name - CollectionDescription = 'Test Collection' - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - }, - { - CollectionName = $testCollectionName2 - CollectionDescription = 'Test Collection 2' - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - ) - } - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollection[0].Name - SessionHost = $testSessionHost - } - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $testSessionHostMulti - } - } - - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Get-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw - } - - Mock -CommandName Get-RDSessionCollection { - $result = @() - - foreach ($sessionCollection in $testCollection) - { - $result += New-Object -TypeName PSObject -Property @{ - CollectionName = $sessionCollection.Name - CollectionDescription = $sessionCollection.Description - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - } - - return $result - } - - It 'Calls Get-RDSessionCollection with CollectionName and ConnectionBroker parameters' { - Get-TargetResource @validTargetResourceCall - Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope It -ParameterFilter { - $CollectionName -eq $testCollection[0].Name -and - $ConnectionBroker -eq $testConnectionBroker - } - } - - It 'Calls Get-RDSessionHost' { - Get-TargetResource @validTargetResourceCall - Assert-MockCalled -CommandName Get-RDSessionHost -Times 1 -Scope It -ParameterFilter { - $CollectionName -eq $testCollection[0].Name -and - $ConnectionBroker -eq $testConnectionBroker - } - } - } - - Context 'Non-existent Session Collection requested (other session collections returned - Win2019 behaviour)' { - - Mock -CommandName Get-RDSessionCollection -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection3' - CollectionDescription = 'Test Collection 3' - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - } - - $result = Get-TargetResource @nonExistentTargetResourceCall1 - It 'Should return return a hash table' { - $result | Should -BeOfType System.Collections.Hashtable - } - - It 'Should return supplied session host, with other values being $null' { - $result.ConnectionBroker = $null - $result.CollectionName = $null - $result.CollectionDescription = $null - $result.SessionHost = $testSessionHost - } - } - - Context 'Non-existent Session Collection requested (no session collections returned - normal behaviour)' { - - Mock -CommandName Get-RDSessionCollection { - return $null - } - - $result = Get-TargetResource @nonExistentTargetResourceCall2 - It 'Should return return a hash table (Win 2019)' { - $result | Should -BeOfType System.Collections.Hashtable - } - - It 'Should return supplied session host, with other values being $null' { - $result.ConnectionBroker = $null - $result.CollectionName = $null - $result.CollectionDescription = $null - $result.SessionHost = $testSessionHost - } - } - - - Context 'Two Session Collections exist with same CollectionName' { - Mock -CommandName Get-RDSessionCollection { - $result = @() - - foreach ($sessionCollection in $testCollection) - { - $result += New-Object -TypeName PSObject -Property @{ - CollectionName = $testCollection[0].Name - CollectionDescription = $sessionCollection.Description - SessionHost = $testSessionHost - ConnectionBroker = $testConnectionBroker - } - } - - return $result - } - - It 'should throw exception' { - { Get-TargetResource @validTargetResourceCall } | Should throw - } - } - - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Set-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw - } - } - - Mock -CommandName Get-RDSessionCollection - Mock -CommandName New-RDSessionCollection - Mock -CommandName Compare-Object - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollection[0].Name - SessionHost = $testSessionHost - } - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $testSessionHostMulti - } - } - - Context 'Validate Set-TargetResource actions' { - It 'Given the configuration is applied, New-RDSessionCollection is called' { - Mock -CommandName Test-TargetResource -MockWith { return $true } - Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost -Verbose - Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - } - } - Context 'New-RDSessionCollection returns an exception, but creates the desired RDSessionCollection' { - Mock -CommandName Test-TargetResource -MockWith { return $true } - Mock -CommandName Compare-Object - Mock -CommandName New-RDSessionCollection -MockWith { - throw [Microsoft.PowerShell.Commands.WriteErrorException] 'The property EncryptionLevel is configured by using Group Policy settings. Use the Group Policy Management Console to configure this property.' - } - - It 'does not return an exception' { - { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Not -Throw - } - - It 'calls New-RDSessionCollection' { - Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - } - } - - Context 'Get-RDSessionCollection returns an exception, without creating the desired RDSessionCollection' { - Mock -CommandName New-RDSessionCollection -MockWith { - throw [Microsoft.PowerShell.Commands.WriteErrorException] "A Remote Desktop Services deployment does not exist on $testConnectionBroker. This operation can be performed after creating a deployment. For information about creating a deployment, run `"Get-Help New-RDVirtualDesktopDeployment`" or `"Get-Help New-RDSessionDeployment`"" - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - $null - } - - It 'returns an exception' { - { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Throw - } - - It 'calls New-RDSessionCollection and Get-RDSessionCollection' { - Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope Describe - } - } - - Context 'Session Collection exists, but list of session hosts is different' { - Mock -CommandName Get-TargetResource -MockWith { - @{ - 'ConnectionBroker' = 'CB' - 'CollectionDescription' = 'Description' - 'CollectionName' = 'ExistingCollection' - 'SessionHost' = 'SurplusHost' - } - } - Mock -CommandName Compare-Object -MockWith { - 'SurplusHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '<=' -PassThru - 'MissingHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '=>' -PassThru - } - Mock -CommandName Add-RDSessionHost - Mock -CommandName Remove-RDSessionHost - - It 'calls Add and Remove-RDSessionHost' { - Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true - Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context - Assert-MockCalled -CommandName Remove-RDSessionHost -Times 1 -Scope Context - } - - Mock -CommandName Get-TargetResource -MockWith { - @{ - 'ConnectionBroker' = 'CB' - 'CollectionDescription' = 'Description' - 'CollectionName' = 'ExistingCollection' - 'SessionHost' = $null - } - } - It 'calls Add-RDSessionHost if no session hosts exist' { - Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true - Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context - } - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Test-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw - } - } - - Context 'Validating Test-TargetResource output' { - Mock -CommandName Get-RDSessionCollection - Mock -CommandName Get-RDSessionHost - - It 'Given Get-RDSessionCollection not returning a collection, test returns false' { - Test-TargetResource @validTargetResourceCall | Should Be $false - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - [pscustomobject]@{ - AutoAssignPersonalDesktop = $false - CollectionAlias = $testCollection[0].Name - CollectionDescription = 'Pester Test Collection Output' - CollectionName = $testCollection[0].Name - CollectionType = 'PooledUnmanaged' - GrantAdministrativePrivilege = $false - ResourceType = 'Remote Desktop' - Size = 1 - } - } - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollection[0].Name - SessionHost = $testSessionHost - } - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $testSessionHostMulti - } - } - - It 'Given Get-RDSessionCollection returning a collection, test returns true' { - Test-TargetResource @validTargetResourceCall | Should Be $true - } - - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $testSessionHostMulti - } - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - [pscustomobject]@{ - AutoAssignPersonalDesktop = $false - CollectionAlias = $testCollectionNameMulti - CollectionDescription = 'Pester Test Collection Output' - CollectionName = $testCollectionNameMulti - CollectionType = 'PooledUnmanaged' - GrantAdministrativePrivilege = $false - ResourceType = 'Remote Desktop' - Size = 1 - } - } - - It 'Given the incorrect number of session hosts it should return false' { - Test-TargetResource @invalidMultiTargetResourceCall -Verbose -Force $true | Should Be $false - } - - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $testSessionHostMulti - } - } - - Mock -CommandName Get-RDSessionHost { - return @{ - CollectionName = $testCollection[0].Name - } - return @{ - CollectionName = $testCollectionNameMulti - SessionHost = $invalidTestSessionHostMulti - } - } - - It 'Given an empty collection of session hosts without the force it should return true' { - Test-TargetResource @invalidMultiTargetResourceCall -Verbose | Should Be $true - } - - It 'Given an empty collection of session hosts with the force it should return false' { - Test-TargetResource @invalidMultiTargetResourceCall -Force $true -Verbose | Should Be $false - } - - It 'Given the list of session hosts is not equal, return false' { - Test-TargetResource @validMultiTargetResourceCall -Force $true | Should Be $false - } - } - } - #endregion - } -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 new file mode 100644 index 0000000..48fb08c --- /dev/null +++ b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 @@ -0,0 +1,340 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DSCResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' + +# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' +# $collectionName = 'TestCollection' + +# Import-Module RemoteDesktop -Force + + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { +# $null +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# } +# } + +# Mock -CommandName Get-RDSessionHost -MockWith { +# [pscustomobject]@{ +# SessionHost = [System.Net.Dns]::GetHostByName((hostname)).HostName +# CollectionName = 'TestCollection' +# } +# } + +# Mock -CommandName Get-RDSessionCollectionConfiguration +# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# IncludeFolderPath = $null +# ExcludeFolderPath = $null +# IncludeFilePath = $null +# ExcludeFilePath = $null +# DiskPath = 'c:\temp' +# EnableUserProfileDisk = $true +# MaxUserProfileDiskSizeGB = 5 +# } +# } -ParameterFilter { $UserProfileDisk -eq $true } + +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Get-TargetResource -CollectionName $testInvalidCollectionName } ` +# | Should throw +# } +# } + +# Context 'Get properties on Windows Server 2012 (R2)' { +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'6.3.9600.0' +# } + +# It 'Should not call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { +# Get-TargetResource -CollectionName $collectionName +# Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 0 -Exactly +# } +# It 'Should not call Set-RDSessionCollectionConfiguration' { +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context +# } +# } + +# Context 'Get properties on Windows Server 2016+' { +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'10.0.14393.0' +# } + +# $getTargetResourceResult = Get-TargetResource -CollectionName $collectionName +# It 'Should call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { +# Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 1 -Exactly +# } +# It 'Get-TargetResource result should list UserProfileDisk property ' { +# param ( +# [string]$Property +# ) +# $getTargetResourceResult.GetEnumerator().Name | Where-Object { $_ -eq $Property } | Should Be $Property +# } -TestCases @( +# @{ +# Property = 'DiskPath' +# } +# @{ +# Property = 'EnableUserProfileDisk' +# } +# @{ +# Property = 'ExcludeFilePath' +# } +# @{ +# Property = 'ExcludeFolderPath' +# } +# @{ +# Property = 'IncludeFilePath' +# } +# @{ +# Property = 'IncludeFolderPath' +# } +# @{ +# Property = 'MaxUserProfileDiskSizeGB' +# } +# ) +# It 'Should not call Set-RDSessionCollectionConfiguration' { +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context +# } +# } +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { + +# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { +# $null +# } + +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Set-TargetResource -CollectionName $testInvalidCollectionName } ` +# | Should throw +# } +# } + +# Context 'Running on set on Windows Server 2012 (R2)' { +# Mock -CommandName Get-RDSessionCollection -MockWith { $true } + +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'6.3.9600.0' +# } + +# It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should not call Set-RDSessionCollectionConfiguration with parameter EnableUserProfileDisk' { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly +# } +# } + +# Context 'Running on set on Windows Server 2016 (or higher)' { +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'10.0.14393.0' +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# throw 'No session collection DoesNotExist was found.' +# } + +# It 'Trying to configure a non existing collection should throw' { +# $errorMessages = try +# { +# Set-TargetResource -CollectionName 'DoesNotExist' -ActiveSessionLimitMin 1 +# } +# catch +# { +# $_ 2>&1 +# } + +# $errorMessages.Exception.Message | Should Be 'Failed to lookup RD Session Collection DoesNotExist. Error: No session collection DoesNotExist was found.' +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { $true } +# It 'Running Set on W2016 with only EnableUserProfileDisk specified should throw on missing DiskPath parameter' { +# $errorMessages = try +# { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true +# } +# catch +# { +# $_ 2>&1 +# } + +# $errorMessages.Exception.Message | Should Be 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' +# } + +# It 'Running Set on W2016 with EnableUserProfileDisk and Diskpath specified should throw on invalid MaxUserProfileDiskSizeGB parameter' { +# $errorMessages = try +# { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ +# } +# catch +# { +# $_ 2>&1 +# } + +# $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value 0 is not valid' +# } + +# It 'Running Set with EnableUserProfileDisk, DiskPath and MaxUserProfileDiskSizeGB, but with an invalid DiskPath, should throw' { +# $errorMessages = try +# { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\NonExistingPath -MaxUserProfileDiskSizeGB 5 +# } +# catch +# { +# $_ 2>&1 +# } + +# $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a valid DiskPath. Path TestDrive:\NonExistingPath not found' +# } + +# It 'Running Set with all valid parameters should call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ -MaxUserProfileDiskSizeGB 5 +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It +# } + +# It 'Running Set without EnableUserProfileDisk should not call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { +# Set-TargetResource -CollectionName $collectionName -ActiveSessionLimitMin 1 +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly -Scope It +# } + +# It 'Running Set with EnableUserProfileDisk disabled should call Set-RDSessionCollectionConfiguration with DisableUserProfileDisk' { +# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false +# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $DisableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It +# } +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { +# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { +# $null +# } + +# Mock -CommandName Get-RDSessionCollection -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# } +# } + +# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# CustomRdpProperty = "use redirection server name:i:1`n" +# } +# } + +# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# IncludeFolderPath = $null +# ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') +# IncludeFilePath = $null +# ExcludeFilePath = $null +# DiskPath = 'c:\temp' +# EnableUserProfileDisk = $false +# MaxUserProfileDiskSizeGB = 5 +# } +# } -ParameterFilter { $UserProfileDisk -eq $true } + +# Context 'Parameter Values,Validations and Errors' { + +# It 'Should error when CollectionName length is greater than 256' { +# { Test-TargetResource -CollectionName $testInvalidCollectionName } ` +# | Should throw +# } +# } + +# Context 'Running on test on Windows Server 2012 (R2)' { +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'6.3.9600.0' +# } + +# It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should ignore the EnableUserProfile property (Test returns True - In Desired State)' { +# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $True +# } +# } + +# Context 'Running on test on Windows Server 2016 (or higher)' { +# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { +# [version]'10.0.14393.0' +# } + +# It 'Running on Windows Server 2016+ with EnableUserProfile disk set to True and current setting set to false should return Test result False - Not In Desired State' { +# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $false +# } + +# It 'Running on Windows Server 2016+ with EnableUserProfile disk set to False and current setting set to false should return Test result True - In Desired State' { +# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false | Should be $True +# } + +# It 'Running on Windows Server 2016+ with CustomRdpProperties specified and existing setting matching with a trailing newline should return Test result True - In Desired State' { +# Test-TargetResource -CollectionName $collectionName -CustomRdpProperty 'use redirection server name:i:1' | +# Should be $true +# } + +# It 'Running on Windows Server 2016+ with out-of-order ExcludeFolderPath values and current EnableUserProfile setting set to true should return Test result True - In Desired State' { +# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { +# [pscustomobject]@{ +# CollectionName = 'TestCollection' +# EnableUserProfileDisk = $true +# ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') +# } +# } -ParameterFilter { $UserProfileDisk -eq $true } + +# $testTargetSplat = @{ +# CollectionName = $collectionName +# EnableUserProfileDisk = $true +# ExcludeFolderPath = @('c:\temp\bar', 'c:\temp\foo') +# } +# Test-TargetResource @testTargetSplat | Should be $true +# } +# } +# } +# #endregion +# } +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.tests.ps1 b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.tests.ps1 deleted file mode 100644 index 8a9e056..0000000 --- a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.tests.ps1 +++ /dev/null @@ -1,340 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DSCResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' - - $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' - $collectionName = 'TestCollection' - - Import-Module RemoteDesktop -Force - - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { - $null - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - } - } - - Mock -CommandName Get-RDSessionHost -MockWith { - [pscustomobject]@{ - SessionHost = [System.Net.Dns]::GetHostByName((hostname)).HostName - CollectionName = 'TestCollection' - } - } - - Mock -CommandName Get-RDSessionCollectionConfiguration - Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - IncludeFolderPath = $null - ExcludeFolderPath = $null - IncludeFilePath = $null - ExcludeFilePath = $null - DiskPath = 'c:\temp' - EnableUserProfileDisk = $true - MaxUserProfileDiskSizeGB = 5 - } - } -ParameterFilter { $UserProfileDisk -eq $true } - - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Get-TargetResource -CollectionName $testInvalidCollectionName } ` - | Should throw - } - } - - Context 'Get properties on Windows Server 2012 (R2)' { - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'6.3.9600.0' - } - - It 'Should not call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { - Get-TargetResource -CollectionName $collectionName - Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 0 -Exactly - } - It 'Should not call Set-RDSessionCollectionConfiguration' { - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context - } - } - - Context 'Get properties on Windows Server 2016+' { - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'10.0.14393.0' - } - - $getTargetResourceResult = Get-TargetResource -CollectionName $collectionName - It 'Should call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { - Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 1 -Exactly - } - It 'Get-TargetResource result should list UserProfileDisk property ' { - param ( - [string]$Property - ) - $getTargetResourceResult.GetEnumerator().Name | Where-Object { $_ -eq $Property } | Should Be $Property - } -TestCases @( - @{ - Property = 'DiskPath' - } - @{ - Property = 'EnableUserProfileDisk' - } - @{ - Property = 'ExcludeFilePath' - } - @{ - Property = 'ExcludeFolderPath' - } - @{ - Property = 'IncludeFilePath' - } - @{ - Property = 'IncludeFolderPath' - } - @{ - Property = 'MaxUserProfileDiskSizeGB' - } - ) - It 'Should not call Set-RDSessionCollectionConfiguration' { - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context - } - } - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - - Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { - $null - } - - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Set-TargetResource -CollectionName $testInvalidCollectionName } ` - | Should throw - } - } - - Context 'Running on set on Windows Server 2012 (R2)' { - Mock -CommandName Get-RDSessionCollection -MockWith { $true } - - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'6.3.9600.0' - } - - It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should not call Set-RDSessionCollectionConfiguration with parameter EnableUserProfileDisk' { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly - } - } - - Context 'Running on set on Windows Server 2016 (or higher)' { - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'10.0.14393.0' - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - throw 'No session collection DoesNotExist was found.' - } - - It 'Trying to configure a non existing collection should throw' { - $errorMessages = try - { - Set-TargetResource -CollectionName 'DoesNotExist' -ActiveSessionLimitMin 1 - } - catch - { - $_ 2>&1 - } - - $errorMessages.Exception.Message | Should Be 'Failed to lookup RD Session Collection DoesNotExist. Error: No session collection DoesNotExist was found.' - } - - Mock -CommandName Get-RDSessionCollection -MockWith { $true } - It 'Running Set on W2016 with only EnableUserProfileDisk specified should throw on missing DiskPath parameter' { - $errorMessages = try - { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true - } - catch - { - $_ 2>&1 - } - - $errorMessages.Exception.Message | Should Be 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' - } - - It 'Running Set on W2016 with EnableUserProfileDisk and Diskpath specified should throw on invalid MaxUserProfileDiskSizeGB parameter' { - $errorMessages = try - { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ - } - catch - { - $_ 2>&1 - } - - $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value 0 is not valid' - } - - It 'Running Set with EnableUserProfileDisk, DiskPath and MaxUserProfileDiskSizeGB, but with an invalid DiskPath, should throw' { - $errorMessages = try - { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\NonExistingPath -MaxUserProfileDiskSizeGB 5 - } - catch - { - $_ 2>&1 - } - - $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a valid DiskPath. Path TestDrive:\NonExistingPath not found' - } - - It 'Running Set with all valid parameters should call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ -MaxUserProfileDiskSizeGB 5 - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It - } - - It 'Running Set without EnableUserProfileDisk should not call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { - Set-TargetResource -CollectionName $collectionName -ActiveSessionLimitMin 1 - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly -Scope It - } - - It 'Running Set with EnableUserProfileDisk disabled should call Set-RDSessionCollectionConfiguration with DisableUserProfileDisk' { - Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false - Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $DisableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It - } - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { - $null - } - - Mock -CommandName Get-RDSessionCollection -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - } - } - - Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - CustomRdpProperty = "use redirection server name:i:1`n" - } - } - - Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - IncludeFolderPath = $null - ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') - IncludeFilePath = $null - ExcludeFilePath = $null - DiskPath = 'c:\temp' - EnableUserProfileDisk = $false - MaxUserProfileDiskSizeGB = 5 - } - } -ParameterFilter { $UserProfileDisk -eq $true } - - Context 'Parameter Values,Validations and Errors' { - - It 'Should error when CollectionName length is greater than 256' { - { Test-TargetResource -CollectionName $testInvalidCollectionName } ` - | Should throw - } - } - - Context 'Running on test on Windows Server 2012 (R2)' { - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'6.3.9600.0' - } - - It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should ignore the EnableUserProfile property (Test returns True - In Desired State)' { - Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $True - } - } - - Context 'Running on test on Windows Server 2016 (or higher)' { - Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { - [version]'10.0.14393.0' - } - - It 'Running on Windows Server 2016+ with EnableUserProfile disk set to True and current setting set to false should return Test result False - Not In Desired State' { - Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $false - } - - It 'Running on Windows Server 2016+ with EnableUserProfile disk set to False and current setting set to false should return Test result True - In Desired State' { - Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false | Should be $True - } - - It 'Running on Windows Server 2016+ with CustomRdpProperties specified and existing setting matching with a trailing newline should return Test result True - In Desired State' { - Test-TargetResource -CollectionName $collectionName -CustomRdpProperty 'use redirection server name:i:1' | - Should be $true - } - - It 'Running on Windows Server 2016+ with out-of-order ExcludeFolderPath values and current EnableUserProfile setting set to true should return Test result True - In Desired State' { - Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { - [pscustomobject]@{ - CollectionName = 'TestCollection' - EnableUserProfileDisk = $true - ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') - } - } -ParameterFilter { $UserProfileDisk -eq $true } - - $testTargetSplat = @{ - CollectionName = $collectionName - EnableUserProfileDisk = $true - ExcludeFolderPath = @('c:\temp\bar', 'c:\temp\foo') - } - Test-TargetResource @testTargetSplat | Should be $true - } - } - } - #endregion - } -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 new file mode 100644 index 0000000..3882630 --- /dev/null +++ b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 @@ -0,0 +1,405 @@ +# $script:DSCModuleName = 'xRemoteDesktopSessionHost' +# $script:DSCResourceName = 'MSFT_xRDSessionDeployment' + +# function Invoke-TestSetup +# { +# try +# { +# Import-Module -Name DscResource.Test -Force +# } +# catch [System.IO.FileNotFoundException] +# { +# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# } + +# $script:testEnvironment = Initialize-TestEnvironment ` +# -DSCModuleName $script:dscModuleName ` +# -DscResourceName $script:dscResourceName ` +# -ResourceType 'Mof' ` +# -TestType 'Unit' +# } + +# function Invoke-TestCleanup +# { +# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# } + +# Invoke-TestSetup + +# try +# { +# InModuleScope $script:dscResourceName { +# $script:DSCResourceName = 'MSFT_xRDSessionDeployment' + +# Import-Module RemoteDesktop -Force + +# $sessionDeploymentSplat = @{ +# SessionHost = 'sessionhost.lan' +# ConnectionBroker = 'connectionbroker.lan' +# WebAccessServer = 'webaccess.lan' +# } + +# $sessionDeploymentMultiSplat = @{ +# SessionHost = 'sessionhost1.lan', 'sessionhost2.lan' +# ConnectionBroker = 'connectionbroker.lan' +# WebAccessServer = 'webaccess1.lan', 'webaccess2.lan' +# } + +# #region Function Get-TargetResource +# Describe "$($script:DSCResourceName)\Get-TargetResource" { + +# [array]$commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters +# $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters + +# $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | Where-Object { $_ -notin $commonParameters } | ForEach-Object -Process { +# @{ +# Property = $_ +# Value = $sessionDeploymentSplat[$_] +# } +# } + +# Context 'RDSessionDeployment is not present' { +# Mock -CommandName Get-Service -ParameterFilter { $Name -eq 'RDMS' } -MockWith { +# Write-Error 'MOCK Get-Service with parameter RDMS' +# } + +# Mock -CommandName Get-RDServer + +# It 'Should attempt to GET the RDMS service but fail given the RDMS service is not present' { +# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue +# Assert-MockCalled -CommandName Get-Service -Times 1 +# } + +# It 'Should attempt to START the RDMS service but fail given the RDMS service is not present' { +# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue +# $serviceWarning | Should BeLike "Failed to start RDMS service. Error: 'Cannot find any service with service name 'RDMS'.'." +# } + +# It 'Should return $null on property in Get-TargetResource ' { +# param +# ( +# $Property, +# $Value +# ) +# $get = Get-TargetResource @sessionDeploymentSplat +# $get.$Property | Should Be $null +# } -TestCases $allParameters +# } + +# Context 'RDSessionDeployment is present' { +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.SessionHost +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.WebAccessServer +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } + +# Mock -CommandName Start-Service +# Mock -CommandName Get-Service -MockWith { +# [pscustomobject]@{ +# Status = 'Stopped' +# } +# } + +# It 'Should attempt to start the RDMS service, given the RDMS service is stopped' { +# Get-TargetResource @sessionDeploymentSplat +# Assert-MockCalled -CommandName Start-Service -Times 1 -Scope It +# } + +# Mock -CommandName Start-Service -MockWith { +# throw 'Throwing from Start-Service mock' +# } + +# It 'Should generate a warning, given RDMS service is stopped and start fails' { +# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue +# $serviceWarning | Should Be "Failed to start RDMS service. Error: 'Throwing from Start-Service mock'." +# } + +# Mock -CommandName Get-Service -MockWith { +# [pscustomobject]@{ +# Status = 'Running' +# } +# } + +# It 'Should not attempt to start the RDMS service, given the RDMS service is running' { +# Get-TargetResource @sessionDeploymentSplat +# Assert-MockCalled -CommandName Start-Service -Times 0 -Scope It +# } + +# It 'Should return property with value in Get-TargetResource ' { +# param +# ( +# $Property, +# $Value +# ) +# $get = Get-TargetResource @sessionDeploymentSplat +# $get.$Property | Should Be $Value +# } -TestCases $allParameters + +# It 'Should connect to the right connection broker' { +# Assert-MockCalled -CommandName Get-RDServer -Scope Context -ParameterFilter { +# $ConnectionBroker -eq $sessionDeploymentSplat['ConnectionBroker'] +# } +# } +# } +# } +# #endregion + +# #region Function Set-TargetResource +# Describe "$($script:DSCResourceName)\Set-TargetResource" { + +# Mock -CommandName New-RDSessionDeployment +# Mock -CommandName Add-RDServer +# Mock -CommandName Get-TargetResource + +# It 'should call New-RDSessionDeployment with all required parameters' { +# Set-TargetResource @sessionDeploymentSplat +# Assert-MockCalled -CommandName New-RDSessionDeployment -Times 1 -ParameterFilter { +# $SessionHost -eq $sessionDeploymentSplat.SessionHost -and +# $ConnectionBroker -eq $sessionDeploymentSplat.ConnectionBroker -and +# $WebAccessServer -eq $sessionDeploymentSplat.WebAccessServer +# } +# Assert-MockCalled -CommandName Add-RDServer -Times 0 +# } + +# Mock -CommandName Get-TargetResource -MockWith { +# [pscustomobject]@{ +# SessionHost = 'OtherSessionHost.Lan' +# ConnectionBroker = $sessionDeploymentSplat.ConnectionBroker +# WebAccessServer = $sessionDeploymentSplat.WebAccessServer +# } +# } + +# It 'should call Add-RDServer with additional servers' { +# Set-TargetResource @sessionDeploymentMultiSplat +# Assert-MockCalled -CommandName Add-RDServer -Times 3 +# } +# } +# #endregion + +# #region Function Test-TargetResource +# Describe "$($script:DSCResourceName)\Test-TargetResource" { + +# Mock -CommandName Get-Service -MockWith { +# [pscustomobject]@{ +# Status = 'Running' +# } +# } +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.SessionHost +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = 'connectionbrokernew.lan' +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.WebAccessServer +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } + +# It 'Should return false, given the ConnectionBroker is not targeted in this deployment' { +# Test-TargetResource @sessionDeploymentSplat | Should Be $false +# } + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.SessionHost +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# [pscustomobject]@{ +# Server = 'webaccessnew.lan' +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } + +# It 'Should return false, given the WebAccessServer is not targeted in this deployment' { +# Test-TargetResource @sessionDeploymentSplat | Should Be $false +# } + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = 'sessionhost1.lan' +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = 'sessionhost2.lan' +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = 'sessionhost3.lan' +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentMultiSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) +# { +# [pscustomobject]@{ +# Server = $waserver +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } +# } + +# It 'Should return false, given the list of Session Hosts is different in this deployment' { +# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false +# } + +# Mock -CommandName Get-RDServer -MockWith { +# [pscustomobject]@{ +# Server = $sessionDeploymentMultiSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) +# { +# [pscustomobject]@{ +# Server = $waserver +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } +# } + +# It 'Should return false, given the list of Session Hosts is empty in this deployment' { +# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false +# } + +# Mock -CommandName Get-RDServer -MockWith { +# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) +# { +# [pscustomobject]@{ +# Server = $shserver +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentMultiSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# } + +# It 'Should return false, given the list of Web Hosts is empty in this deployment' { +# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false +# } + +# Mock -CommandName Get-RDServer -MockWith { +# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) +# { +# [pscustomobject]@{ +# Server = $shserver +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) +# { +# [pscustomobject]@{ +# Server = $waserver +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } +# } + +# It 'Should return true, given the SessionDeployment is completed' { +# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $true +# } + +# Mock -CommandName Get-RDServer -MockWith { +# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) +# { +# [pscustomobject]@{ +# Server = $shserver +# Roles = @( +# 'RDS-RD-SERVER' +# ) +# } +# } +# [pscustomobject]@{ +# Server = $sessionDeploymentMultiSplat.ConnectionBroker +# Roles = @( +# 'RDS-CONNECTION-BROKER' +# ) +# } +# [pscustomobject]@{ +# Server = 'SomeWebAccessServer.lan' +# Roles = @( +# 'RDS-WEB-ACCESS' +# ) +# } +# } + +# It 'Should return false if Web Access Server list is different' { +# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false +# } +# } +# #endregion +# } +# } +# catch +# { +# throw $_ +# } +# finally +# { +# Invoke-TestCleanup +# } diff --git a/tests/Unit/MSFT_xRDSessionDeployment.tests.ps1 b/tests/Unit/MSFT_xRDSessionDeployment.tests.ps1 deleted file mode 100644 index 12d50c9..0000000 --- a/tests/Unit/MSFT_xRDSessionDeployment.tests.ps1 +++ /dev/null @@ -1,405 +0,0 @@ -$script:DSCModuleName = 'xRemoteDesktopSessionHost' -$script:DSCResourceName = 'MSFT_xRDSessionDeployment' - -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } - - $script:testEnvironment = Initialize-TestEnvironment ` - -DSCModuleName $script:dscModuleName ` - -DscResourceName $script:dscResourceName ` - -ResourceType 'Mof' ` - -TestType 'Unit' -} - -function Invoke-TestCleanup -{ - Restore-TestEnvironment -TestEnvironment $script:testEnvironment -} - -Invoke-TestSetup - -try -{ - InModuleScope $script:dscResourceName { - $script:DSCResourceName = 'MSFT_xRDSessionDeployment' - - Import-Module RemoteDesktop -Force - - $sessionDeploymentSplat = @{ - SessionHost = 'sessionhost.lan' - ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess.lan' - } - - $sessionDeploymentMultiSplat = @{ - SessionHost = 'sessionhost1.lan', 'sessionhost2.lan' - ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess1.lan', 'webaccess2.lan' - } - - #region Function Get-TargetResource - Describe "$($script:DSCResourceName)\Get-TargetResource" { - - [array]$commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters - $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters - - $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | Where-Object { $_ -notin $commonParameters } | ForEach-Object -Process { - @{ - Property = $_ - Value = $sessionDeploymentSplat[$_] - } - } - - Context 'RDSessionDeployment is not present' { - Mock -CommandName Get-Service -ParameterFilter { $Name -eq 'RDMS' } -MockWith { - Write-Error 'MOCK Get-Service with parameter RDMS' - } - - Mock -CommandName Get-RDServer - - It 'Should attempt to GET the RDMS service but fail given the RDMS service is not present' { - Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue - Assert-MockCalled -CommandName Get-Service -Times 1 - } - - It 'Should attempt to START the RDMS service but fail given the RDMS service is not present' { - Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue - $serviceWarning | Should BeLike "Failed to start RDMS service. Error: 'Cannot find any service with service name 'RDMS'.'." - } - - It 'Should return $null on property in Get-TargetResource ' { - param - ( - $Property, - $Value - ) - $get = Get-TargetResource @sessionDeploymentSplat - $get.$Property | Should Be $null - } -TestCases $allParameters - } - - Context 'RDSessionDeployment is present' { - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = $sessionDeploymentSplat.SessionHost - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = $sessionDeploymentSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - [pscustomobject]@{ - Server = $sessionDeploymentSplat.WebAccessServer - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - - Mock -CommandName Start-Service - Mock -CommandName Get-Service -MockWith { - [pscustomobject]@{ - Status = 'Stopped' - } - } - - It 'Should attempt to start the RDMS service, given the RDMS service is stopped' { - Get-TargetResource @sessionDeploymentSplat - Assert-MockCalled -CommandName Start-Service -Times 1 -Scope It - } - - Mock -CommandName Start-Service -MockWith { - throw 'Throwing from Start-Service mock' - } - - It 'Should generate a warning, given RDMS service is stopped and start fails' { - Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue - $serviceWarning | Should Be "Failed to start RDMS service. Error: 'Throwing from Start-Service mock'." - } - - Mock -CommandName Get-Service -MockWith { - [pscustomobject]@{ - Status = 'Running' - } - } - - It 'Should not attempt to start the RDMS service, given the RDMS service is running' { - Get-TargetResource @sessionDeploymentSplat - Assert-MockCalled -CommandName Start-Service -Times 0 -Scope It - } - - It 'Should return property with value in Get-TargetResource ' { - param - ( - $Property, - $Value - ) - $get = Get-TargetResource @sessionDeploymentSplat - $get.$Property | Should Be $Value - } -TestCases $allParameters - - It 'Should connect to the right connection broker' { - Assert-MockCalled -CommandName Get-RDServer -Scope Context -ParameterFilter { - $ConnectionBroker -eq $sessionDeploymentSplat['ConnectionBroker'] - } - } - } - } - #endregion - - #region Function Set-TargetResource - Describe "$($script:DSCResourceName)\Set-TargetResource" { - - Mock -CommandName New-RDSessionDeployment - Mock -CommandName Add-RDServer - Mock -CommandName Get-TargetResource - - It 'should call New-RDSessionDeployment with all required parameters' { - Set-TargetResource @sessionDeploymentSplat - Assert-MockCalled -CommandName New-RDSessionDeployment -Times 1 -ParameterFilter { - $SessionHost -eq $sessionDeploymentSplat.SessionHost -and - $ConnectionBroker -eq $sessionDeploymentSplat.ConnectionBroker -and - $WebAccessServer -eq $sessionDeploymentSplat.WebAccessServer - } - Assert-MockCalled -CommandName Add-RDServer -Times 0 - } - - Mock -CommandName Get-TargetResource -MockWith { - [pscustomobject]@{ - SessionHost = 'OtherSessionHost.Lan' - ConnectionBroker = $sessionDeploymentSplat.ConnectionBroker - WebAccessServer = $sessionDeploymentSplat.WebAccessServer - } - } - - It 'should call Add-RDServer with additional servers' { - Set-TargetResource @sessionDeploymentMultiSplat - Assert-MockCalled -CommandName Add-RDServer -Times 3 - } - } - #endregion - - #region Function Test-TargetResource - Describe "$($script:DSCResourceName)\Test-TargetResource" { - - Mock -CommandName Get-Service -MockWith { - [pscustomobject]@{ - Status = 'Running' - } - } - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = $sessionDeploymentSplat.SessionHost - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = 'connectionbrokernew.lan' - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - [pscustomobject]@{ - Server = $sessionDeploymentSplat.WebAccessServer - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - - It 'Should return false, given the ConnectionBroker is not targeted in this deployment' { - Test-TargetResource @sessionDeploymentSplat | Should Be $false - } - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = $sessionDeploymentSplat.SessionHost - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = $sessionDeploymentSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - [pscustomobject]@{ - Server = 'webaccessnew.lan' - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - - It 'Should return false, given the WebAccessServer is not targeted in this deployment' { - Test-TargetResource @sessionDeploymentSplat | Should Be $false - } - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = 'sessionhost1.lan' - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = 'sessionhost2.lan' - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = 'sessionhost3.lan' - Roles = @( - 'RDS-RD-SERVER' - ) - } - [pscustomobject]@{ - Server = $sessionDeploymentMultiSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) - { - [pscustomobject]@{ - Server = $waserver - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - } - - It 'Should return false, given the list of Session Hosts is different in this deployment' { - Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false - } - - Mock -CommandName Get-RDServer -MockWith { - [pscustomobject]@{ - Server = $sessionDeploymentMultiSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) - { - [pscustomobject]@{ - Server = $waserver - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - } - - It 'Should return false, given the list of Session Hosts is empty in this deployment' { - Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false - } - - Mock -CommandName Get-RDServer -MockWith { - foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) - { - [pscustomobject]@{ - Server = $shserver - Roles = @( - 'RDS-RD-SERVER' - ) - } - } - [pscustomobject]@{ - Server = $sessionDeploymentMultiSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - } - - It 'Should return false, given the list of Web Hosts is empty in this deployment' { - Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false - } - - Mock -CommandName Get-RDServer -MockWith { - foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) - { - [pscustomobject]@{ - Server = $shserver - Roles = @( - 'RDS-RD-SERVER' - ) - } - } - [pscustomobject]@{ - Server = $sessionDeploymentSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) - { - [pscustomobject]@{ - Server = $waserver - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - } - - It 'Should return true, given the SessionDeployment is completed' { - Test-TargetResource @sessionDeploymentMultiSplat | Should Be $true - } - - Mock -CommandName Get-RDServer -MockWith { - foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) - { - [pscustomobject]@{ - Server = $shserver - Roles = @( - 'RDS-RD-SERVER' - ) - } - } - [pscustomobject]@{ - Server = $sessionDeploymentMultiSplat.ConnectionBroker - Roles = @( - 'RDS-CONNECTION-BROKER' - ) - } - [pscustomobject]@{ - Server = 'SomeWebAccessServer.lan' - Roles = @( - 'RDS-WEB-ACCESS' - ) - } - } - - It 'Should return false if Web Access Server list is different' { - Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false - } - } - #endregion - } -} -catch -{ - throw $_ -} -finally -{ - Invoke-TestCleanup -} diff --git a/tests/Unit/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.Tests.ps1 b/tests/Unit/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.Tests.ps1 new file mode 100644 index 0000000..04df76d --- /dev/null +++ b/tests/Unit/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.Tests.ps1 @@ -0,0 +1,91 @@ +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:subModuleName = 'xRemoteDesktopSessionHost.Common' + + $script:parentModule = Get-Module -Name $script:dscModuleName -ListAvailable | Select-Object -First 1 + $script:subModulesFolder = Join-Path -Path $script:parentModule.ModuleBase -ChildPath 'Modules' + + $script:subModulePath = Join-Path -Path $script:subModulesFolder -ChildPath $script:subModuleName + + Import-Module -Name $script:subModulePath -Force -ErrorAction 'Stop' + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:subModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:subModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:subModuleName +} + +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:subModuleName -All | Remove-Module -Force +} + +Describe 'Test-xRemoteDesktopSessionHostOsRequirement' { + Context 'Windows 10' { + BeforeAll { + Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 10, 1, 1, 1) } + } + + It 'Should return true' { + Test-xRemoteDesktopSessionHostOsRequirement | Should -BeTrue + } + } + + Context 'Windows 8.1' { + BeforeAll { + Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 3, 1, 1) } + } + + It 'Should return true' { + Test-xRemoteDesktopSessionHostOsRequirement | Should -BeTrue + } + } + + Context 'Windows 8' { + BeforeAll { + Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 2, 9200, 0) } + } + + It 'Should return true' { + Test-xRemoteDesktopSessionHostOsRequirement | Should -BeTrue + } + } + + Context 'Windows 7' { + BeforeAll { + Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 1, 1, 0) } + } + + It 'Should return false' { + Test-xRemoteDesktopSessionHostOsRequirement | Should -BeFalse + } + } +} diff --git a/tests/Unit/xRemoteDesktopSessionHost.tests.ps1 b/tests/Unit/xRemoteDesktopSessionHost.tests.ps1 deleted file mode 100644 index e650172..0000000 --- a/tests/Unit/xRemoteDesktopSessionHost.tests.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -function Invoke-TestSetup -{ - try - { - Import-Module -Name DscResource.Test -Force - } - catch [System.IO.FileNotFoundException] - { - throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' - } -} - -function Invoke-TestCleanup -{ - -} - -Invoke-TestSetup - -try -{ - InModuleScope xRemoteDesktopSessionHostCommon { - - #region Function Test-xRemoteDesktopSessionHostOsRequirement - Describe 'Test-xRemoteDesktopSessionHostOsRequirement' { - Context 'Windows 10' { - Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 10, 1, 1, 1) } - It 'Should return true' { - Test-xRemoteDesktopSessionHostOsRequirement | Should be $true - } - } - Context 'Windows 8.1' { - Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 3, 1, 1) } - It 'Should return true' { - Test-xRemoteDesktopSessionHostOsRequirement | Should be $true - } - } - Context 'Windows 8' { - Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 2, 9200, 0) } - It 'Should return true' { - Test-xRemoteDesktopSessionHostOsRequirement | Should be $true - } - } - Context 'Windows 7' { - Mock Get-xRemoteDesktopSessionHostOsVersion -MockWith { return (New-Object 'Version' 6, 1, 1, 0) } - It 'Should return false' { - Test-xRemoteDesktopSessionHostOsRequirement | Should be $false - } - } - } - #endregion - - } -} -finally -{ - Invoke-TestCleanup -} From 4daf3a4bfa9c5fd6d4c617cd93bac66a901770f8 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:21:20 +0100 Subject: [PATCH 02/44] Migrate RDCertificateConfiguration --- .../MSFT_xRDCertificateConfiguration.psm1 | 6 +- ...MSFT_xRDCertificateConfiguration.Tests.ps1 | 406 ++++++++---------- 2 files changed, 192 insertions(+), 220 deletions(-) diff --git a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 index 17473cd..d0a50f0 100644 --- a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -# Assert-Module -ModuleName 'RemoteDesktop' - $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' ####################################################################### @@ -41,6 +39,8 @@ function Get-TargetResource $Credential ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose -Message ( $script:localizedData.VerboseGetCertificate -f $Role, $ConnectionBroker ) @@ -77,6 +77,8 @@ function Set-TargetResource $Credential ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + $rdCertificateSplat = @{ Role = $Role ConnectionBroker = $ConnectionBroker diff --git a/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 index 5bc9f39..2b6f04b 100644 --- a/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 +++ b/tests/Unit/MSFT_xRDCertificateConfiguration.Tests.ps1 @@ -69,7 +69,7 @@ Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' Role = 'RDRedirector' ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDRedirector.pfx' + ImportPath = 'TestDrive:\RDRedirector.pfx' Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) } } @@ -82,7 +82,7 @@ Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { $testParams = @{ Role = 'RDRedirector' ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDRedirector.pfx' + ImportPath = 'TestDrive:\RDRedirector.pfx' Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) } @@ -93,6 +93,9 @@ Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { $result.ImportPath | Should -Be $testParams.ImportPath $result.Credential.UserName | Should -Be $testParams.Credential.UserName } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDCertificate -Exactly -Times 1 -Scope It } } @@ -108,7 +111,7 @@ Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { $testParams = @{ Role = 'RDRedirector' ConnectionBroker = 'connectionbroker.lan' - ImportPath = 'testdrive:\RDRedirector.pfx' + ImportPath = 'TestDrive:\RDRedirector.pfx' Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) } @@ -122,222 +125,189 @@ Describe 'MSFT_xRDCertificateConfiguration\Get-TargetResource' -Tag 'Get' { $result.ImportPath | Should -BeNullOrEmpty $result.Credential.UserName | Should -BeNullOrEmpty } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDCertificate -Exactly -Times 1 -Scope It } } } -# Describe 'Testing MSFT_xRDCertificateConfiguration' { - -# Mock -CommandName Set-RDCertificate - -# Context 'When a certificate is not configured' { - -# Mock -CommandName Get-RDCertificate -MockWith { -# [pscustomobject]@{ -# Thumbprint = $null -# Role = 'RDPublishing' -# } -# } -ParameterFilter { $Role -eq 'RDPublishing' } - -# Mock -CommandName Get-PfxData -MockWith { -# [pscustomobject]@{ -# EndEntityCertificates = [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' -# } -# } -# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDPublishing.pfx' } - -# $resourceNotConfiguredSplat = @{ -# Role = 'RDPublishing' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDPublishing.pfx' -# Credential = [pscredential]::new( -# 'Test', -# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) -# ) -# } - -# It 'Get-TargetResource returns no thumbprint' { -# (Get-TargetResource @resourceNotConfiguredSplat).Thumbprint | Should -BeNullOrEmpty -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse -# } - -# It 'Set-TargetResource runs Set-RDCertificate' { -# Set-TargetResource @resourceNotConfiguredSplat -# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { -# $Role -eq $resourceNotConfiguredSplat.Role -and -# $ConnectionBroker -eq $resourceNotConfiguredSplat.ConnectionBroker -and -# $ImportPath -eq $resourceNotConfiguredSplat.ImportPath -and -# $Password -eq $resourceNotConfiguredSplat.Credential.Password -and -# $Force -eq $true -# } -# } -# } - -# Context 'When the proper certificate is configured' { - -# Mock -CommandName Get-RDCertificate -MockWith { -# [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' -# Role = 'RDRedirector' -# } -# } -ParameterFilter { $Role -eq 'RDRedirector' } - -# Mock -CommandName Get-PfxData -MockWith { -# [pscustomobject]@{ -# EndEntityCertificates = [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' -# } -# } -# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDRedirector.pfx' } - -# $resourceConfiguredSplat = @{ -# Role = 'RDRedirector' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDRedirector.pfx' -# Credential = [pscredential]::new( -# 'Test', -# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) -# ) -# } - -# It 'Get-TargetResource returns the correct thumbprint' { -# (Get-TargetResource @resourceConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' -# } - -# It 'Test-TargetResource returns true' { -# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue -# } -# } - -# Context 'When a wrong certificate is configured' { - -# Mock -CommandName Get-RDCertificate -MockWith { -# [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' -# Role = 'RDGateway' -# } -# } -ParameterFilter { $Role -eq 'RDGateway' } - -# Mock -CommandName Get-PfxData -MockWith { -# [pscustomobject]@{ -# EndEntityCertificates = [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' -# } -# } -# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } - -# $resourceWrongConfiguredSplat = @{ -# Role = 'RDGateway' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDGateway.pfx' -# Credential = [pscredential]::new( -# 'Test', -# (ConvertTo-SecureString -AsPlainText -String 'pester' -Force) -# ) -# } - -# It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { -# (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse -# } - -# It 'Set-TargetResource runs Set-RDCertificate' { -# Set-TargetResource @resourceWrongConfiguredSplat -# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { -# $Role -eq $resourceWrongConfiguredSplat.Role -and -# $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and -# $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and -# $Password -eq $resourceWrongConfiguredSplat.Credential.Password -and -# $Force -eq $true -# } -# } -# } - -# Context 'When a wrong certificate is configured and the PFX file is protected based on group membership (ProtectTo)' { -# Mock -CommandName Get-RDCertificate -MockWith { -# [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' -# Role = 'RDGateway' -# } -# } -ParameterFilter { $Role -eq 'RDGateway' } - -# Mock -CommandName Get-PfxData -MockWith { -# [pscustomobject]@{ -# EndEntityCertificates = [pscustomobject]@{ -# Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8B' -# } -# } -# } -ParameterFilter { $ImportPath -eq 'testdrive:\RDGateway.pfx' } - -# $resourceWrongConfiguredSplat = @{ -# Role = 'RDGateway' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDGateway.pfx' -# } - -# It 'Get-TargetResource returns the thumbprint of the currently configured certificate' { -# (Get-TargetResource @resourceWrongConfiguredSplat).Thumbprint | Should -Be '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse -# } - -# It 'Set-TargetResource runs Set-RDCertificate without password' { -# Set-TargetResource @resourceWrongConfiguredSplat -# Assert-MockCalled -CommandName Set-RDCertificate -Times 1 -Exactly -ParameterFilter { -# $Role -eq $resourceWrongConfiguredSplat.Role -and -# $ConnectionBroker -eq $resourceWrongConfiguredSplat.ConnectionBroker -and -# $ImportPath -eq $resourceWrongConfiguredSplat.ImportPath -and -# $Force -eq $true -# } -# } -# } - -# Context 'When a certificate fails to test' { -# Mock Get-RDCertificate -# Mock Get-PfxData -MockWith { -# throw 'Cannot import PFX file' -# } - -# $resourceWrongConfiguredSplat = @{ -# Role = 'RDGateway' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDGateway.pfx' -# } - -# It 'Test-TargetResource displays a warning when a certificate fails to test' { -# $message = Test-TargetResource @resourceWrongConfiguredSplat 3>&1 -# $message | Should -Not -BeNullOrEmpty -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceWrongConfiguredSplat | Should -BeFalse -# } -# } - -# Context 'When a certificate fails to set' { - -# Mock Set-RDCertificate -MockWith { -# throw 'Failed to apply certificate' -# } - -# $resourceWrongConfiguredSplat = @{ -# Role = 'RDGateway' -# ConnectionBroker = 'connectionbroker.lan' -# ImportPath = 'testdrive:\RDGateway.pfx' -# } - -# It 'Set-TargetResource returns an error when the certificate could not be applied' { -# { Set-TargetResource @resourceWrongConfiguredSplat -ErrorAction Stop } | -# Should -Throw 'Failed to apply certificate' -# } -# } -# } +Describe 'MSFT_xRDCertificateConfiguration\Test-TargetResource' -Tag 'Test' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the system is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + } + + Mock -CommandName Get-PfxData -MockWith { + @{ + EndEntityCertificates = @( + @{ + Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' + } + ) + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-PfxData -Exactly -Times 1 -Scope It + } + } + + Context 'When the system is not in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + } + + Mock -CommandName Get-PfxData -MockWith { + @{ + EndEntityCertificates = @( + @{ + Thumbprint = '00006BBC44A3AB668A3B02CE0B258FEAEC1AFA8A' + } + ) + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-PfxData -Exactly -Times 1 -Scope It + } + } + + Context 'When the pfx does not exist or cannot be opened' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + Thumbprint = '53086BBC44A3AB668A3B02CE0B258FEAEC1AFA8C' + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + } + + Mock -CommandName Get-PfxData -MockWith { throw } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-PfxData -Exactly -Times 1 -Scope It + } + } +} + +Describe 'MSFT_xRDCertificateConfiguration\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When setting the resource' { + BeforeAll { + Mock -CommandName Set-RDCertificate + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDCertificate -Exactly -Times 1 -Scope It + } + } + + Context 'When setting the resource throws an error' { + BeforeAll { + Mock -CommandName Set-RDCertificate -MockWith { throw } + Mock -CommandName Write-Error + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Role = 'RDRedirector' + ConnectionBroker = 'connectionbroker.lan' + ImportPath = 'TestDrive:\RDRedirector.pfx' + Credential = [pscredential]::new('Test', (ConvertTo-SecureString -AsPlainText -String 'pester' -Force)) + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDCertificate -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Write-Error -Exactly -Times 1 -Scope It + } + } +} From 3427bee05e4b05f28b0a27c439526aa02a3bbc60 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:25:11 +0100 Subject: [PATCH 03/44] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da88e17..53db38e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed formatting in all source files, [issue #113](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/113). - Update repo files to latest versions in Sampler. Fixes [issue #118](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/118). - Enable DocGenerator and move docs to wiki. Fixes [issue #101](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/101). +- Migrated tests to Pester 5. Fixes [issue #119](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/119). ## [2.1.0] - 2022-08-07 From 92100324048c253bd1e8904d73491fbbbb46a0a7 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:29:02 +0100 Subject: [PATCH 04/44] Enable ModuleFast --- Resolve-Dependency.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 index 9381d40..3086909 100644 --- a/Resolve-Dependency.psd1 +++ b/Resolve-Dependency.psd1 @@ -3,7 +3,7 @@ AllowPrerelease = $false WithYAML = $true - #UseModuleFast = $true + UseModuleFast = $true #ModuleFastVersion = '0.1.2' #ModuleFastBleedingEdge = $true From bf748e723744af11248515ad55f4be4b9fe84329 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 15:29:52 +0100 Subject: [PATCH 05/44] Formatting --- Resolve-Dependency.psd1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resolve-Dependency.psd1 b/Resolve-Dependency.psd1 index 3086909..27a91e3 100644 --- a/Resolve-Dependency.psd1 +++ b/Resolve-Dependency.psd1 @@ -3,7 +3,7 @@ AllowPrerelease = $false WithYAML = $true - UseModuleFast = $true + UseModuleFast = $true #ModuleFastVersion = '0.1.2' #ModuleFastBleedingEdge = $true From e445856fd318c4359c7c9e80d1878e4543b78652 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 15 Oct 2025 16:20:12 +0100 Subject: [PATCH 06/44] Migrate MSFT_xRDConnectionBrokerHAMode --- .../MSFT_xRDConnectionBrokerHAMode.psm1 | 51 ++- .../MSFT_xRDConnectionBrokerHAMode.Tests.ps1 | 420 ++++++++++++++---- 2 files changed, 357 insertions(+), 114 deletions(-) diff --git a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 index f06a942..c99fd91 100644 --- a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 +++ b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 @@ -9,7 +9,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' $localhost = [System.Net.Dns]::GetHostByName((hostname)).HostName @@ -23,22 +23,29 @@ function Get-TargetResource param ( [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter(Mandatory = $true)] - [string] $DatabaseConnectionString, + [System.String] + $DatabaseConnectionString, [Parameter()] - [string] $DatabaseSecondaryConnectionString, + [System.String] + $DatabaseSecondaryConnectionString, [Parameter()] - [string] $DatabaseFilePath, + [System.String] + $DatabaseFilePath, [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $ClientAccessName + [System.String] + $ClientAccessName ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose -Message ($script:localizedData.VerboseGetHAMode -f $ConnectionBroker, $ClientAccessName) if ([string]::IsNullOrWhiteSpace($ConnectionBroker)) @@ -67,22 +74,29 @@ function Set-TargetResource param ( [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter(Mandatory = $true)] - [string] $DatabaseConnectionString, + [System.String] + $DatabaseConnectionString, [Parameter()] - [string] $DatabaseSecondaryConnectionString, + [System.String] + $DatabaseSecondaryConnectionString, [Parameter()] - [string] $DatabaseFilePath, + [System.String] + $DatabaseFilePath, [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $ClientAccessName + [System.String] + $ClientAccessName ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose -Message ($script:localizedData.VerboseConfigureHAMode -f $ConnectionBroker, $ClientAccessName) if ([string]::IsNullOrWhiteSpace($ConnectionBroker)) @@ -119,20 +133,25 @@ function Test-TargetResource param ( [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter(Mandatory = $true)] - [string] $DatabaseConnectionString, + [System.String] + $DatabaseConnectionString, [Parameter()] - [string] $DatabaseSecondaryConnectionString, + [System.String] + $DatabaseSecondaryConnectionString, [Parameter()] - [string] $DatabaseFilePath, + [System.String] + $DatabaseFilePath, [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $ClientAccessName + [System.String] + $ClientAccessName ) Write-Verbose ($script:localizedData.VerboseTestHAMode -f $ConnectionBroker, $ClientAccessName) diff --git a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 index 4893890..a2f1ca1 100644 --- a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 +++ b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 @@ -1,109 +1,333 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () -# #region HEADER +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' 3>&1 4>&1 5>&1 6>&1 > $null + } -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } + # 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.' + } +} -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } +BeforeAll { + $script:dscModuleName = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDConnectionBrokerHAMode' -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +# Describe 'Testing MSFT_xRDConnectionBrokerHAMode' { +# Mock -CommandName Import-Module -MockWith {} -ParameterFilter { $Name -eq 'RemoteDesktop' } + +# Mock -CommandName Set-RDConnectionBrokerHighAvailability -ParameterFilter { $ClientAccessName -eq 'rdsfarm.contoso.com' } + +# Context 'Connection Broker not in HA mode' { +# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'RDCB1' +# ActiveManagementServer = '' +# ClientAccessName = '' +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# } +# } + +# $resourceNotConfiguredSplat = @{ +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1.contoso.com;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# ClientAccessName = 'rdsfarm.contoso.com' +# DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' +# } + +# It 'Get-TargetResource returns no active management server' { +# (Get-TargetResource @resourceNotConfiguredSplat).ActiveManagementServer | Should -BeNullOrEmpty +# } + +# It 'Test-TargetResource returns false' { +# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse +# } -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDConnectionBrokerHAMode' - -# #region Function Get-TargetResource -# Describe "Testing $($script:DSCResourceName)" { -# Mock -CommandName Import-Module -MockWith {} -ParameterFilter { $Name -eq 'RemoteDesktop' } - -# Mock -CommandName Set-RDConnectionBrokerHighAvailability -ParameterFilter { $ClientAccessName -eq 'rdsfarm.contoso.com' } - -# Context 'Connection Broker not in HA mode' { - -# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'RDCB1' -# ActiveManagementServer = '' -# ClientAccessName = '' -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# } -# } - -# $resourceNotConfiguredSplat = @{ -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1.contoso.com;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# ClientAccessName = 'rdsfarm.contoso.com' -# DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' -# } - -# It 'Get-TargetResource returns no active management server' { -# (Get-TargetResource @resourceNotConfiguredSplat).ActiveManagementServer | Should -BeNullOrEmpty -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse -# } - -# It 'Set-TargetResource runs Set-RDConnectionBrokerHighAvailability' { -# Set-TargetResource @resourceNotConfiguredSplat -# Assert-MockCalled -CommandName Set-RDConnectionBrokerHighAvailability -Times 1 -Exactly -ParameterFilter { -# $DatabaseConnectionString -eq 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -and -# $ClientAccessName -eq 'rdsfarm.contoso.com' -# } -# } +# It 'Set-TargetResource runs Set-RDConnectionBrokerHighAvailability' { +# Set-TargetResource @resourceNotConfiguredSplat +# Assert-MockCalled -CommandName Set-RDConnectionBrokerHighAvailability -Times 1 -Exactly -ParameterFilter { +# $DatabaseConnectionString -eq 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -and +# $ClientAccessName -eq 'rdsfarm.contoso.com' # } +# } +# } -# Context 'Connection Broker in HA mode' { - -# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'RDCB1' -# ActiveManagementServer = 'RDCB1' -# ClientAccessName = 'rdsfarm.contoso.com' -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# } -# } - -# $resourceConfiguredSplat = @{ -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# ClientAccessName = 'rdsfarm.contoso.com' -# } - -# It 'Get-TargetResource returns an active management server' { -# (Get-TargetResource @resourceConfiguredSplat).ActiveManagementServer | Should -Be 'RDCB1' -# } - -# It 'Test-TargetResource returns true' { -# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue -# } +# Context 'Connection Broker in HA mode' { +# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { +# [pscustomobject]@{ +# ConnectionBroker = 'RDCB1' +# ActiveManagementServer = 'RDCB1' +# ClientAccessName = 'rdsfarm.contoso.com' +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' # } # } + +# $resourceConfiguredSplat = @{ +# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' +# ClientAccessName = 'rdsfarm.contoso.com' +# } + +# It 'Get-TargetResource returns an active management server' { +# (Get-TargetResource @resourceConfiguredSplat).ActiveManagementServer | Should -Be 'RDCB1' +# } + +# It 'Test-TargetResource returns true' { +# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue +# } # } # } -# finally -# { -# #region FOOTER -# Invoke-TestCleanup -# #endregion -# } + +Describe 'MSFT_xRDConnectionBrokerHAMode\Get-TargetResource' -Tag 'Get' { + Context 'When the resource is in the desired state' { + Context 'When the connection broker is not local' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { + @{ + ConnectionBroker = 'RDCB1' + ActiveManagementServer = 'RDCB1' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'RDCB1' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.ActiveManagementServer | Should -Be 'RDCB1' + $result.ClientAccessName | Should -Be $testParams.ClientAccessName + $result.DatabaseConnectionString | Should -Be $testParams.DatabaseConnectionString + $result.DatabaseSecondaryConnectionString | Should -Be $testParams.DatabaseSecondaryConnectionString + $result.DatabaseFilePath | Should -Be $testParams.DatabaseFilePath + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDConnectionBrokerHighAvailability -Exactly -Times 1 -Scope It + } + } + + Context 'When the connection broker is local' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { + @{ + ConnectionBroker = Get-ComputerName + ActiveManagementServer = 'RDCB1' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be (Get-ComputerName) + $result.ActiveManagementServer | Should -Be 'RDCB1' + $result.ClientAccessName | Should -Be $testParams.ClientAccessName + $result.DatabaseConnectionString | Should -Be $testParams.DatabaseConnectionString + $result.DatabaseSecondaryConnectionString | Should -Be $testParams.DatabaseSecondaryConnectionString + $result.DatabaseFilePath | Should -Be $testParams.DatabaseFilePath + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDConnectionBrokerHighAvailability -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the resource is not in the desired state' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDConnectionBrokerHighAvailability + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'RDCB1' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.ActiveManagementServer | Should -BeNullOrEmpty + $result.ClientAccessName | Should -BeNullOrEmpty + $result.DatabaseConnectionString | Should -BeNullOrEmpty + $result.DatabaseSecondaryConnectionString | Should -Be $testParams.DatabaseSecondaryConnectionString + $result.DatabaseFilePath | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDConnectionBrokerHighAvailability -Exactly -Times 1 -Scope It + } + } +} + +Describe 'MSFT_xRDConnectionBrokerHAMode\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'RDCB1' + ActiveManagementServer = 'RDCB1' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + } + } + + It 'Should return true' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'RDCB1' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'RDCB1' + ActiveManagementServer = '' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + } + } + + It 'Should return false' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'RDCB1' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } +} + +Describe 'MSFT_xRDConnectionBrokerHAMode\Set-TargetResource' -Tag 'Set' { + Context 'When Set-RDConnectionBrokerHighAvailability runs successfully' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Set-RDConnectionBrokerHighAvailability + } + + It 'Should run Set-RDConnectionBrokerHighAvailability' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = '' + DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB2;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' + ClientAccessName = 'rdsfarm.contoso.com' + DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDConnectionBrokerHighAvailability -Exactly -Times 1 -Scope It + } + } +} From fa012a5ab95a8134f087bb2b6885ff672eea9c94 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:39:07 +0100 Subject: [PATCH 07/44] Add module import --- .../MSFT_xRDGatewayConfiguration.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 index f13deca..b3f13ef 100644 --- a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 @@ -9,7 +9,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' +Assert-Module -ModuleName 'RemoteDesktop' -ImportModule function ValidateCustomModeParameters { From f0fb4509ca5b956a204bfa3b1e0f90831fffa992 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:08:46 +0100 Subject: [PATCH 08/44] GetHostByName obsolete --- .../MSFT_xRDConnectionBrokerHAMode.psm1 | 2 +- source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 | 2 +- tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 index c99fd91..1e64fc7 100644 --- a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 +++ b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 @@ -11,7 +11,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' -$localhost = [System.Net.Dns]::GetHostByName((hostname)).HostName +$localhost = [System.Net.Dns]::GetHostEntry((hostname)).HostName ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index eae1838..a54337b 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -11,7 +11,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) Assert-Module -ModuleName 'RemoteDesktop' -$localhost = [System.Net.Dns]::GetHostByName((hostname)).HostName +$localhost = [System.Net.Dns]::GetHostEntry((hostname)).HostName function ValidateCustomModeParameters { diff --git a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 index 48fb08c..cf93c1f 100644 --- a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 @@ -52,7 +52,7 @@ # Mock -CommandName Get-RDSessionHost -MockWith { # [pscustomobject]@{ -# SessionHost = [System.Net.Dns]::GetHostByName((hostname)).HostName +# SessionHost = [System.Net.Dns]::GetHostEntry((hostname)).HostName # CollectionName = 'TestCollection' # } # } From 8562acda2f9049d162b3414e15bb41e08e7c350c Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:09:51 +0100 Subject: [PATCH 09/44] Use $env:ComputerName --- .../MSFT_xRDConnectionBrokerHAMode.psm1 | 2 +- source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 index 1e64fc7..e399177 100644 --- a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 +++ b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 @@ -11,7 +11,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' -$localhost = [System.Net.Dns]::GetHostEntry((hostname)).HostName +$localhost = [System.Net.Dns]::GetHostEntry(($env:COMPUTERNAME)).HostName ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index a54337b..217fa96 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -11,7 +11,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) Assert-Module -ModuleName 'RemoteDesktop' -$localhost = [System.Net.Dns]::GetHostEntry((hostname)).HostName +$localhost = [System.Net.Dns]::GetHostEntry(($env:COMPUTERNAME)).HostName function ValidateCustomModeParameters { From 24ff7f3953c5cbd7c2c3d375d37396fcfcd6d9f7 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:43:47 +0100 Subject: [PATCH 10/44] Fix HQRM --- build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.yaml b/build.yaml index 88a0a16..d203728 100644 --- a/build.yaml +++ b/build.yaml @@ -126,7 +126,7 @@ DscTest: - Modules/DscResource.Common # Must exclude built module file because it should not be tested like MOF-based resources - xRemoteDesktopSessionHost.psm1 - MainGitBranch: master + MainGitBranch: main ModuleBuildTasks: Sampler: From 7a7b2faa54f9c7499ee540a02249b257f3a0e967 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:48:23 +0100 Subject: [PATCH 11/44] Update formatting --- RequiredModules.psd1 | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 4468378..9c005a3 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -1,5 +1,5 @@ @{ - PSDependOptions = @{ + PSDependOptions = @{ AddToPath = $true Target = 'output\RequiredModules' Parameters = @{ @@ -7,26 +7,26 @@ } } - InvokeBuild = 'latest' - PSScriptAnalyzer = 'latest' - Pester = 'latest' - Plaster = 'latest' - ModuleBuilder = 'latest' - ChangelogManagement = 'latest' - Sampler = 'latest' - 'Sampler.GitHubTasks' = 'latest' - MarkdownLinkCheck = 'latest' - 'DscResource.Test' = 'latest' - xDscResourceDesigner = 'latest' + InvokeBuild = 'latest' + PSScriptAnalyzer = 'latest' + Pester = 'latest' + Plaster = 'latest' + ModuleBuilder = 'latest' + ChangelogManagement = 'latest' + Sampler = 'latest' + 'Sampler.GitHubTasks' = 'latest' + MarkdownLinkCheck = 'latest' + 'DscResource.Test' = 'latest' + xDscResourceDesigner = 'latest' # Build dependencies needed for using the module - 'DscResource.Common' = 'latest' + 'DscResource.Common' = 'latest' # Analyzer rules 'DscResource.AnalyzerRules' = 'latest' 'Indented.ScriptAnalyzerRules' = 'latest' # Prerequisite modules for documentation. - 'DscResource.DocGenerator' = 'latest' - PlatyPS = 'latest' + 'DscResource.DocGenerator' = 'latest' + PlatyPS = 'latest' } From 8aa275310af5d185606ac056b246809862653bfb Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 19 Oct 2025 13:31:32 +0100 Subject: [PATCH 12/44] Add Indented.ScriptAnalyzerRules --- .vscode/analyzersettings.psd1 | 14 ++++++++++---- RequiredModules.psd1 | 8 ++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.vscode/analyzersettings.psd1 b/.vscode/analyzersettings.psd1 index a6fc6b2..dc37985 100644 --- a/.vscode/analyzersettings.psd1 +++ b/.vscode/analyzersettings.psd1 @@ -1,5 +1,8 @@ @{ - CustomRulePath = '.\output\RequiredModules\DscResource.AnalyzerRules' + CustomRulePath = @( + './output/RequiredModules/DscResource.AnalyzerRules' + './output/RequiredModules/Indented.ScriptAnalyzerRules' + ) includeDefaultRules = $true IncludeRules = @( # DSC Community style guideline rules from the module ScriptAnalyzer. @@ -89,24 +92,27 @@ CheckPipeForRedundantWhitespace = $true CheckParameter = $false } - PSPlaceOpenBrace = @{ Enable = $true OnSameLine = $false NewLineAfter = $true IgnoreOneLineBlock = $false } - PSPlaceCloseBrace = @{ Enable = $true NoEmptyLineBefore = $true IgnoreOneLineBlock = $false NewLineAfter = $true } - PSAlignAssignmentStatement = @{ Enable = $true CheckHashtable = $true } + PSUseCorrectCasing = @{ + Enable = $true + CheckCommands = $true + CheckKeyword = $true + CheckOperator = $true + } } } diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index 4468378..d1ba1fb 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -1,5 +1,5 @@ @{ - PSDependOptions = @{ + PSDependOptions = @{ AddToPath = $true Target = 'output\RequiredModules' Parameters = @{ @@ -20,13 +20,13 @@ xDscResourceDesigner = 'latest' # Build dependencies needed for using the module - 'DscResource.Common' = 'latest' + 'DscResource.Common' = 'latest' # Analyzer rules 'DscResource.AnalyzerRules' = 'latest' 'Indented.ScriptAnalyzerRules' = 'latest' # Prerequisite modules for documentation. - 'DscResource.DocGenerator' = 'latest' - PlatyPS = 'latest' + 'DscResource.DocGenerator' = 'latest' + PlatyPS = 'latest' } From fc1a228cdfb41e0538f242e4c431e82f820b5a1d Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:28:53 +0100 Subject: [PATCH 13/44] Add service name --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3fc5381..bd88eff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,7 +36,8 @@ "keepachangelog", "notin", "pscmdlet", - "steppable" + "steppable", + "RDMS" ], "cSpell.ignorePaths": [ ".git" From c1357f21a87f33b968a10660c6ec39c59cdd1199 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Mon, 20 Oct 2025 17:29:07 +0100 Subject: [PATCH 14/44] Migrate RDSessionDeployment --- .../MSFT_xRDSessionDeployment.psm1 | 39 +- .../Unit/MSFT_xRDSessionDeployment.Tests.ps1 | 782 +++++++++--------- 2 files changed, 405 insertions(+), 416 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 index 3f72900..4521c4a 100644 --- a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 +++ b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -21,13 +19,20 @@ function Get-TargetResource param ( [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter(Mandatory = $true)] - [string[]] $WebAccessServer + [System.String[]] + $WebAccessServer ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose 'Getting list of RD Server roles.' # Start service RDMS is needed because otherwise a reboot loop could happen due to @@ -63,13 +68,20 @@ function Set-TargetResource param ( [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter(Mandatory = $true)] - [string[]] $WebAccessServer + [System.String[]] + $WebAccessServer ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + $currentStatus = Get-TargetResource @PSBoundParameters if ($null -eq $currentStatus) @@ -109,11 +121,16 @@ function Test-TargetResource param ( [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter(Mandatory = $true)] - [string[]] $WebAccessServer + [System.String[]] + $WebAccessServer ) Write-Verbose 'Checking RDSH role is deployed on this node.' diff --git a/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 index 3882630..d551b21 100644 --- a/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 @@ -1,405 +1,377 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDSessionDeployment' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DscResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDSessionDeployment' - -# Import-Module RemoteDesktop -Force - -# $sessionDeploymentSplat = @{ -# SessionHost = 'sessionhost.lan' -# ConnectionBroker = 'connectionbroker.lan' -# WebAccessServer = 'webaccess.lan' -# } - -# $sessionDeploymentMultiSplat = @{ -# SessionHost = 'sessionhost1.lan', 'sessionhost2.lan' -# ConnectionBroker = 'connectionbroker.lan' -# WebAccessServer = 'webaccess1.lan', 'webaccess2.lan' -# } - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# [array]$commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters -# $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters - -# $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | Where-Object { $_ -notin $commonParameters } | ForEach-Object -Process { -# @{ -# Property = $_ -# Value = $sessionDeploymentSplat[$_] -# } -# } - -# Context 'RDSessionDeployment is not present' { -# Mock -CommandName Get-Service -ParameterFilter { $Name -eq 'RDMS' } -MockWith { -# Write-Error 'MOCK Get-Service with parameter RDMS' -# } - -# Mock -CommandName Get-RDServer - -# It 'Should attempt to GET the RDMS service but fail given the RDMS service is not present' { -# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue -# Assert-MockCalled -CommandName Get-Service -Times 1 -# } - -# It 'Should attempt to START the RDMS service but fail given the RDMS service is not present' { -# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue -# $serviceWarning | Should BeLike "Failed to start RDMS service. Error: 'Cannot find any service with service name 'RDMS'.'." -# } - -# It 'Should return $null on property in Get-TargetResource ' { -# param -# ( -# $Property, -# $Value -# ) -# $get = Get-TargetResource @sessionDeploymentSplat -# $get.$Property | Should Be $null -# } -TestCases $allParameters -# } - -# Context 'RDSessionDeployment is present' { -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.SessionHost -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.WebAccessServer -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } - -# Mock -CommandName Start-Service -# Mock -CommandName Get-Service -MockWith { -# [pscustomobject]@{ -# Status = 'Stopped' -# } -# } - -# It 'Should attempt to start the RDMS service, given the RDMS service is stopped' { -# Get-TargetResource @sessionDeploymentSplat -# Assert-MockCalled -CommandName Start-Service -Times 1 -Scope It -# } - -# Mock -CommandName Start-Service -MockWith { -# throw 'Throwing from Start-Service mock' -# } - -# It 'Should generate a warning, given RDMS service is stopped and start fails' { -# Get-TargetResource @sessionDeploymentSplat -WarningVariable serviceWarning -WarningAction SilentlyContinue -# $serviceWarning | Should Be "Failed to start RDMS service. Error: 'Throwing from Start-Service mock'." -# } - -# Mock -CommandName Get-Service -MockWith { -# [pscustomobject]@{ -# Status = 'Running' -# } -# } - -# It 'Should not attempt to start the RDMS service, given the RDMS service is running' { -# Get-TargetResource @sessionDeploymentSplat -# Assert-MockCalled -CommandName Start-Service -Times 0 -Scope It -# } - -# It 'Should return property with value in Get-TargetResource ' { -# param -# ( -# $Property, -# $Value -# ) -# $get = Get-TargetResource @sessionDeploymentSplat -# $get.$Property | Should Be $Value -# } -TestCases $allParameters - -# It 'Should connect to the right connection broker' { -# Assert-MockCalled -CommandName Get-RDServer -Scope Context -ParameterFilter { -# $ConnectionBroker -eq $sessionDeploymentSplat['ConnectionBroker'] -# } -# } -# } -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { - -# Mock -CommandName New-RDSessionDeployment -# Mock -CommandName Add-RDServer -# Mock -CommandName Get-TargetResource - -# It 'should call New-RDSessionDeployment with all required parameters' { -# Set-TargetResource @sessionDeploymentSplat -# Assert-MockCalled -CommandName New-RDSessionDeployment -Times 1 -ParameterFilter { -# $SessionHost -eq $sessionDeploymentSplat.SessionHost -and -# $ConnectionBroker -eq $sessionDeploymentSplat.ConnectionBroker -and -# $WebAccessServer -eq $sessionDeploymentSplat.WebAccessServer -# } -# Assert-MockCalled -CommandName Add-RDServer -Times 0 -# } - -# Mock -CommandName Get-TargetResource -MockWith { -# [pscustomobject]@{ -# SessionHost = 'OtherSessionHost.Lan' -# ConnectionBroker = $sessionDeploymentSplat.ConnectionBroker -# WebAccessServer = $sessionDeploymentSplat.WebAccessServer -# } -# } - -# It 'should call Add-RDServer with additional servers' { -# Set-TargetResource @sessionDeploymentMultiSplat -# Assert-MockCalled -CommandName Add-RDServer -Times 3 -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { - -# Mock -CommandName Get-Service -MockWith { -# [pscustomobject]@{ -# Status = 'Running' -# } -# } -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.SessionHost -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = 'connectionbrokernew.lan' -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.WebAccessServer -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } - -# It 'Should return false, given the ConnectionBroker is not targeted in this deployment' { -# Test-TargetResource @sessionDeploymentSplat | Should Be $false -# } - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.SessionHost -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# [pscustomobject]@{ -# Server = 'webaccessnew.lan' -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } - -# It 'Should return false, given the WebAccessServer is not targeted in this deployment' { -# Test-TargetResource @sessionDeploymentSplat | Should Be $false -# } - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'sessionhost1.lan' -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = 'sessionhost2.lan' -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = 'sessionhost3.lan' -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentMultiSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) -# { -# [pscustomobject]@{ -# Server = $waserver -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } -# } - -# It 'Should return false, given the list of Session Hosts is different in this deployment' { -# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false -# } - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = $sessionDeploymentMultiSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) -# { -# [pscustomobject]@{ -# Server = $waserver -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } -# } - -# It 'Should return false, given the list of Session Hosts is empty in this deployment' { -# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false -# } - -# Mock -CommandName Get-RDServer -MockWith { -# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) -# { -# [pscustomobject]@{ -# Server = $shserver -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentMultiSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# } - -# It 'Should return false, given the list of Web Hosts is empty in this deployment' { -# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false -# } - -# Mock -CommandName Get-RDServer -MockWith { -# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) -# { -# [pscustomobject]@{ -# Server = $shserver -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# foreach ($waserver in $sessionDeploymentMultiSplat.WebAccessServer) -# { -# [pscustomobject]@{ -# Server = $waserver -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } -# } - -# It 'Should return true, given the SessionDeployment is completed' { -# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $true -# } - -# Mock -CommandName Get-RDServer -MockWith { -# foreach ($shserver in $sessionDeploymentMultiSplat.SessionHost) -# { -# [pscustomobject]@{ -# Server = $shserver -# Roles = @( -# 'RDS-RD-SERVER' -# ) -# } -# } -# [pscustomobject]@{ -# Server = $sessionDeploymentMultiSplat.ConnectionBroker -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# [pscustomobject]@{ -# Server = 'SomeWebAccessServer.lan' -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } - -# It 'Should return false if Web Access Server list is different' { -# Test-TargetResource @sessionDeploymentMultiSplat | Should Be $false -# } -# } -# #endregion -# } -# } -# catch -# { -# throw $_ -# } -# finally -# { -# Invoke-TestCleanup -# } +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDSessionDeployment' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDSessionDeployment\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the resource is not present' { + BeforeAll { + Mock -CommandName Get-Service + Mock -CommandName Start-Service -MockWith { + throw 'Cannot find any service with service name ''RDMS''.' + } + + Mock -CommandName Get-RDServer + } + + Context 'When the RDMS service is not present' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $result = Get-TargetResource @testParams -WarningVariable serviceWarning -WarningAction SilentlyContinue + + $result.SessionHost | Should -BeNullOrEmpty + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.WebAccessServer | Should -BeNullOrEmpty + + $serviceWarning | Should -BeLike "Failed to start RDMS service. Error: 'Cannot find any service with service name 'RDMS'.'." + } + + Should -Invoke -CommandName Get-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Start-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the resource is present' { + BeforeAll { + Mock -CommandName Get-RDServer -MockWith { + [PSCustomObject] @{ + Server = 'sessionhost.lan' + Roles = @( + 'RDS-RD-SERVER' + ) + } + [PSCustomObject] @{ + Server = 'connectionbroker.lan' + Roles = @( + 'RDS-CONNECTION-BROKER' + ) + } + [PSCustomObject] @{ + Server = 'webaccess.lan' + Roles = @( + 'RDS-WEB-ACCESS' + ) + } + } + + Mock -CommandName Start-Service + Mock -CommandName Get-Service -MockWith { + [PSCustomObject] @{ + Status = 'Stopped' + } + } + } + + Context 'When the RDMS service is stopped' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $result = Get-TargetResource @testParams + + $result.SessionHost | Should -Be $testParams.SessionHost + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.WebAccessServer | Should -Be $testParams.WebAccessServer + } + + Should -Invoke -CommandName Get-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Start-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When the RDMS service fails to start' { + BeforeAll { + Mock -CommandName Start-Service -MockWith { + throw 'Throwing from Start-Service mock' + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $result = Get-TargetResource @testParams -WarningVariable serviceWarning -WarningAction SilentlyContinue + + $result.SessionHost | Should -Be $testParams.SessionHost + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.WebAccessServer | Should -Be $testParams.WebAccessServer + + $serviceWarning | Should -BeLike "Failed to start RDMS service. Error: 'Throwing from Start-Service mock'." + } + + Should -Invoke -CommandName Get-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Start-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When the RDMS service is already running' { + BeforeAll { + Mock -CommandName Get-Service -MockWith { + [PSCustomObject]@{ + Status = 'Running' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $result = Get-TargetResource @testParams + + $result.SessionHost | Should -Be $testParams.SessionHost + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.WebAccessServer | Should -Be $testParams.WebAccessServer + } + + Should -Invoke -CommandName Get-Service -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Start-Service -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + } +} + +Describe 'MSFT_xRDSessionDeployment\Set-TargetResource' -Tag 'Set' { + Context 'When the resource is not in the desired state' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName New-RDSessionDeployment + Mock -CommandName Add-RDServer + } + + Context 'When the deployment does not exist' { + BeforeAll { + Mock -CommandName Get-TargetResource + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionDeployment -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + } + } + + Context 'When the deployment does exist exist' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost1.lan', 'sessionhost2.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess1.lan', 'webaccess2.lan' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionDeployment -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 3 -Scope It + } + } + } +} + +Describe 'MSFT_xRDSessionDeployment\Test-TargetResource' -Tag 'Test' { + BeforeAll { + Mock -CommandName Assert-Module + } + + BeforeDiscovery { + $testCases = @( + @{ + Property = 'ConnectionBroker' + Current = 'connectionbroker.lan' + Desired = 'connectionbroker2.lan' + }, + @{ + Property = 'SessionHost' + Current = $null + Desired = 'sessionhost1.lan', 'sessionhost3.lan' + }, + @{ + Property = 'SessionHost' + Current = 'sessionhost.lan' + Desired = 'sessionhost1.lan', 'sessionhost3.lan' + }, + @{ + Property = 'WebAccessServer' + Current = $null + Desired = 'webaccess1.lan', 'webaccess3.lan' + } + @{ + Property = 'WebAccessServer' + Current = 'webaccess.lan' + Desired = 'webaccess1.lan', 'webaccess3.lan' + } + ) + } + + Context 'When the property '''' is not correct' -ForEach $testCases { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + $obj = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $obj[$Property] = $Current + return $obj + } + } + + It 'Should return the correct result' { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + $testParams[$Property] = $Desired + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + + Context 'When the system is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess.lan' + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } +} From 1cb65911525abfb7ec71324225ae916151e0efb7 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:00:08 +0100 Subject: [PATCH 15/44] Add PR 124 --- .../MSFT_xRDSessionDeployment.psm1 | 56 +++----- .../Unit/MSFT_xRDSessionDeployment.Tests.ps1 | 133 +++++++++++++----- 2 files changed, 122 insertions(+), 67 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 index 4521c4a..09305bc 100644 --- a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 +++ b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 @@ -54,7 +54,7 @@ function Get-TargetResource @{ SessionHost = [System.String[]] ($deployed | Where-Object Roles -Contains 'RDS-RD-SERVER' | ForEach-Object Server) ConnectionBroker = $deployed | Where-Object Roles -Contains 'RDS-CONNECTION-BROKER' | ForEach-Object Server - WebAccessServer = $deployed | Where-Object Roles -Contains 'RDS-WEB-ACCESS' | ForEach-Object Server + WebAccessServer = [System.String[]] ($deployed | Where-Object Roles -Contains 'RDS-WEB-ACCESS' | ForEach-Object Server) } } @@ -104,11 +104,23 @@ function Set-TargetResource Add-RDServer -Server $server -Role 'RDS-RD-SERVER' -ConnectionBroker $ConnectionBroker } + foreach ($server in ($currentStatus.SessionHost | Where-Object { $_ -notin $SessionHost })) + { + Write-Verbose "Removing server '$server' from deployment." + Remove-RDServer -Server $server -Role 'RDS-RD-SERVER' -ConnectionBroker $ConnectionBroker -Force + } + foreach ($server in ($WebAccessServer | Select-Object -Skip 1 | Where-Object { $_ -notin $currentStatus.WebAccessServer })) { Write-Verbose "Adding server '$server' to deployment." Add-RDServer -Server $server -Role 'RDS-WEB-ACCESS' -ConnectionBroker $ConnectionBroker } + + foreach ($server in ($currentStatus.WebAccessServer | Where-Object { $_ -notin $WebAccessServer })) + { + Write-Verbose "Removing Web Server '$server' from deployment." + Remove-RDServer -Server $server -Role 'RDS-WEB-ACCESS' -ConnectionBroker $ConnectionBroker -Force + } } ####################################################################### @@ -127,48 +139,22 @@ function Test-TargetResource [Parameter(Mandatory = $true)] [System.String] $ConnectionBroker, - + [Parameter(Mandatory = $true)] [System.String[]] $WebAccessServer ) Write-Verbose 'Checking RDSH role is deployed on this node.' - $currentStatus = Get-TargetResource @PSBoundParameters - - if ($currentStatus.ConnectionBroker -ne $ConnectionBroker) - { - Write-Verbose -Message "Found connection broker '$($currentStatus.ConnectionBroker)', expected '$ConnectionBroker'" - return $false - } - if ($WebAccessServer.Count -gt 0 -and $null -eq $currentStatus.WebAccessServer) - { - Write-Verbose -Message "Desired list of Web Access Servers is empty, while $($WebAccessServer.Count) Web Access Servers should have been configured." - return $false - } - - $compare = Compare-Object -ReferenceObject $WebAccessServer -DifferenceObject $currentStatus.WebAccessServer - if ($null -ne $compare) - { - Write-Verbose -Message "Desired list of Web Access Servers not equal`r`n$($compare | Out-String)" - return $false - } - - if ($SessionHost.Count -gt 0 -and $null -eq $currentStatus.SessionHost) - { - Write-Verbose -Message "Desired list of session hosts is empty, while $($SessionHost.Count) session hosts should have been configured." - return $false - } - - $compare = Compare-Object -ReferenceObject $SessionHost -DifferenceObject $currentStatus.SessionHost - if ($null -ne $compare) - { - Write-Verbose -Message "Desired list of session hosts not equal`r`n$($compare | Out-String)" - return $false - } + $desiredState = $PSBoundParameters + $currentState = Get-TargetResource @PSBoundParameters - $true + return Test-DscParameterState ` + -CurrentValues $currentState ` + -DesiredValues $desiredState ` + -SortArrayValues ` + -Verbose:$VerbosePreference } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 index d551b21..6a6c931 100644 --- a/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionDeployment.Tests.ps1 @@ -135,9 +135,9 @@ Describe 'MSFT_xRDSessionDeployment\Get-TargetResource' -Tag 'Get' { Set-StrictMode -Version 1.0 $testParams = @{ - SessionHost = 'sessionhost.lan' + SessionHost = [System.String[]] 'sessionhost.lan' ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess.lan' + WebAccessServer = [System.String[]] 'webaccess.lan' } $result = Get-TargetResource @testParams @@ -225,6 +225,7 @@ Describe 'MSFT_xRDSessionDeployment\Set-TargetResource' -Tag 'Set' { Mock -CommandName Assert-Module Mock -CommandName New-RDSessionDeployment Mock -CommandName Add-RDServer + Mock -CommandName Remove-RDServer } Context 'When the deployment does not exist' { @@ -251,43 +252,111 @@ Describe 'MSFT_xRDSessionDeployment\Set-TargetResource' -Tag 'Set' { } } - Context 'When the deployment does exist exist' { - BeforeAll { - Mock -CommandName Get-TargetResource -MockWith { + Context 'When the deployment does exist' { + BeforeDiscovery { + $addTestCases = @( @{ - SessionHost = 'sessionhost.lan' - ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess.lan' + Property = 'SessionHost' + DesiredValue = [System.String[]] ('sessionhost1.lan', 'sessionhost2.lan') + CurrentValue = [System.String[]] ('sessionhost1.lan') } - } + @{ + Property = 'WebAccessServer' + DesiredValue = [System.String[]] ('webaccess1.lan', 'webaccess2.lan') + CurrentValue = [System.String[]] ('webaccess1.lan') + } + ) + + $removeTestCases = @( + @{ + Property = 'SessionHost' + DesiredValue = [System.String[]] ('sessionhost1.lan') + CurrentValue = [System.String[]] ('sessionhost1.lan', 'sessionhost2.lan') + } + @{ + Property = 'WebAccessServer' + DesiredValue = [System.String[]] ('webaccess1.lan') + CurrentValue = [System.String[]] ('webaccess1.lan', 'webaccess2.lan') + } + ) } - It 'Should call the correct mocks' { - InModuleScope -ScriptBlock { - Set-StrictMode -Version 1.0 + Context 'When a '''' should be added' -ForEach $addTestCases { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + $obj = @{ + SessionHost = [System.String[]] ('sessionhost1.lan') + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = [System.String[]] ('webaccess1.lan') + } + + $obj[$Property] = $CurrentValue + return $obj + } + } - $testParams = @{ - SessionHost = 'sessionhost1.lan', 'sessionhost2.lan' - ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess1.lan', 'webaccess2.lan' + It 'Should call the correct mocks' { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost1.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess1.lan' + } + + $testParams[$Property] = $DesiredValue + + $null = Set-TargetResource @testParams } - $null = Set-TargetResource @testParams + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionDeployment -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-RDServer -Exactly -Times 0 -Scope It } + } - Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It - Should -Invoke -CommandName New-RDSessionDeployment -Exactly -Times 0 -Scope It - Should -Invoke -CommandName Add-RDServer -Exactly -Times 3 -Scope It + Context 'When a '''' should be removed' -ForEach $removeTestCases { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { + $obj = @{ + SessionHost = [System.String[]] ('sessionhost1.lan') + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = [System.String[]] ('webaccess1.lan') + } + + $obj[$Property] = $CurrentValue + return $obj + } + } + + It 'Should call the correct mocks' { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + SessionHost = 'sessionhost1.lan' + ConnectionBroker = 'connectionbroker.lan' + WebAccessServer = 'webaccess1.lan' + } + + $testParams[$Property] = $DesiredValue + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionDeployment -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Remove-RDServer -Exactly -Times 1 -Scope It + } } } } } Describe 'MSFT_xRDSessionDeployment\Test-TargetResource' -Tag 'Test' { - BeforeAll { - Mock -CommandName Assert-Module - } - BeforeDiscovery { $testCases = @( @{ @@ -298,22 +367,22 @@ Describe 'MSFT_xRDSessionDeployment\Test-TargetResource' -Tag 'Test' { @{ Property = 'SessionHost' Current = $null - Desired = 'sessionhost1.lan', 'sessionhost3.lan' + Desired = [System.String[]] 'sessionhost1.lan', 'sessionhost3.lan' }, @{ Property = 'SessionHost' Current = 'sessionhost.lan' - Desired = 'sessionhost1.lan', 'sessionhost3.lan' + Desired = [System.String[]] 'sessionhost1.lan', 'sessionhost3.lan' }, @{ Property = 'WebAccessServer' Current = $null - Desired = 'webaccess1.lan', 'webaccess3.lan' + Desired = [System.String[]] 'webaccess1.lan', 'webaccess3.lan' } @{ Property = 'WebAccessServer' Current = 'webaccess.lan' - Desired = 'webaccess1.lan', 'webaccess3.lan' + Desired = [System.String[]] 'webaccess1.lan', 'webaccess3.lan' } ) } @@ -322,9 +391,9 @@ Describe 'MSFT_xRDSessionDeployment\Test-TargetResource' -Tag 'Test' { BeforeAll { Mock -CommandName Get-TargetResource -MockWith { $obj = @{ - SessionHost = 'sessionhost.lan' + SessionHost = [System.String[]] 'sessionhost.lan' ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess.lan' + WebAccessServer = [System.String[]]'webaccess.lan' } $obj[$Property] = $Current @@ -353,9 +422,9 @@ Describe 'MSFT_xRDSessionDeployment\Test-TargetResource' -Tag 'Test' { BeforeAll { Mock -CommandName Get-TargetResource -MockWith { @{ - SessionHost = 'sessionhost.lan' + SessionHost = [System.String[]] 'sessionhost.lan' ConnectionBroker = 'connectionbroker.lan' - WebAccessServer = 'webaccess.lan' + WebAccessServer = [System.String[]]'webaccess.lan' } } } From cf3bf493946fa65d8128d412ca868540772cd8ca Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:32:55 +0100 Subject: [PATCH 16/44] Reformat --- ...SFT_xRDSessionCollectionConfiguration.psm1 | 360 +++++++++++++----- 1 file changed, 260 insertions(+), 100 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 index d903306..c87a2bb 100644 --- a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -22,58 +20,112 @@ function Get-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter()] - [uint32] $ActiveSessionLimitMin, + [System.UInt32] + $ActiveSessionLimitMin, + [Parameter()] - [boolean] $AuthenticateUsingNLA, + [System.Boolean] + $AuthenticateUsingNLA, + [Parameter()] - [boolean] $AutomaticReconnectionEnabled, + [System.Boolean] + $AutomaticReconnectionEnabled, + [Parameter()] - [string] $BrokenConnectionAction, + [System.String] + $BrokenConnectionAction, + [Parameter()] - [string] $ClientDeviceRedirectionOptions, + [System.String] + $ClientDeviceRedirectionOptions, + [Parameter()] - [boolean] $ClientPrinterAsDefault, + [System.Boolean] + $ClientPrinterAsDefault, + [Parameter()] - [boolean] $ClientPrinterRedirected, + [System.Boolean] + $ClientPrinterRedirected, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [string] $CustomRdpProperty, + [System.String] + $CustomRdpProperty, + [Parameter()] - [uint32] $DisconnectedSessionLimitMin, + [System.UInt32] + $DisconnectedSessionLimitMin, + [Parameter()] - [string] $EncryptionLevel, + [System.String] + $EncryptionLevel, + [Parameter()] - [uint32] $IdleSessionLimitMin, + [System.UInt32] + $IdleSessionLimitMin, + [Parameter()] - [uint32] $MaxRedirectedMonitors, + [System.UInt32] + $MaxRedirectedMonitors, + [Parameter()] - [boolean] $RDEasyPrintDriverEnabled, + [System.Boolean] + $RDEasyPrintDriverEnabled, + [Parameter()] - [string] $SecurityLayer, + [System.String] + $SecurityLayer, + [Parameter()] - [boolean] $TemporaryFoldersDeletedOnExit, + [System.Boolean] + $TemporaryFoldersDeletedOnExit, + [Parameter()] - [string[]] $UserGroup, + [System.String[]] + $UserGroup, + [Parameter()] - [string] $DiskPath, + [System.String] + $DiskPath, + [Parameter()] - [bool] $EnableUserProfileDisk, + [System.Boolean] + $EnableUserProfileDisk, + [Parameter()] - [uint32] $MaxUserProfileDiskSizeGB, + [System.UInt32] + $MaxUserProfileDiskSizeGB, + [Parameter()] - [string[]] $IncludeFolderPath, + [System.String[]] + $IncludeFolderPath, + [Parameter()] - [string[]] $ExcludeFolderPath, + [System.String[]] + $ExcludeFolderPath, + [Parameter()] - [string[]] $IncludeFilePath, + [System.String[]] + $IncludeFilePath, + [Parameter()] - [string[]] $ExcludeFilePath + [System.String[]] + $ExcludeFilePath ) + + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose "Getting currently configured RDSH Collection properties for collection $CollectionName" $collectionGeneral = Get-RDSessionCollectionConfiguration -CollectionName $CollectionName @@ -84,23 +136,27 @@ function Get-TargetResource $result = @{ CollectionName = $collectionGeneral.CollectionName - ActiveSessionLimitMin = $collectionConnection.ActiveSessionLimitMin - AuthenticateUsingNLA = $collectionSecurity.AuthenticateUsingNLA - AutomaticReconnectionEnabled = $collectionConnection.AutomaticReconnectionEnabled - BrokenConnectionAction = $collectionConnection.BrokenConnectionAction + CollectionDescription = $collectionGeneral.CollectionDescription + # For whatever reason this value gets returned with a trailing carriage return + CustomRdpProperty = ([System.String]$collectionGeneral.CustomRdpProperty).Trim() + ClientDeviceRedirectionOptions = $collectionClient.ClientDeviceRedirectionOptions ClientPrinterAsDefault = $collectionClient.ClientPrinterAsDefault ClientPrinterRedirected = $collectionClient.ClientPrinterRedirected - CollectionDescription = $collectionGeneral.CollectionDescription - # For whatever reason this value gets returned with a trailing carriage return - CustomRdpProperty = ([string]$collectionGeneral.CustomRdpProperty).Trim() - DisconnectedSessionLimitMin = $collectionConnection.DisconnectedSessionLimitMin - EncryptionLevel = $collectionSecurity.EncryptionLevel - IdleSessionLimitMin = $collectionConnection.IdleSessionLimitMin MaxRedirectedMonitors = $collectionClient.MaxRedirectedMonitors RDEasyPrintDriverEnabled = $collectionClient.RDEasyPrintDriverEnabled - SecurityLayer = $collectionSecurity.SecurityLayer + + ActiveSessionLimitMin = $collectionConnection.ActiveSessionLimitMin + AutomaticReconnectionEnabled = $collectionConnection.AutomaticReconnectionEnabled + BrokenConnectionAction = $collectionConnection.BrokenConnectionAction + DisconnectedSessionLimitMin = $collectionConnection.DisconnectedSessionLimitMin + IdleSessionLimitMin = $collectionConnection.IdleSessionLimitMin TemporaryFoldersDeletedOnExit = $collectionConnection.TemporaryFoldersDeletedOnExit + + AuthenticateUsingNLA = $collectionSecurity.AuthenticateUsingNLA + EncryptionLevel = $collectionSecurity.EncryptionLevel + SecurityLayer = $collectionSecurity.SecurityLayer + UserGroup = $collectionUserGroup.UserGroup } @@ -132,58 +188,112 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter()] - [uint32] $ActiveSessionLimitMin, + [System.UInt32] + $ActiveSessionLimitMin, + [Parameter()] - [boolean] $AuthenticateUsingNLA, + [System.Boolean] + $AuthenticateUsingNLA, + [Parameter()] - [boolean] $AutomaticReconnectionEnabled, + [System.Boolean] + $AutomaticReconnectionEnabled, + [Parameter()] - [string] $BrokenConnectionAction, + [System.String] + $BrokenConnectionAction, + [Parameter()] - [string] $ClientDeviceRedirectionOptions, + [System.String] + $ClientDeviceRedirectionOptions, + [Parameter()] - [boolean] $ClientPrinterAsDefault, + [System.Boolean] + $ClientPrinterAsDefault, + [Parameter()] - [boolean] $ClientPrinterRedirected, + [System.Boolean] + $ClientPrinterRedirected, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [string] $CustomRdpProperty, + [System.String] + $CustomRdpProperty, + [Parameter()] - [uint32] $DisconnectedSessionLimitMin, + [System.UInt32] + $DisconnectedSessionLimitMin, + [Parameter()] - [string] $EncryptionLevel, + [System.String] + $EncryptionLevel, + [Parameter()] - [uint32] $IdleSessionLimitMin, + [System.UInt32] + $IdleSessionLimitMin, + [Parameter()] - [uint32] $MaxRedirectedMonitors, + [System.UInt32] + $MaxRedirectedMonitors, + [Parameter()] - [boolean] $RDEasyPrintDriverEnabled, + [System.Boolean] + $RDEasyPrintDriverEnabled, + [Parameter()] - [string] $SecurityLayer, + [System.String] + $SecurityLayer, + [Parameter()] - [boolean] $TemporaryFoldersDeletedOnExit, + [System.Boolean] + $TemporaryFoldersDeletedOnExit, + [Parameter()] - [string[]] $UserGroup, + [System.String[]] + $UserGroup, + [Parameter()] - [string] $DiskPath, + [System.String] + $DiskPath, + [Parameter()] - [bool] $EnableUserProfileDisk, + [System.Boolean] + $EnableUserProfileDisk, + [Parameter()] - [uint32] $MaxUserProfileDiskSizeGB, + [System.UInt32] + $MaxUserProfileDiskSizeGB, + [Parameter()] - [string[]] $IncludeFolderPath, + [System.String[]] + $IncludeFolderPath, + [Parameter()] - [string[]] $ExcludeFolderPath, + [System.String[]] + $ExcludeFolderPath, + [Parameter()] - [string[]] $IncludeFilePath, + [System.String[]] + $IncludeFilePath, + [Parameter()] - [string[]] $ExcludeFilePath + [System.String[]] + $ExcludeFilePath ) + + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose 'Setting DSC collection properties' try @@ -217,10 +327,9 @@ function Set-TargetResource if ($DiskPath) { - $validateDiskPath = Test-Path -Path $DiskPath -ErrorAction SilentlyContinue - if (-not($validateDiskPath)) + if (-not(Test-Path -Path $DiskPath -ErrorAction SilentlyContinue)) { - throw "To enable UserProfileDisk we need a valid DiskPath. Path $DiskPath not found" + New-InvalidArgumentException -ArgumentName 'DiskPath' -Message ('To enable UserProfileDisk we need a valid DiskPath. Path {0} not found' -f $DiskPath) } else { @@ -229,7 +338,7 @@ function Set-TargetResource } else { - throw 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' + New-InvalidArgumentException -ArgumentName 'DiskPath' -Message 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' } if ($MaxUserProfileDiskSizeGB -gt 0) @@ -238,7 +347,9 @@ function Set-TargetResource } else { - throw "To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value $MaxUserProfileDiskSizeGB is not valid" + New-InvalidArgumentException -ArgumentName 'MaxUserProfileDiskSizeGB' -Message ( + 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value {0} is not valid' -f $MaxUserProfileDiskSizeGB + ) } $enableUserProfileDiskSplat = @{ @@ -295,61 +406,110 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter()] - [uint32] $ActiveSessionLimitMin, + [System.UInt32] + $ActiveSessionLimitMin, + [Parameter()] - [boolean] $AuthenticateUsingNLA, + [System.Boolean] + $AuthenticateUsingNLA, + [Parameter()] - [boolean] $AutomaticReconnectionEnabled, + [System.Boolean] + $AutomaticReconnectionEnabled, + [Parameter()] - [string] $BrokenConnectionAction, + [System.String] + $BrokenConnectionAction, + [Parameter()] - [string] $ClientDeviceRedirectionOptions, + [System.String] + $ClientDeviceRedirectionOptions, + [Parameter()] - [boolean] $ClientPrinterAsDefault, + [System.Boolean] + $ClientPrinterAsDefault, + [Parameter()] - [boolean] $ClientPrinterRedirected, + [System.Boolean] + $ClientPrinterRedirected, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [string] $CustomRdpProperty, + [System.String] + $CustomRdpProperty, + [Parameter()] - [uint32] $DisconnectedSessionLimitMin, + [System.UInt32] + $DisconnectedSessionLimitMin, + [Parameter()] - [string] $EncryptionLevel, + [System.String] + $EncryptionLevel, + [Parameter()] - [uint32] $IdleSessionLimitMin, + [System.UInt32] + $IdleSessionLimitMin, + [Parameter()] - [uint32] $MaxRedirectedMonitors, + [System.UInt32] + $MaxRedirectedMonitors, + [Parameter()] - [boolean] $RDEasyPrintDriverEnabled, + [System.Boolean] + $RDEasyPrintDriverEnabled, + [Parameter()] - [string] $SecurityLayer, + [System.String] + $SecurityLayer, + [Parameter()] - [boolean] $TemporaryFoldersDeletedOnExit, + [System.Boolean] + $TemporaryFoldersDeletedOnExit, + [Parameter()] - [string[]] $UserGroup, + [System.String[]] + $UserGroup, + [Parameter()] - [string] $DiskPath, + [System.String] + $DiskPath, + [Parameter()] - [bool] $EnableUserProfileDisk, + [System.Boolean] + $EnableUserProfileDisk, + [Parameter()] - [uint32] $MaxUserProfileDiskSizeGB, + [System.UInt32] + $MaxUserProfileDiskSizeGB, + [Parameter()] - [string[]] $IncludeFolderPath, + [System.String[]] + $IncludeFolderPath, + [Parameter()] - [string[]] $ExcludeFolderPath, + [System.String[]] + $ExcludeFolderPath, + [Parameter()] - [string[]] $IncludeFilePath, + [System.String[]] + $IncludeFilePath, + [Parameter()] - [string[]] $ExcludeFilePath + [System.String[]] + $ExcludeFilePath ) - $verbose = $PSBoundParameters.Verbose -eq $true - Write-Verbose 'Testing DSC collection properties' $null = $PSBoundParameters.Remove('Verbose') @@ -361,8 +521,8 @@ function Test-TargetResource Write-Verbose 'Running on W2012R2 or lower, removing properties that are not compatible' $null = $PSBoundParameters.Remove('CollectionName') - $null = $PSBoundParameters.Remove('DiskPath') $null = $PSBoundParameters.Remove('EnableUserProfileDisk') + $null = $PSBoundParameters.Remove('DiskPath') $null = $PSBoundParameters.Remove('ExcludeFilePath') $null = $PSBoundParameters.Remove('ExcludeFolderPath') $null = $PSBoundParameters.Remove('IncludeFilePath') @@ -387,7 +547,7 @@ function Test-TargetResource DesiredValues = $PSBoundParameters TurnOffTypeChecking = $true SortArrayValues = $true - Verbose = $verbose + Verbose = $VerbosePreference } Test-DscParameterState @testDscParameterStateSplat From 7d6f25613f05a97f9956254597eaf69a500fac79 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:33:10 +0100 Subject: [PATCH 17/44] Migrate RDSessionCollectionConfiguration --- ...RDSessionCollectionConfiguration.Tests.ps1 | 1446 +++++++++++++---- 1 file changed, 1106 insertions(+), 340 deletions(-) diff --git a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 index cf93c1f..5903d79 100644 --- a/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionCollectionConfiguration.Tests.ps1 @@ -1,340 +1,1106 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDSessionCollectionConfiguration' - -# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' -# $collectionName = 'TestCollection' - -# Import-Module RemoteDesktop -Force - - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { -# $null -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# } -# } - -# Mock -CommandName Get-RDSessionHost -MockWith { -# [pscustomobject]@{ -# SessionHost = [System.Net.Dns]::GetHostEntry((hostname)).HostName -# CollectionName = 'TestCollection' -# } -# } - -# Mock -CommandName Get-RDSessionCollectionConfiguration -# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# IncludeFolderPath = $null -# ExcludeFolderPath = $null -# IncludeFilePath = $null -# ExcludeFilePath = $null -# DiskPath = 'c:\temp' -# EnableUserProfileDisk = $true -# MaxUserProfileDiskSizeGB = 5 -# } -# } -ParameterFilter { $UserProfileDisk -eq $true } - -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Get-TargetResource -CollectionName $testInvalidCollectionName } ` -# | Should throw -# } -# } - -# Context 'Get properties on Windows Server 2012 (R2)' { -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'6.3.9600.0' -# } - -# It 'Should not call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { -# Get-TargetResource -CollectionName $collectionName -# Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 0 -Exactly -# } -# It 'Should not call Set-RDSessionCollectionConfiguration' { -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context -# } -# } - -# Context 'Get properties on Windows Server 2016+' { -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'10.0.14393.0' -# } - -# $getTargetResourceResult = Get-TargetResource -CollectionName $collectionName -# It 'Should call Get-RDSessionCollectionConfiguration with parameter UserProfileDisk' { -# Assert-MockCalled -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Times 1 -Exactly -# } -# It 'Get-TargetResource result should list UserProfileDisk property ' { -# param ( -# [string]$Property -# ) -# $getTargetResourceResult.GetEnumerator().Name | Where-Object { $_ -eq $Property } | Should Be $Property -# } -TestCases @( -# @{ -# Property = 'DiskPath' -# } -# @{ -# Property = 'EnableUserProfileDisk' -# } -# @{ -# Property = 'ExcludeFilePath' -# } -# @{ -# Property = 'ExcludeFolderPath' -# } -# @{ -# Property = 'IncludeFilePath' -# } -# @{ -# Property = 'IncludeFolderPath' -# } -# @{ -# Property = 'MaxUserProfileDiskSizeGB' -# } -# ) -# It 'Should not call Set-RDSessionCollectionConfiguration' { -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -Times 0 -Exactly -Scope Context -# } -# } -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { - -# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { -# $null -# } - -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Set-TargetResource -CollectionName $testInvalidCollectionName } ` -# | Should throw -# } -# } - -# Context 'Running on set on Windows Server 2012 (R2)' { -# Mock -CommandName Get-RDSessionCollection -MockWith { $true } - -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'6.3.9600.0' -# } - -# It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should not call Set-RDSessionCollectionConfiguration with parameter EnableUserProfileDisk' { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly -# } -# } - -# Context 'Running on set on Windows Server 2016 (or higher)' { -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'10.0.14393.0' -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# throw 'No session collection DoesNotExist was found.' -# } - -# It 'Trying to configure a non existing collection should throw' { -# $errorMessages = try -# { -# Set-TargetResource -CollectionName 'DoesNotExist' -ActiveSessionLimitMin 1 -# } -# catch -# { -# $_ 2>&1 -# } - -# $errorMessages.Exception.Message | Should Be 'Failed to lookup RD Session Collection DoesNotExist. Error: No session collection DoesNotExist was found.' -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { $true } -# It 'Running Set on W2016 with only EnableUserProfileDisk specified should throw on missing DiskPath parameter' { -# $errorMessages = try -# { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -# } -# catch -# { -# $_ 2>&1 -# } - -# $errorMessages.Exception.Message | Should Be 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' -# } - -# It 'Running Set on W2016 with EnableUserProfileDisk and Diskpath specified should throw on invalid MaxUserProfileDiskSizeGB parameter' { -# $errorMessages = try -# { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ -# } -# catch -# { -# $_ 2>&1 -# } - -# $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value 0 is not valid' -# } - -# It 'Running Set with EnableUserProfileDisk, DiskPath and MaxUserProfileDiskSizeGB, but with an invalid DiskPath, should throw' { -# $errorMessages = try -# { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\NonExistingPath -MaxUserProfileDiskSizeGB 5 -# } -# catch -# { -# $_ 2>&1 -# } - -# $errorMessages.Exception.Message | Should Be 'To enable UserProfileDisk we need a valid DiskPath. Path TestDrive:\NonExistingPath not found' -# } - -# It 'Running Set with all valid parameters should call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true -DiskPath TestDrive:\ -MaxUserProfileDiskSizeGB 5 -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It -# } - -# It 'Running Set without EnableUserProfileDisk should not call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { -# Set-TargetResource -CollectionName $collectionName -ActiveSessionLimitMin 1 -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly -Scope It -# } - -# It 'Running Set with EnableUserProfileDisk disabled should call Set-RDSessionCollectionConfiguration with DisableUserProfileDisk' { -# Set-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false -# Assert-MockCalled -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $DisableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It -# } -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { -# Mock -CommandName Set-RDSessionCollectionConfiguration -MockWith { -# $null -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# } -# } - -# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# CustomRdpProperty = "use redirection server name:i:1`n" -# } -# } - -# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# IncludeFolderPath = $null -# ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') -# IncludeFilePath = $null -# ExcludeFilePath = $null -# DiskPath = 'c:\temp' -# EnableUserProfileDisk = $false -# MaxUserProfileDiskSizeGB = 5 -# } -# } -ParameterFilter { $UserProfileDisk -eq $true } - -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Test-TargetResource -CollectionName $testInvalidCollectionName } ` -# | Should throw -# } -# } - -# Context 'Running on test on Windows Server 2012 (R2)' { -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'6.3.9600.0' -# } - -# It 'Running on Windows Server 2012 (R2) with EnableUserProfile disk set to True should ignore the EnableUserProfile property (Test returns True - In Desired State)' { -# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $True -# } -# } - -# Context 'Running on test on Windows Server 2016 (or higher)' { -# Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { -# [version]'10.0.14393.0' -# } - -# It 'Running on Windows Server 2016+ with EnableUserProfile disk set to True and current setting set to false should return Test result False - Not In Desired State' { -# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $true | Should be $false -# } - -# It 'Running on Windows Server 2016+ with EnableUserProfile disk set to False and current setting set to false should return Test result True - In Desired State' { -# Test-TargetResource -CollectionName $collectionName -EnableUserProfileDisk $false | Should be $True -# } - -# It 'Running on Windows Server 2016+ with CustomRdpProperties specified and existing setting matching with a trailing newline should return Test result True - In Desired State' { -# Test-TargetResource -CollectionName $collectionName -CustomRdpProperty 'use redirection server name:i:1' | -# Should be $true -# } - -# It 'Running on Windows Server 2016+ with out-of-order ExcludeFolderPath values and current EnableUserProfile setting set to true should return Test result True - In Desired State' { -# Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection' -# EnableUserProfileDisk = $true -# ExcludeFolderPath = @('c:\temp\foo', 'c:\temp\bar') -# } -# } -ParameterFilter { $UserProfileDisk -eq $true } - -# $testTargetSplat = @{ -# CollectionName = $collectionName -# EnableUserProfileDisk = $true -# ExcludeFolderPath = @('c:\temp\bar', 'c:\temp\foo') -# } -# Test-TargetResource @testTargetSplat | Should be $true -# } -# } -# } -# #endregion -# } -# } -# finally -# { -# Invoke-TestCleanup -# } +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDSessionCollectionConfiguration' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDSessionCollectionConfiguration\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the resource is present' { + BeforeDiscovery { + $userProfileProperties = @( + @{ + Property = 'DiskPath' + } + @{ + Property = 'EnableUserProfileDisk' + } + @{ + Property = 'ExcludeFilePath' + } + @{ + Property = 'ExcludeFolderPath' + } + @{ + Property = 'IncludeFilePath' + } + @{ + Property = 'IncludeFolderPath' + } + @{ + Property = 'MaxUserProfileDiskSizeGB' + } + ) + } + + BeforeAll { + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = "use redirection server name:i:1`n" + } + } + + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = 0 + ClientPrinterRedirected = 0 + MaxRedirectedMonitors = 16 + RDEasyPrintDriverEnabled = 0 + } + } -ParameterFilter { $Client -eq $true } + + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + ActiveSessionLimitMin = 0 + AutomaticReconnectionEnabled = $true + BrokenConnectionAction = 'Disconnect' + DisconnectedSessionLimitMin = 120 + IdleSessionLimitMin = 480 + TemporaryFoldersDeletedOnExit = $true + } + } -ParameterFilter { $Connection -eq $true } + + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + AuthenticateUsingNLA = $true + EncryptionLevel = 'High' + SecurityLayer = 'SSL' + } + } -ParameterFilter { $Security -eq $true } + + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + UserGroup = @('Domain\Group1', 'Domain\Group2') + } + } -ParameterFilter { $UserGroup -eq $true } + + Mock -CommandName Get-RDSessionCollectionConfiguration -MockWith { + @{ + CollectionName = 'TestCollection' + IncludeFolderPath = $null + ExcludeFolderPath = $null + IncludeFilePath = $null + ExcludeFilePath = $null + DiskPath = 'c:\temp' + EnableUserProfileDisk = $true + MaxUserProfileDiskSizeGB = 5 + } + } -ParameterFilter { $UserProfileDisk -eq $true } + } + + Context 'When the server is ''Windows Server 2012 (R2)''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'6.3.9600.0' + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:result = Get-TargetResource -CollectionName 'TestCollection' + + $result.CollectionName | Should -Be 'TestCollection' + $result.CollectionDescription | Should -Be 'Test Description' + $result.CustomRdpProperty | Should -Be 'use redirection server name:i:1' + + $result.ClientDeviceRedirectionOptions | Should -Be 'None' + $result.ClientPrinterAsDefault | Should -Be 0 + $result.ClientPrinterRedirected | Should -Be 0 + $result.MaxRedirectedMonitors | Should -Be 16 + $result.RDEasyPrintDriverEnabled | Should -BeFalse + + $result.ActiveSessionLimitMin | Should -Be 0 + $result.AutomaticReconnectionEnabled | Should -BeTrue + $result.BrokenConnectionAction | Should -Be 'Disconnect' + $result.DisconnectedSessionLimitMin | Should -Be 120 + $result.IdleSessionLimitMin | Should -Be 480 + $result.TemporaryFoldersDeletedOnExit | Should -BeTrue + + $result.AuthenticateUsingNLA | Should -BeTrue + $result.EncryptionLevel | Should -Be 'High' + $result.SecurityLayer | Should -Be 'SSL' + + $result.UserGroup | Should -Be @('Domain\Group1', 'Domain\Group2') + } + + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Client -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Connection -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Security -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserGroup -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Exactly -Times 0 -Scope It + } + + It 'Should not return the UserProfileDisk property ''''' -ForEach $userProfileProperties { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:result.ContainsKey($Property) | Should -BeFalse + } + } + } + + Context 'When the server is ''Windows Server 2016 or later''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'10.0.14393.0' + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:result = Get-TargetResource -CollectionName 'TestCollection' + + $result.CollectionName | Should -Be 'TestCollection' + $result.CollectionDescription | Should -Be 'Test Description' + $result.CustomRdpProperty | Should -Be 'use redirection server name:i:1' + + $result.ClientDeviceRedirectionOptions | Should -Be 'None' + $result.ClientPrinterAsDefault | Should -Be 0 + $result.ClientPrinterRedirected | Should -Be 0 + $result.MaxRedirectedMonitors | Should -Be 16 + $result.RDEasyPrintDriverEnabled | Should -BeFalse + + $result.ActiveSessionLimitMin | Should -Be 0 + $result.AutomaticReconnectionEnabled | Should -BeTrue + $result.BrokenConnectionAction | Should -Be 'Disconnect' + $result.DisconnectedSessionLimitMin | Should -Be 120 + $result.IdleSessionLimitMin | Should -Be 480 + $result.TemporaryFoldersDeletedOnExit | Should -BeTrue + + $result.AuthenticateUsingNLA | Should -BeTrue + $result.EncryptionLevel | Should -Be 'High' + $result.SecurityLayer | Should -Be 'SSL' + + $result.UserGroup | Should -Be @('Domain\Group1', 'Domain\Group2') + } + + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Client -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Connection -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $Security -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserGroup -eq $true } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollectionConfiguration -ParameterFilter { $UserProfileDisk -eq $true } -Exactly -Times 1 -Scope It + } + + It 'Should return the UserProfileDisk property ''''' -ForEach $userProfileProperties { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:result.ContainsKey($Property) | Should -BeTrue + } + } + } + } +} + +Describe 'MSFT_xRDSessionCollectionConfiguration\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the session collection does not exist' { + BeforeAll { + Mock -CommandName Get-RDSessionCollection -MockWith { + throw 'No session collection was found.' + } + } + + It 'Should throw an exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'NonExistingCollection' + } + + { Set-TargetResource @testParams } | Should -Throw + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + } + } + + Context 'When the session collection exists' { + BeforeAll { + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Set-RDSessionCollectionConfiguration + } + + Context 'When the server is ''Windows Server 2012 (R2)''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'6.3.9600.0' + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 5 + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $DiskPath } -Exactly -Times 1 -Scope It + } + } + + Context 'When the server is ''Windows Server 2016 or later''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'10.0.14393.0' + } + } + + Context 'When ''UserProfileDisk'' is enabled' { + Context 'When ''DiskPath'' is invalid' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { + return $false + } + } + + It 'Should throw the correct exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 5 + } + + $errorRecord = Get-InvalidArgumentRecord -ArgumentName 'DiskPath' -Message ('To enable UserProfileDisk we need a valid DiskPath. Path {0} not found' -f $testParams.DiskPath) + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorRecord.Exception.Message + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $EnableUserProfileDisk } -Exactly -Times 1 -Scope It + } + } + + Context 'When ''DiskPath'' is not provided' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { + return $true + } + } + + It 'Should throw the correct exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + MaxUserProfileDiskSizeGB = 5 + } + + $errorRecord = Get-InvalidArgumentRecord -ArgumentName 'DiskPath' -Message 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorRecord.Exception.Message + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $EnableUserProfileDisk } -Exactly -Times 1 -Scope It + } + } + + Context 'When ''MaxUserProfileDiskSizeGB'' is invalid' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { + return $true + } + } + + It 'Should throw the correct exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 0 + } + + $errorRecord = Get-InvalidArgumentRecord -ArgumentName 'MaxUserProfileDiskSizeGB' -Message ( + 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value {0} is not valid' -f $testParams.MaxUserProfileDiskSizeGB + ) + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorRecord.Exception.Message + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $EnableUserProfileDisk } -Exactly -Times 1 -Scope It + } + } + + Context 'When ''EnableUserProfileDisk'' configuration is updated' { + BeforeAll { + Mock -CommandName Test-Path -MockWith { + return $true + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 5 + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $EnableUserProfileDisk } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Exactly -Times 1 -Scope It + } + } + } + + Context 'When ''UserProfileDisk'' is disabled' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $false + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $null -eq $DisableUserProfileDisk } -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $DisableUserProfileDisk -eq $true } -Exactly -Times 1 -Scope It + } + } + } + } + + # Context 'Running on set on Windows Server 2016 (or higher)' { + # Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + # [version]'10.0.14393.0' + # } + + # Mock -CommandName Get-RDSessionCollection -MockWith { + # throw 'No session collection DoesNotExist was found.' + # } + + # It 'Trying to configure a non existing collection should throw' { + # $errorMessages = try + # { + # Set-TargetResource -CollectionName 'DoesNotExist' -ActiveSessionLimitMin 1 + # } + # catch + # { + # $_ 2>&1 + # } + + # $errorMessages.Exception.Message | Should -Be 'Failed to lookup RD Session Collection DoesNotExist. Error: No session collection DoesNotExist was found.' + # } + + # Mock -CommandName Get-RDSessionCollection -MockWith { $true } + # It 'Running Set on W2016 with only EnableUserProfileDisk specified should throw on missing DiskPath parameter' { + # $errorMessages = try + # { + # Set-TargetResource -CollectionName 'TestCollection' -EnableUserProfileDisk $true + # } + # catch + # { + # $_ 2>&1 + # } + + # $errorMessages.Exception.Message | Should -Be 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' + # } + + # It 'Running Set on W2016 with EnableUserProfileDisk and Diskpath specified should throw on invalid MaxUserProfileDiskSizeGB parameter' { + # $errorMessages = try + # { + # Set-TargetResource -CollectionName 'TestCollection' -EnableUserProfileDisk $true -DiskPath TestDrive:\ + # } + # catch + # { + # $_ 2>&1 + # } + + # $errorMessages.Exception.Message | Should -Be 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value 0 is not valid' + # } + + # It 'Running Set with EnableUserProfileDisk, DiskPath and MaxUserProfileDiskSizeGB, but with an invalid DiskPath, should throw' { + # $errorMessages = try + # { + # Set-TargetResource -CollectionName 'TestCollection' -EnableUserProfileDisk $true -DiskPath TestDrive:\NonExistingPath -MaxUserProfileDiskSizeGB 5 + # } + # catch + # { + # $_ 2>&1 + # } + + # $errorMessages.Exception.Message | Should -Be 'To enable UserProfileDisk we need a valid DiskPath. Path TestDrive:\NonExistingPath not found' + # } + + # It 'Running Set with all valid parameters should call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { + # Set-TargetResource -CollectionName 'TestCollection' -EnableUserProfileDisk $true -DiskPath TestDrive:\ -MaxUserProfileDiskSizeGB 5 + # Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It + # } + + # It 'Running Set without EnableUserProfileDisk should not call Set-RDSessionCollectionConfiguration with EnableUserProfileDisk' { + # Set-TargetResource -CollectionName 'TestCollection' -ActiveSessionLimitMin 1 + # Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $EnableUserProfileDisk -eq $true } -Times 0 -Exactly -Scope It + # } + + # It 'Running Set with EnableUserProfileDisk disabled should call Set-RDSessionCollectionConfiguration with DisableUserProfileDisk' { + # Set-TargetResource -CollectionName 'TestCollection' -EnableUserProfileDisk $false + # Should -Invoke -CommandName Set-RDSessionCollectionConfiguration -ParameterFilter { $DisableUserProfileDisk -eq $true } -Times 1 -Exactly -Scope It + # } + # } +} + +Describe 'MSFT_xRDSessionCollectionConfiguration\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is not in the desired state' { + Context 'When the server is ''Windows Server 2012 (R2)''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'6.3.9600.0' + } + + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $true + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the server is ''Windows Server 2016 or later''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'10.0.14393.0' + } + + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 5 + } + } + } + + Context 'When user profile is enabled' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $true + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When user profile is disabled' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $true + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $false + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When the resource is in the desired state' { + Context 'When the server is ''Windows Server 2012 (R2)''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'6.3.9600.0' + } + + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the server is ''Windows Server 2016 or later''' { + BeforeAll { + Mock -CommandName Get-xRemoteDesktopSessionHostOsVersion -MockWith { + [version]'10.0.14393.0' + } + } + + Context 'When user profile is enabled' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + DiskPath = 'C:\UserProfiles' + MaxUserProfileDiskSizeGB = 5 + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $true + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When user profile is disabled' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $false + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + CollectionDescription = 'Test Description' + CustomRdpProperty = 'use redirection server name:i:0' + + ClientDeviceRedirectionOptions = 'None' + ClientPrinterAsDefault = $true + ClientPrinterRedirected = $true + MaxRedirectedMonitors = 8 + RDEasyPrintDriverEnabled = $true + + ActiveSessionLimitMin = 60 + AutomaticReconnectionEnabled = $false + BrokenConnectionAction = 'LogOff' + DisconnectedSessionLimitMin = 60 + IdleSessionLimitMin = 300 + TemporaryFoldersDeletedOnExit = $false + + AuthenticateUsingNLA = $false + EncryptionLevel = 'Low' + SecurityLayer = 'RDP' + + UserGroup = @('Domain\Group1') + + EnableUserProfileDisk = $false + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-xRemoteDesktopSessionHostOsVersion -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + } +} From 5fd79ad57f57f75e627cb0f4fc824d7521267b78 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:47:21 +0100 Subject: [PATCH 18/44] Fix HQRM --- .../MSFT_xRDSessionCollectionConfiguration.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 index c87a2bb..70e4b54 100644 --- a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 @@ -329,7 +329,7 @@ function Set-TargetResource { if (-not(Test-Path -Path $DiskPath -ErrorAction SilentlyContinue)) { - New-InvalidArgumentException -ArgumentName 'DiskPath' -Message ('To enable UserProfileDisk we need a valid DiskPath. Path {0} not found' -f $DiskPath) + New-ArgumentException -ArgumentName 'DiskPath' -Message ('To enable UserProfileDisk we need a valid DiskPath. Path {0} not found' -f $DiskPath) } else { @@ -338,7 +338,7 @@ function Set-TargetResource } else { - New-InvalidArgumentException -ArgumentName 'DiskPath' -Message 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' + New-ArgumentException -ArgumentName 'DiskPath' -Message 'No value found for parameter DiskPath. This is a mandatory parameter if EnableUserProfileDisk is set to True' } if ($MaxUserProfileDiskSizeGB -gt 0) @@ -347,7 +347,7 @@ function Set-TargetResource } else { - New-InvalidArgumentException -ArgumentName 'MaxUserProfileDiskSizeGB' -Message ( + New-ArgumentException -ArgumentName 'MaxUserProfileDiskSizeGB' -Message ( 'To enable UserProfileDisk we need a setting for MaxUserProfileDiskSizeGB that is greater than 0. Current value {0} is not valid' -f $MaxUserProfileDiskSizeGB ) } From 6ad0831a7616680ae78847b57cd18abbefde894f Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 22 Oct 2025 17:42:13 +0100 Subject: [PATCH 19/44] SessionCollection WIP --- .../MSFT_xRDSessionCollection.psm1 | 104 ++- .../Unit/MSFT_xRDSessionCollection.Tests.ps1 | 860 +++++++++--------- 2 files changed, 504 insertions(+), 460 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 index 0ed2e9d..f69a987 100644 --- a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -22,17 +20,30 @@ function Get-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [bool] $Force + [System.Boolean] + $Force ) + + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose -Message 'Getting information about RDSH collection.' + $params = @{ ConnectionBroker = $ConnectionBroker CollectionName = $CollectionName @@ -46,23 +57,23 @@ function Get-TargetResource if ($Collection.Count -eq 0) { return @{ + CollectionName = $null ConnectionBroker = $null CollectionDescription = $null - CollectionName = $null - SessionHost = $SessionHost + SessionHost = [System.String[]] $SessionHost Force = $Force } } if ($Collection.Count -gt 1) { - throw 'Non-singular RDSessionCollection in result set' + New-InvalidResultException -Message 'Non-singular RDSessionCollection in result set' } return @{ + CollectionName = $Collection.CollectionName ConnectionBroker = $ConnectionBroker CollectionDescription = $Collection.CollectionDescription - CollectionName = $Collection.CollectionName SessionHost = [System.String[]] (Get-RDSessionHost -CollectionName $CollectionName -ConnectionBroker $ConnectionBroker -ErrorAction SilentlyContinue).SessionHost Force = $Force } @@ -78,18 +89,30 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [bool] $Force + [System.Boolean] + $Force ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + $currentStatus = Get-TargetResource @PSBoundParameters + if ($null -ne $currentStatus.CollectionName -and $Force) { Write-Verbose -Message "Session collection $CollectionName already exists. Updating Session Hosts." @@ -139,7 +162,8 @@ function Set-TargetResource { $exception = [System.Management.Automation.RuntimeException]::new($exceptionString) } - throw [System.Management.Automation.ErrorRecord]::new($exception, 'Failure to coerce resource into the desired state', [System.Management.Automation.ErrorCategory]::InvalidResult, $CollectionName) + + $PSCmdlet.ThrowTerminatingError([System.Management.Automation.ErrorRecord]::new($exception, 'Failure to coerce resource into the desired state', [System.Management.Automation.ErrorCategory]::InvalidResult, $CollectionName)) } } @@ -154,40 +178,36 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string[]] $SessionHost, + [System.String[]] + $SessionHost, + [Parameter()] - [string] $CollectionDescription, + [System.String] + $CollectionDescription, + [Parameter(Mandatory = $true)] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, + [Parameter()] - [bool] $Force + [System.Boolean] + $Force ) Write-Verbose 'Checking for existence of RDSH collection.' - $currentStatus = Get-TargetResource @PSBoundParameters - if ($null -eq $currentStatus.CollectionName) - { - Write-Verbose -Message "No collection $CollectionName found" - return $false - } - - if ($null -eq $currentStatus.SessionHost) - { - Write-Verbose -Message "No session host(s) found in collection $CollectionName" - return (-not $Force) - } - - $compare = Compare-Object -ReferenceObject $SessionHost -DifferenceObject $currentStatus.SessionHost - if ($null -ne $compare -and $Force) - { - Write-Verbose -Message "Desired list of session hosts not equal`r`n$($compare | Out-String) and Force is true" - return $false - } + $desiredState = $PSBoundParameters + $currentState = Get-TargetResource @PSBoundParameters - return $true + return Test-DscParameterState ` + -CurrentValues $currentState ` + -DesiredValues $desiredState ` + -SortArrayValues ` + -Verbose:$VerbosePreference } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 index e697dca..549d500 100644 --- a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 @@ -1,427 +1,451 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDSessionCollection' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDSessionCollection' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +# $testcollectionNameMulti = 'TestCollectionMulti' + +# $testCollection = @( +# @{ +# Name = 'TestCollection1' +# Description = 'Test Collection 1' # } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' +# @{ +# Name = 'TestCollection2' +# Description = 'Test Collection 2' # } +# ) -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' +# $testSessionHost = 'localhost' +# $testSessionHostMulti = 'rdsh1', 'rdsh2', 'rdsh3' +# $testConnectionBroker = 'localhost.fqdn' + +# $validTargetResourceCall = @{ +# CollectionName = $testCollection[0].Name +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker # } -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# $nonExistentTargetResourceCall1 = @{ +# CollectionName = 'TestCollection4' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker # } -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDSessionCollection' - -# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' -# $testcollectionNameMulti = 'TestCollectionMulti' - -# $testCollection = @( -# @{ -# Name = 'TestCollection1' -# Description = 'Test Collection 1' -# } -# @{ -# Name = 'TestCollection2' -# Description = 'Test Collection 2' -# } -# ) - -# $testSessionHost = 'localhost' -# $testSessionHostMulti = 'rdsh1', 'rdsh2', 'rdsh3' -# $testConnectionBroker = 'localhost.fqdn' - -# $validTargetResourceCall = @{ -# CollectionName = $testCollection[0].Name -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } - -# $nonExistentTargetResourceCall1 = @{ -# CollectionName = 'TestCollection4' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } - -# $nonExistentTargetResourceCall2 = @{ -# CollectionName = 'TestCollection5' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# $validMultiTargetResourceCall = @{ -# CollectionName = $testcollectionNameMulti -# SessionHost = $testSessionHostMulti -# ConnectionBroker = $testConnectionBroker -# } -# $invalidMultiTargetResourceCall = @{ -# CollectionName = $testcollectionNameMulti -# SessionHost = $testSessionHostMulti | Select-Object -Skip 1 -# ConnectionBroker = $testConnectionBroker -# } - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { -# Mock -CommandName Get-RDSessionCollection { -# return @( -# { -# CollectionName = $testCollection[0].Name -# CollectionDescription = 'Test Collection' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# }, -# { -# CollectionName = $testCollectionName2 -# CollectionDescription = 'Test Collection 2' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# ) -# } -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollection[0].Name -# SessionHost = $testSessionHost -# } -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $testSessionHostMulti -# } -# } - -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Get-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw -# } - -# Mock -CommandName Get-RDSessionCollection { -# $result = @() - -# foreach ($sessionCollection in $testCollection) -# { -# $result += New-Object -TypeName PSObject -Property @{ -# CollectionName = $sessionCollection.Name -# CollectionDescription = $sessionCollection.Description -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# } - -# return $result -# } - -# It 'Calls Get-RDSessionCollection with CollectionName and ConnectionBroker parameters' { -# Get-TargetResource @validTargetResourceCall -# Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope It -ParameterFilter { -# $CollectionName -eq $testCollection[0].Name -and -# $ConnectionBroker -eq $testConnectionBroker -# } -# } - -# It 'Calls Get-RDSessionHost' { -# Get-TargetResource @validTargetResourceCall -# Assert-MockCalled -CommandName Get-RDSessionHost -Times 1 -Scope It -ParameterFilter { -# $CollectionName -eq $testCollection[0].Name -and -# $ConnectionBroker -eq $testConnectionBroker -# } -# } -# } - -# Context 'Non-existent Session Collection requested (other session collections returned - Win2019 behaviour)' { - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# [pscustomobject]@{ -# CollectionName = 'TestCollection3' -# CollectionDescription = 'Test Collection 3' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# } - -# $result = Get-TargetResource @nonExistentTargetResourceCall1 -# It 'Should return return a hash table' { -# $result | Should -BeOfType System.Collections.Hashtable -# } - -# It 'Should return supplied session host, with other values being $null' { -# $result.ConnectionBroker = $null -# $result.CollectionName = $null -# $result.CollectionDescription = $null -# $result.SessionHost = $testSessionHost -# } -# } - -# Context 'Non-existent Session Collection requested (no session collections returned - normal behaviour)' { - -# Mock -CommandName Get-RDSessionCollection { -# return $null -# } - -# $result = Get-TargetResource @nonExistentTargetResourceCall2 -# It 'Should return return a hash table (Win 2019)' { -# $result | Should -BeOfType System.Collections.Hashtable -# } - -# It 'Should return supplied session host, with other values being $null' { -# $result.ConnectionBroker = $null -# $result.CollectionName = $null -# $result.CollectionDescription = $null -# $result.SessionHost = $testSessionHost -# } -# } - - -# Context 'Two Session Collections exist with same CollectionName' { -# Mock -CommandName Get-RDSessionCollection { -# $result = @() - -# foreach ($sessionCollection in $testCollection) -# { -# $result += New-Object -TypeName PSObject -Property @{ -# CollectionName = $testCollection[0].Name -# CollectionDescription = $sessionCollection.Description -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# } - -# return $result -# } - -# It 'should throw exception' { -# { Get-TargetResource @validTargetResourceCall } | Should throw -# } -# } - -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { - -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Set-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw -# } -# } - -# Mock -CommandName Get-RDSessionCollection -# Mock -CommandName New-RDSessionCollection -# Mock -CommandName Compare-Object -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollection[0].Name -# SessionHost = $testSessionHost -# } -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $testSessionHostMulti -# } -# } - -# Context 'Validate Set-TargetResource actions' { -# It 'Given the configuration is applied, New-RDSessionCollection is called' { -# Mock -CommandName Test-TargetResource -MockWith { return $true } -# Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost -Verbose -# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context -# } -# } -# Context 'New-RDSessionCollection returns an exception, but creates the desired RDSessionCollection' { -# Mock -CommandName Test-TargetResource -MockWith { return $true } -# Mock -CommandName Compare-Object -# Mock -CommandName New-RDSessionCollection -MockWith { -# throw [Microsoft.PowerShell.Commands.WriteErrorException] 'The property EncryptionLevel is configured by using Group Policy settings. Use the Group Policy Management Console to configure this property.' -# } - -# It 'does not return an exception' { -# { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Not -Throw -# } - -# It 'calls New-RDSessionCollection' { -# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context -# } -# } - -# Context 'Get-RDSessionCollection returns an exception, without creating the desired RDSessionCollection' { -# Mock -CommandName New-RDSessionCollection -MockWith { -# throw [Microsoft.PowerShell.Commands.WriteErrorException] "A Remote Desktop Services deployment does not exist on $testConnectionBroker. This operation can be performed after creating a deployment. For information about creating a deployment, run `"Get-Help New-RDVirtualDesktopDeployment`" or `"Get-Help New-RDSessionDeployment`"" -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# $null -# } - -# It 'returns an exception' { -# { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Throw -# } - -# It 'calls New-RDSessionCollection and Get-RDSessionCollection' { -# Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context -# Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope Describe -# } -# } - -# Context 'Session Collection exists, but list of session hosts is different' { -# Mock -CommandName Get-TargetResource -MockWith { -# @{ -# 'ConnectionBroker' = 'CB' -# 'CollectionDescription' = 'Description' -# 'CollectionName' = 'ExistingCollection' -# 'SessionHost' = 'SurplusHost' -# } -# } -# Mock -CommandName Compare-Object -MockWith { -# 'SurplusHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '<=' -PassThru -# 'MissingHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '=>' -PassThru -# } -# Mock -CommandName Add-RDSessionHost -# Mock -CommandName Remove-RDSessionHost - -# It 'calls Add and Remove-RDSessionHost' { -# Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true -# Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context -# Assert-MockCalled -CommandName Remove-RDSessionHost -Times 1 -Scope Context -# } - -# Mock -CommandName Get-TargetResource -MockWith { -# @{ -# 'ConnectionBroker' = 'CB' -# 'CollectionDescription' = 'Description' -# 'CollectionName' = 'ExistingCollection' -# 'SessionHost' = $null -# } -# } -# It 'calls Add-RDSessionHost if no session hosts exist' { -# Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true -# Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context -# } -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Test-TargetResource -CollectionName $testInvalidCollectionName -SessionHost $testSessionHost } | Should throw -# } -# } - -# Context 'Validating Test-TargetResource output' { -# Mock -CommandName Get-RDSessionCollection -# Mock -CommandName Get-RDSessionHost - -# It 'Given Get-RDSessionCollection not returning a collection, test returns false' { -# Test-TargetResource @validTargetResourceCall | Should Be $false -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# [pscustomobject]@{ -# AutoAssignPersonalDesktop = $false -# CollectionAlias = $testCollection[0].Name -# CollectionDescription = 'Pester Test Collection Output' -# CollectionName = $testCollection[0].Name -# CollectionType = 'PooledUnmanaged' -# GrantAdministrativePrivilege = $false -# ResourceType = 'Remote Desktop' -# Size = 1 -# } -# } -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollection[0].Name -# SessionHost = $testSessionHost -# } -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $testSessionHostMulti -# } -# } - -# It 'Given Get-RDSessionCollection returning a collection, test returns true' { -# Test-TargetResource @validTargetResourceCall | Should Be $true -# } - -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $testSessionHostMulti -# } -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# [pscustomobject]@{ -# AutoAssignPersonalDesktop = $false -# CollectionAlias = $testCollectionNameMulti -# CollectionDescription = 'Pester Test Collection Output' -# CollectionName = $testCollectionNameMulti -# CollectionType = 'PooledUnmanaged' -# GrantAdministrativePrivilege = $false -# ResourceType = 'Remote Desktop' -# Size = 1 -# } -# } - -# It 'Given the incorrect number of session hosts it should return false' { -# Test-TargetResource @invalidMultiTargetResourceCall -Verbose -Force $true | Should Be $false -# } - -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $testSessionHostMulti -# } -# } - -# Mock -CommandName Get-RDSessionHost { -# return @{ -# CollectionName = $testCollection[0].Name -# } -# return @{ -# CollectionName = $testCollectionNameMulti -# SessionHost = $invalidTestSessionHostMulti -# } -# } - -# It 'Given an empty collection of session hosts without the force it should return true' { -# Test-TargetResource @invalidMultiTargetResourceCall -Verbose | Should Be $true -# } - -# It 'Given an empty collection of session hosts with the force it should return false' { -# Test-TargetResource @invalidMultiTargetResourceCall -Force $true -Verbose | Should Be $false -# } - -# It 'Given the list of session hosts is not equal, return false' { -# Test-TargetResource @validMultiTargetResourceCall -Force $true | Should Be $false -# } -# } -# } -# #endregion -# } +# $nonExistentTargetResourceCall2 = @{ +# CollectionName = 'TestCollection5' +# SessionHost = $testSessionHost +# ConnectionBroker = $testConnectionBroker # } -# finally -# { -# Invoke-TestCleanup +# $validMultiTargetResourceCall = @{ +# CollectionName = $testcollectionNameMulti +# SessionHost = $testSessionHostMulti +# ConnectionBroker = $testConnectionBroker # } +# $invalidMultiTargetResourceCall = @{ +# CollectionName = $testcollectionNameMulti +# SessionHost = $testSessionHostMulti | Select-Object -Skip 1 +# ConnectionBroker = $testConnectionBroker +# } + +Describe 'MSFT_xRDSessionCollection\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the resource is present' { + BeforeAll { + Mock -CommandName Get-RDSessionCollection -MockWith { + return @( + [PSCustomObject] @{ + CollectionName = 'TestCollection1' + CollectionDescription = 'Test Collection 1' + } + [PSCustomObject] @{ + CollectionName = 'TestCollection2' + CollectionDescription = 'Test Collection 2' + } + [PSCustomObject] @{ + CollectionName = 'TestCollection2' + CollectionDescription = 'Test Collection 2' + } + ) + } + } + + Context 'When more than one match is returned' { + It 'Should throw the correct exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection2' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 2' + ConnectionBroker = 'localhost.fqdn' + } + + $errorRecord = Get-InvalidResultRecord -Message 'Non-singular RDSessionCollection in result set' + + { Get-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorRecord.Exception.Message + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + } + } + + Context 'When only one match is returned' { + BeforeAll { + Mock -CommandName Get-RDSessionHost -MockWith { + return @( + @{ + SessionHost = 'localhost' + } + ) + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + $result = Get-TargetResource @testParams + + $result.CollectionName | Should -Be $testParams.CollectionName + $result.CollectionDescription | Should -Be $testParams.CollectionDescription + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.SessionHost | Should -Be $testParams.SessionHost + $result.Force | Should -Be $testParams.Force + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionHost -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the resource is present' { + Context 'When no matches are returned (default behavior)' { + BeforeAll { + Mock -CommandName Get-RDSessionCollection + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + $result = Get-TargetResource @testParams + + $result.CollectionName | Should -BeNullOrEmpty + $result.CollectionDescription | Should -BeNullOrEmpty + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.SessionHost | Should -Be $testParams.SessionHost + $result.Force | Should -Be $testParams.Force + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + } + } + + Context 'When non matching collections are returned (2019 behavior)' { + BeforeAll { + Mock -CommandName Get-RDSessionCollection -MockWith { + return @( + [PSCustomObject] @{ + CollectionName = 'TestCollection2' + CollectionDescription = 'Test Collection 2' + } + [PSCustomObject] @{ + CollectionName = 'TestCollection3' + CollectionDescription = 'Test Collection 3' + } + ) + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + $result = Get-TargetResource @testParams + + $result.CollectionName | Should -BeNullOrEmpty + $result.CollectionDescription | Should -BeNullOrEmpty + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.SessionHost | Should -Be $testParams.SessionHost + $result.Force | Should -Be $testParams.Force + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + } + } + } +} + +Describe 'MSFT_xRDSessionCollection\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Assert-Module + } + + + # Mock -CommandName Get-RDSessionCollection + # Mock -CommandName New-RDSessionCollection + # Mock -CommandName Compare-Object + # Mock -CommandName Get-RDSessionHost { + # return @{ + # CollectionName = $testCollection[0].Name + # SessionHost = $testSessionHost + # } + # return @{ + # CollectionName = $testCollectionNameMulti + # SessionHost = $testSessionHostMulti + # } + # } + + # Context 'Validate Set-TargetResource actions' { + # It 'Given the configuration is applied, New-RDSessionCollection is called' { + # Mock -CommandName Test-TargetResource -MockWith { return $true } + # Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost -Verbose + # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context + # } + # } + # Context 'New-RDSessionCollection returns an exception, but creates the desired RDSessionCollection' { + # Mock -CommandName Test-TargetResource -MockWith { return $true } + # Mock -CommandName Compare-Object + # Mock -CommandName New-RDSessionCollection -MockWith { + # throw [Microsoft.PowerShell.Commands.WriteErrorException] 'The property EncryptionLevel is configured by using Group Policy settings. Use the Group Policy Management Console to configure this property.' + # } + + # It 'does not return an exception' { + # { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Not -Throw + # } + + # It 'calls New-RDSessionCollection' { + # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context + # } + # } + + # Context 'Get-RDSessionCollection returns an exception, without creating the desired RDSessionCollection' { + # Mock -CommandName New-RDSessionCollection -MockWith { + # throw [Microsoft.PowerShell.Commands.WriteErrorException] "A Remote Desktop Services deployment does not exist on $testConnectionBroker. This operation can be performed after creating a deployment. For information about creating a deployment, run `"Get-Help New-RDVirtualDesktopDeployment`" or `"Get-Help New-RDSessionDeployment`"" + # } + + # Mock -CommandName Get-RDSessionCollection -MockWith { + # $null + # } + + # It 'returns an exception' { + # { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Throw + # } + + # It 'calls New-RDSessionCollection and Get-RDSessionCollection' { + # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context + # Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope Describe + # } + # } + + # Context 'Session Collection exists, but list of session hosts is different' { + # Mock -CommandName Get-TargetResource -MockWith { + # @{ + # 'ConnectionBroker' = 'CB' + # 'CollectionDescription' = 'Description' + # 'CollectionName' = 'ExistingCollection' + # 'SessionHost' = 'SurplusHost' + # } + # } + # Mock -CommandName Compare-Object -MockWith { + # 'SurplusHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '<=' -PassThru + # 'MissingHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '=>' -PassThru + # } + # Mock -CommandName Add-RDSessionHost + # Mock -CommandName Remove-RDSessionHost + + # It 'calls Add and Remove-RDSessionHost' { + # Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true + # Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context + # Assert-MockCalled -CommandName Remove-RDSessionHost -Times 1 -Scope Context + # } + + # Mock -CommandName Get-TargetResource -MockWith { + # @{ + # 'ConnectionBroker' = 'CB' + # 'CollectionDescription' = 'Description' + # 'CollectionName' = 'ExistingCollection' + # 'SessionHost' = $null + # } + # } + # It 'calls Add-RDSessionHost if no session hosts exist' { + # Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true + # Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context + # } + # } +} + +Describe 'MSFT_xRDSessionCollection\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection1' + SessionHost = [System.String[]] 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the resource is not in the desired state' { + BeforeDiscovery { + $testCases = @( + @{ + ParameterName = 'CollectionName' + DesiredValue = 'TestCollection2' + } + @{ + ParameterName = 'SessionHost' + DesiredValue = [System.String[]] @('rdsh1', 'rdsh2') + } + @{ + ParameterName = 'CollectionDescription' + DesiredValue = 'Test Collection 1 Updated' + } + ) + } + + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection1' + SessionHost = [System.String[]] 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + } + + Context 'When the parameter '''' does not match' -ForEach $testCases { + It 'Should return the correct result' { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = 'localhost' + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + # Override the parameter under test to the desired value + $testParams[$ParameterName] = $DesiredValue + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } +} From da9b4e25d71f5e314980a207905f2bd49546bc23 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:17:29 +0100 Subject: [PATCH 20/44] Test local failure --- .../Unit/MSFT_xRDSessionCollection.Tests.ps1 | 104 +++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 index 549d500..731ae08 100644 --- a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 @@ -268,7 +268,109 @@ Describe 'MSFT_xRDSessionCollection\Set-TargetResource' -Tag 'Set' { Mock -CommandName Assert-Module } - + Context 'When the resource is present' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + CollectionName = 'TestCollection1' + SessionHost = [System.String[]] @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + + Mock -CommandName Add-RDSessionHost + Mock -CommandName Remove-RDSessionHost + } + + Context 'When a session host should be added' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2', 'rdsh3') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDSessionHost -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-RDSessionHost -Exactly -Times 0 -Scope It + } + } + + Context 'When a session host should be removed' { + It 'Should return the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDSessionHost -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Remove-RDSessionHost -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the resource is absent' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + CollectionName = $null + ConnectionBroker = $null + CollectionDescription = $null + SessionHost = [System.String[]] @('rdsh1', 'rdsh2') + Force = $false + } + } + + Mock -CommandName New-RDSessionCollection + } + + Context 'When creating the session collection succeeds' { + BeforeAll { + Mock -CommandName Test-TargetResource -MockWith { return $true } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionCollection -Exactly -Times 1 -Scope It # TODO: this is not passing locally + Should -Invoke -CommandName Test-TargetResource -Exactly -Times 1 -Scope It + } + } + } + # Mock -CommandName Get-RDSessionCollection # Mock -CommandName New-RDSessionCollection # Mock -CommandName Compare-Object From 0d6069e4ed34455508e1596cc37441558e7798b4 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:27:28 +0000 Subject: [PATCH 21/44] Remove use of New-Object --- .../Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 index 64792e9..a827233 100644 --- a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 @@ -1,4 +1,4 @@ function Test-xRemoteDesktopSessionHostOsRequirement { - return (Get-xRemoteDesktopSessionHostOsVersion) -ge (New-Object 'Version' 6, 2, 9200, 0) + return (Get-xRemoteDesktopSessionHostOsVersion) -ge ([Version]::new(6, 2, 9200, 0)) } From 69e1c7c04dee774e6393bc23f49395eb5c4bc072 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:29:09 +0000 Subject: [PATCH 22/44] Migrate RDSessionCollection --- .../MSFT_xRDSessionCollection.psm1 | 12 +- .../Unit/MSFT_xRDSessionCollection.Tests.ps1 | 306 ++++++++---------- 2 files changed, 151 insertions(+), 167 deletions(-) diff --git a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 index f69a987..e3d1192 100644 --- a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 @@ -115,6 +115,7 @@ function Set-TargetResource if ($null -ne $currentStatus.CollectionName -and $Force) { + $missing, $surplus = @() Write-Verbose -Message "Session collection $CollectionName already exists. Updating Session Hosts." if ($null -ne $currentStatus.SessionHost) { @@ -139,10 +140,19 @@ function Set-TargetResource return } + $newCollectionParams = @{ + CollectionName = $CollectionName + CollectionDescription = $CollectionDescription + ConnectionBroker = $ConnectionBroker + SessionHost = $SessionHost + } + + $exception = $null + try { Write-Verbose -Message 'Creating a new RDSH collection.' - New-RDSessionCollection @PSBoundParameters -ErrorAction Stop + New-RDSessionCollection @newCollectionParams -ErrorAction Stop } catch { diff --git a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 index 731ae08..2c2cb63 100644 --- a/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 +++ b/tests/Unit/MSFT_xRDSessionCollection.Tests.ps1 @@ -56,51 +56,6 @@ AfterAll { Get-Module -Name $script:dscResourceName -All | Remove-Module -Force } -# $testcollectionNameMulti = 'TestCollectionMulti' - -# $testCollection = @( -# @{ -# Name = 'TestCollection1' -# Description = 'Test Collection 1' -# } -# @{ -# Name = 'TestCollection2' -# Description = 'Test Collection 2' -# } -# ) - -# $testSessionHost = 'localhost' -# $testSessionHostMulti = 'rdsh1', 'rdsh2', 'rdsh3' -# $testConnectionBroker = 'localhost.fqdn' - -# $validTargetResourceCall = @{ -# CollectionName = $testCollection[0].Name -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } - -# $nonExistentTargetResourceCall1 = @{ -# CollectionName = 'TestCollection4' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } - -# $nonExistentTargetResourceCall2 = @{ -# CollectionName = 'TestCollection5' -# SessionHost = $testSessionHost -# ConnectionBroker = $testConnectionBroker -# } -# $validMultiTargetResourceCall = @{ -# CollectionName = $testcollectionNameMulti -# SessionHost = $testSessionHostMulti -# ConnectionBroker = $testConnectionBroker -# } -# $invalidMultiTargetResourceCall = @{ -# CollectionName = $testcollectionNameMulti -# SessionHost = $testSessionHostMulti | Select-Object -Skip 1 -# ConnectionBroker = $testConnectionBroker -# } - Describe 'MSFT_xRDSessionCollection\Get-TargetResource' -Tag 'Get' { BeforeAll { Mock -CommandName Assert-Module @@ -270,43 +225,93 @@ Describe 'MSFT_xRDSessionCollection\Set-TargetResource' -Tag 'Set' { Context 'When the resource is present' { BeforeAll { - Mock -CommandName Get-TargetResource -MockWith { - return @{ - CollectionName = 'TestCollection1' - SessionHost = [System.String[]] @('rdsh1', 'rdsh2') - CollectionDescription = 'Test Collection 1' - ConnectionBroker = 'localhost.fqdn' - Force = $false - } - } - Mock -CommandName Add-RDSessionHost Mock -CommandName Remove-RDSessionHost } Context 'When a session host should be added' { - It 'Should call the correct mocks' { - InModuleScope -ScriptBlock { - Set-StrictMode -Version 1.0 + Context 'When the session host is not ''$null''' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + CollectionName = 'TestCollection1' + SessionHost = [System.String[]] @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + } - $testParams = @{ - CollectionName = 'TestCollection1' - SessionHost = @('rdsh1', 'rdsh2', 'rdsh3') - CollectionDescription = 'Test Collection 1' - ConnectionBroker = 'localhost.fqdn' - Force = $true + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2', 'rdsh3') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $true + } + + $null = Set-TargetResource @testParams } - $null = Set-TargetResource @testParams + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDSessionHost -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-RDSessionHost -Exactly -Times 0 -Scope It } + } - Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It - Should -Invoke -CommandName Add-RDSessionHost -Exactly -Times 1 -Scope It - Should -Invoke -CommandName Remove-RDSessionHost -Exactly -Times 0 -Scope It + Context 'When the session host is ''$null''' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + CollectionName = 'TestCollection1' + SessionHost = $null + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2', 'rdsh3') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDSessionHost -Exactly -Times 3 -Scope It + Should -Invoke -CommandName Remove-RDSessionHost -Exactly -Times 0 -Scope It + } } } Context 'When a session host should be removed' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + return @{ + CollectionName = 'TestCollection1' + SessionHost = [System.String[]] @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + } + } + It 'Should return the correct mocks' { InModuleScope -ScriptBlock { Set-StrictMode -Version 1.0 @@ -340,16 +345,75 @@ Describe 'MSFT_xRDSessionCollection\Set-TargetResource' -Tag 'Set' { Force = $false } } - - Mock -CommandName New-RDSessionCollection } Context 'When creating the session collection succeeds' { BeforeAll { - Mock -CommandName Test-TargetResource -MockWith { return $true } + Mock -CommandName New-RDSessionCollection + } + + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Test-TargetResource -MockWith { return $true } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Test-TargetResource -Exactly -Times 1 -Scope It + } } - It 'Should call the correct mocks' { + Context 'When the resource is not in the desired state' { + BeforeAll { + Mock -CommandName Test-TargetResource -MockWith { return $false } + } + + It 'Should throw the correct exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection1' + SessionHost = @('rdsh1', 'rdsh2') + CollectionDescription = 'Test Collection 1' + ConnectionBroker = 'localhost.fqdn' + Force = $false + } + + $errorString = ("'Test-TargetResource' returns false after call to 'New-RDSessionCollection'; CollectionName: {0}; ConnectionBroker {1}." -f $testParams.CollectionName, $testParams.ConnectionBroker) + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorString + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Test-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When creating the session collection fails' { + BeforeAll { + Mock -CommandName New-RDSessionCollection -MockWith { throw 'Mock error' } + Mock -CommandName Test-TargetResource -MockWith { return $false } + } + + It 'Should throw the correct exception' { InModuleScope -ScriptBlock { Set-StrictMode -Version 1.0 @@ -361,107 +425,17 @@ Describe 'MSFT_xRDSessionCollection\Set-TargetResource' -Tag 'Set' { Force = $false } - Set-TargetResource @testParams + $errorString = ("'Test-TargetResource' returns false after call to 'New-RDSessionCollection'; CollectionName: {0}; ConnectionBroker {1}." -f $testParams.CollectionName, $testParams.ConnectionBroker) + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage $errorString } Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It - Should -Invoke -CommandName New-RDSessionCollection -Exactly -Times 1 -Scope It # TODO: this is not passing locally + Should -Invoke -CommandName New-RDSessionCollection -Exactly -Times 1 -Scope It Should -Invoke -CommandName Test-TargetResource -Exactly -Times 1 -Scope It } } } - - # Mock -CommandName Get-RDSessionCollection - # Mock -CommandName New-RDSessionCollection - # Mock -CommandName Compare-Object - # Mock -CommandName Get-RDSessionHost { - # return @{ - # CollectionName = $testCollection[0].Name - # SessionHost = $testSessionHost - # } - # return @{ - # CollectionName = $testCollectionNameMulti - # SessionHost = $testSessionHostMulti - # } - # } - - # Context 'Validate Set-TargetResource actions' { - # It 'Given the configuration is applied, New-RDSessionCollection is called' { - # Mock -CommandName Test-TargetResource -MockWith { return $true } - # Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost -Verbose - # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - # } - # } - # Context 'New-RDSessionCollection returns an exception, but creates the desired RDSessionCollection' { - # Mock -CommandName Test-TargetResource -MockWith { return $true } - # Mock -CommandName Compare-Object - # Mock -CommandName New-RDSessionCollection -MockWith { - # throw [Microsoft.PowerShell.Commands.WriteErrorException] 'The property EncryptionLevel is configured by using Group Policy settings. Use the Group Policy Management Console to configure this property.' - # } - - # It 'does not return an exception' { - # { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Not -Throw - # } - - # It 'calls New-RDSessionCollection' { - # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - # } - # } - - # Context 'Get-RDSessionCollection returns an exception, without creating the desired RDSessionCollection' { - # Mock -CommandName New-RDSessionCollection -MockWith { - # throw [Microsoft.PowerShell.Commands.WriteErrorException] "A Remote Desktop Services deployment does not exist on $testConnectionBroker. This operation can be performed after creating a deployment. For information about creating a deployment, run `"Get-Help New-RDVirtualDesktopDeployment`" or `"Get-Help New-RDSessionDeployment`"" - # } - - # Mock -CommandName Get-RDSessionCollection -MockWith { - # $null - # } - - # It 'returns an exception' { - # { Set-TargetResource -CollectionName $testCollection[0].Name -ConnectionBroker $testConnectionBroker -SessionHost $testSessionHost } | Should -Throw - # } - - # It 'calls New-RDSessionCollection and Get-RDSessionCollection' { - # Assert-MockCalled -CommandName New-RDSessionCollection -Times 1 -Scope Context - # Assert-MockCalled -CommandName Get-RDSessionCollection -Times 1 -Scope Describe - # } - # } - - # Context 'Session Collection exists, but list of session hosts is different' { - # Mock -CommandName Get-TargetResource -MockWith { - # @{ - # 'ConnectionBroker' = 'CB' - # 'CollectionDescription' = 'Description' - # 'CollectionName' = 'ExistingCollection' - # 'SessionHost' = 'SurplusHost' - # } - # } - # Mock -CommandName Compare-Object -MockWith { - # 'SurplusHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '<=' -PassThru - # 'MissingHost' | Add-Member -NotePropertyName SideIndicator -NotePropertyValue '=>' -PassThru - # } - # Mock -CommandName Add-RDSessionHost - # Mock -CommandName Remove-RDSessionHost - - # It 'calls Add and Remove-RDSessionHost' { - # Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true - # Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context - # Assert-MockCalled -CommandName Remove-RDSessionHost -Times 1 -Scope Context - # } - - # Mock -CommandName Get-TargetResource -MockWith { - # @{ - # 'ConnectionBroker' = 'CB' - # 'CollectionDescription' = 'Description' - # 'CollectionName' = 'ExistingCollection' - # 'SessionHost' = $null - # } - # } - # It 'calls Add-RDSessionHost if no session hosts exist' { - # Set-TargetResource -CollectionName 'ExistingCollection' -ConnectionBroker 'CB' -SessionHost 'MissingHost' -Force $true - # Assert-MockCalled -CommandName Add-RDSessionHost -Times 1 -Scope Context - # } - # } } Describe 'MSFT_xRDSessionCollection\Test-TargetResource' -Tag 'Test' { From 7a46f465bddfd26a0ffec8837af3dcfc81132cc5 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 29 Oct 2025 17:22:58 +0000 Subject: [PATCH 23/44] Migrate RDServer --- .../MSFT_xRDServer/MSFT_xRDServer.psm1 | 114 ++-- tests/Unit/MSFT_xRDServer.Tests.ps1 | 606 +++++++++++++----- 2 files changed, 502 insertions(+), 218 deletions(-) diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index 217fa96..6ea1b4b 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -9,61 +9,8 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - $localhost = [System.Net.Dns]::GetHostEntry(($env:COMPUTERNAME)).HostName -function ValidateCustomModeParameters -{ - param - ( - [Parameter()] - [ValidateSet('RDS-Connection-Broker', 'RDS-Virtualization', 'RDS-RD-Server', 'RDS-Web-Access', 'RDS-Gateway', 'RDS-Licensing')] - [string] - $Role, - - [Parameter()] - [string] - $GatewayExternalFqdn - ) - - Write-Verbose 'validating parameters...' - - $customParams = @{ - GatewayExternalFqdn = $GatewayExternalFqdn - } - - if ($Role -eq 'RDS-Gateway') - { - # ensure GatewayExternalFqdn was passed in, otherwise 'Add-RDServer' will fail - $emptyBoundParameters = $null - $emptyBoundParameters = $customParams.GetEnumerator() | Where-Object { $_.Value -eq [string]::Empty } - - if ($emptyBoundParameters) - { - $emptyBoundParameters | ForEach-Object { Write-Verbose ">> '$($_.Key)' parameter is empty" } - - Write-Warning "[PARAMETER VALIDATION FAILURE] I'm gonna throw, right now..." - - throw ("Requested server role 'RDS-Gateway', you must pass in the 'GatewayExternalFqdn' parameter.") - } - } - else - { - # give warning about incorrect usage of the resource (do not fail) - - $parametersWithValues = $customParams.getenumerator() | Where-Object { $_.value } - - if ($parametersWithValues.count -gt 0) - { - $parametersWithValues | ForEach-Object { Write-Verbose ">> '$($_.Key)' was specified, the value is: '$($_.Value)'" } - - Write-Warning ("[WARNING]: Requested server role is '$Role', the following parameter can only be used with server role 'RDS-Gateway': " + - "$($parametersWithValues.Key -join ', '). The parameter will be ignored in the call to Add-RDServer to avoid error!") - } - } -} - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -75,25 +22,32 @@ function Get-TargetResource ( [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $Server, [Parameter(Mandatory = $true)] [ValidateSet('RDS-Connection-Broker', 'RDS-Virtualization', 'RDS-RD-Server', 'RDS-Web-Access', 'RDS-Gateway', 'RDS-Licensing')] - [string] + [System.String] $Role, [Parameter()] - [string] + [System.String] $GatewayExternalFqdn # only for RDS-Gateway ) - $result = $null + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + + $result = @{ + ConnectionBroker = $null + Server = $null + Role = $null + GatewayExternalFqdn = $null + } if (-not $ConnectionBroker) { @@ -143,7 +97,7 @@ function Get-TargetResource # or, possibly, Remote Desktop Deployment doesn't exist/Remote Desktop Management Service not running } - $result + return $result } ######################################################################## @@ -156,24 +110,35 @@ function Set-TargetResource ( [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $Server, [Parameter(Mandatory = $true)] [ValidateSet('RDS-Connection-Broker', 'RDS-Virtualization', 'RDS-RD-Server', 'RDS-Web-Access', 'RDS-Gateway', 'RDS-Licensing')] - [string] + [System.String] $Role, [Parameter()] - [string] + [System.String] $GatewayExternalFqdn # only for RDS-Gateway ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + + if ($Role -eq 'RDS-Gateway') + { + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter @('GatewayExternalFqdn') + } + elseif ($PSBoundParameters.ContainsKey('GatewayExternalFqdn')) + { + Write-Warning ('[WARNING]: Requested server role is ''{0}'', the following parameter can only be used with server role ''RDS-Gateway'': ''GatewayExternalFqdn''. The parameter will be ignored in the call to Add-RDServer to avoid error!' -f $Role) + } + if (-not $ConnectionBroker) { $ConnectionBroker = $localhost @@ -181,9 +146,6 @@ function Set-TargetResource Write-Verbose "Adding server '$($Server.ToLower())' as $Role to the deployment on '$($ConnectionBroker.ToLower())'..." - # validate parameters - ValidateCustomModeParameters -Role $Role -GatewayExternalFqdn $GatewayExternalFqdn - if ($Role -eq 'RDS-Gateway') { Write-Verbose ">> GatewayExternalFqdn: '$GatewayExternalFqdn'" @@ -224,6 +186,7 @@ function Set-TargetResource { Add-RDServer @PSBoundParameters } + Write-Verbose 'Add-RDServer done.' } @@ -238,30 +201,35 @@ function Test-TargetResource ( [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $Server, [Parameter(Mandatory = $true)] [ValidateSet('RDS-Connection-Broker', 'RDS-Virtualization', 'RDS-RD-Server', 'RDS-Web-Access', 'RDS-Gateway', 'RDS-Licensing')] - [string] + [System.String] $Role, [Parameter()] - [string] + [System.String] $GatewayExternalFqdn # only for RDS-Gateway ) $target = Get-TargetResource @PSBoundParameters - $result = $null -ne $target + $testDscParameterStateSplat = @{ + CurrentValues = $target + DesiredValues = $PSBoundParameters + TurnOffTypeChecking = $false + SortArrayValues = $true + Verbose = $VerbosePreference + } - Write-Verbose "Test-TargetResource returning: $result" - return $result + return Test-DscParameterState @testDscParameterStateSplat } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDServer.Tests.ps1 b/tests/Unit/MSFT_xRDServer.Tests.ps1 index f4ab68f..85efe93 100644 --- a/tests/Unit/MSFT_xRDServer.Tests.ps1 +++ b/tests/Unit/MSFT_xRDServer.Tests.ps1 @@ -1,145 +1,461 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDServer' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDServer' - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'connectionbroker.lan' -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# } -# } - -# It 'Given a server that does not exist in the deployment, Get returns nothing' { -# Get-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should BeNullOrEmpty -# } - -# $getResult = Get-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker -# It 'Given a server connectionbroker.lan with the role RDS-CONNECTION-BROKER in the deployment, get returns property for this server' { -# param ( -# [string]$Property -# ) - -# $getResult.$Property | Should Not BeNullOrEmpty -# } -TestCases @( -# @{ -# Property = 'ConnectionBroker' -# } -# @{ -# Property = 'Server' -# } -# @{ -# Property = 'Role' -# } -# ) - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'connectionbroker.lan' -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# 'RDS-GATEWAY' -# ) -# } -# } - -# $getResult = Get-TargetResource -ConnectionBroker connectionbroker.lan -Server connectionbroker.lan -Role RDS-Gateway -# It 'Given a server with gateway role, the External Gateway FQDN is returned by Get' { -# $getResult.GatewayExternalFqdn | Should Be 'testgateway.external.fqdn' -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { -# Mock -CommandName Get-RDServer -MockWith { - -# if ($Role -eq 'RDS-Connection-Broker') -# { -# [pscustomobject]@{ -# Server = 'connectionbroker.lan' -# Roles = @( -# 'RDS-CONNECTION-BROKER' -# ) -# } -# } -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# } -# } - -# It 'Given a new server in the deployment, test returns false' { -# Test-TargetResource -Server does.not.exist -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $false -# } - -# It 'Given an existing server in the deployment, but with an unassigned role, test returns false' { -# Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Gateway | Should be $false -# } - -# It 'Given an existing server in the deployment, with an existing role, test returns true' { -# Test-TargetResource -Server connectionbroker.lan -ConnectionBroker connectionbroker.lan -Role RDS-Connection-Broker | Should be $true -# } -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when if role is RDS-Gateway and GatewayExternalFqdn is missing.' { -# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -Server 'server1' -Role 'RDS-Gateway' } ` -# | Should throw -# } -# } -# } -# #endregion - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDServer' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDServer\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When the resource is present' { + BeforeAll { + Mock -CommandName Get-RDServer -MockWith { + @{ + Server = 'connectionbroker.lan' + Roles = @( + 'RDS-CONNECTION-BROKER' + ) + } + } + } + + Context 'When the role is not ''RDS-Gateway''' { + Context 'When the ''ConnectionBroker'' is specified' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Connection-Broker' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.Server | Should -Be $testParams.Server + $result.Role | Should -Be $testParams.Role + $result.GatewayExternalFqdn | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When the ''ConnectionBroker'' is not specified' { + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Server = 'connectionbroker.lan' + Role = 'RDS-Connection-Broker' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $(Get-ComputerName -FullyQualifiedDomainName) + $result.Server | Should -Be $testParams.Server + $result.Role | Should -Be $testParams.Role + $result.GatewayExternalFqdn | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the role is ''RDS-Gateway''' { + BeforeAll { + Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { + @{ + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.Server | Should -Be $testParams.Server + $result.Role | Should -Be $testParams.Role + $result.GatewayExternalFqdn | Should -Be $testParams.GatewayExternalFqdn + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the resource is absent' { + Context 'When no RD Servers are configured' { + BeforeAll { + Mock -CommandName Get-RDServer + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'nonexistentserver.lan' + Role = 'RDS-Connection-Broker' + } + + $result = Get-TargetResource @testParams + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.Server | Should -BeNullOrEmpty + $result.Role | Should -BeNullOrEmpty + $result.GatewayExternalFqdn | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When the Server is not in the configured RD Servers' { + BeforeAll { + Mock -CommandName Get-RDServer -MockWith { + @{ + Server = 'connectionbroker.lan' + Roles = @( + 'RDS-CONNECTION-BROKER' + ) + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'nonexistentserver.lan' + Role = 'RDS-Connection-Broker' + } + + $result = Get-TargetResource @testParams + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.Server | Should -BeNullOrEmpty + $result.Role | Should -BeNullOrEmpty + $result.GatewayExternalFqdn | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + } + } + } +} + +Describe 'MSFT_xRDServer\Test-TargetResource' -Tag 'Test' { + Context 'When the system is in the desired state' { + Context 'When Role is ''RDS-GATEWAY''' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When Role is not ''RDS-GATEWAY''' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Virtualization' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Virtualization' + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the system not is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'newconnectionbroker.lan' + Server = 'connectionbroker.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'testgateway.external.fqdn' + } + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } +} + +Describe 'MSFT_xRDServer\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When adding a resource' { + Context 'When Role is ''RDS-Licensing'' or ''RDS-Gateway''' { + BeforeAll { + Mock -CommandName Assert-BoundParameter -RemoveParameterType @('RequiredBehavior') + } + + Context 'When ''Add-RDGateway'' completes successfully' { + BeforeAll { + Mock -CommandName Add-RDServer + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'rdsgateway.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'gateway.external.fqdn' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When ''Add-RDGateway'' completes with 2 errors' { + BeforeAll { + InModuleScope -ScriptBlock { + Mock -CommandName Add-RDServer -MockWith { + $list = [System.Collections.ArrayList]::new() + $list.Add((New-ErrorRecord -Exception [System.Management.Automation.CommandNotFoundException] -ErrorCategory 'ObjectNotFound' -ErrorId 'CommandNotFoundException')) + $list.Add((New-ErrorRecord -Exception [System.Management.Automation.CommandNotFoundException] -ErrorCategory 'ObjectNotFound' -ErrorId 'CommandNotFoundException')) + + Set-Variable -Name $PesterBoundParameters.ErrorVariable -Scope 3 -Value $list + } + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'rdsgateway.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'gateway.external.fqdn' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When ''Add-RDGateway'' completes with 1 error' { + BeforeAll { + InModuleScope -ScriptBlock { + Mock -CommandName Add-RDServer -MockWith { + $list = [System.Collections.ArrayList]::new() + $list.Add((New-ErrorRecord -Exception [System.Management.Automation.CommandNotFoundException] -ErrorCategory 'ObjectNotFound' -ErrorId 'CommandNotFoundException')) + + Set-Variable -Name $PesterBoundParameters.ErrorVariable -Scope 3 -Value $list + } + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + Server = 'rdsgateway.lan' + Role = 'RDS-Gateway' + GatewayExternalFqdn = 'gateway.external.fqdn' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + } + + Context 'When Role is not ''RDS-Licensing'' or ''RDS-Gateway''' { + BeforeAll { + Mock -CommandName Add-RDServer + } + + Context 'When ''ConnectionBroker'' is not specified' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Server = 'rdshost.lan' + Role = 'RDS-RD-Server' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + + Context 'When ''GatewayExternalFqdn'' is not specified' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + Server = 'rdshost.lan' + Role = 'RDS-RD-Server' + GatewayExternalFqdn = 'should.be.ignored.lan' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + } + } +} From b782e8f1c85ee196e13cafe4e02051a5377e3628 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:07:46 +0000 Subject: [PATCH 24/44] Migrate RDRemoteApp --- .../MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 | 170 +++- tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 | 898 ++++++++++++------ 2 files changed, 739 insertions(+), 329 deletions(-) diff --git a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 index bbd3e98..ce71327 100644 --- a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 +++ b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -22,35 +20,62 @@ function Get-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string] $DisplayName, + [System.String] + $DisplayName, + [Parameter(Mandatory = $true)] - [string] $FilePath, + [System.String] + $FilePath, + [Parameter(Mandatory = $true)] - [string] $Alias, + [System.String] + $Alias, + [Parameter()] [ValidateSet('Present', 'Absent')] - [string]$Ensure = 'Present', + [System.String] + $Ensure = 'Present', + [Parameter()] - [string] $FileVirtualPath, + [System.String] + $FileVirtualPath, + [Parameter()] - [string] $FolderName, + [System.String] + $FolderName, + [Parameter()] [ValidateSet('Allow', 'DoNotAllow', 'Require')] - [string] $CommandLineSetting, + [System.String] + $CommandLineSetting, + [Parameter()] - [string] $RequiredCommandLine, + [System.String] + $RequiredCommandLine, + [Parameter()] - [uint32] $IconIndex, + [System.UInt32] + $IconIndex, + [Parameter()] - [string] $IconPath, + [System.String] + $IconPath, + [Parameter()] - [string[]] $UserGroups, + [System.String[]] + $UserGroups, + [Parameter()] - [boolean] $ShowInWebAccess + [System.Boolean] + $ShowInWebAccess ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + try { $null = Get-RDSessionCollection -CollectionName $CollectionName -ErrorAction Stop @@ -64,10 +89,10 @@ function Get-TargetResource $remoteApp = Get-RDRemoteApp -CollectionName $CollectionName -Alias $Alias -ErrorAction SilentlyContinue $return = @{ - CollectionName = $remoteApp.CollectionName + CollectionName = $CollectionName DisplayName = $remoteApp.DisplayName FilePath = $remoteApp.FilePath - Alias = $remoteApp.Alias + Alias = $Alias FileVirtualPath = $remoteApp.FileVirtualPath FolderName = $remoteApp.FolderName CommandLineSetting = $remoteApp.CommandLineSetting @@ -100,35 +125,63 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string] $DisplayName, + [System.String] + $DisplayName, + [Parameter(Mandatory = $true)] - [string] $FilePath, + [System.String] + $FilePath, + [Parameter(Mandatory = $true)] - [string] $Alias, + [System.String] + $Alias, + [Parameter()] [ValidateSet('Present', 'Absent')] - [string]$Ensure = 'Present', + [System.String] + $Ensure = 'Present', + [Parameter()] - [string] $FileVirtualPath, + [System.String] + $FileVirtualPath, + [Parameter()] - [string] $FolderName, + [System.String] + $FolderName, + [Parameter()] [ValidateSet('Allow', 'DoNotAllow', 'Require')] - [string] $CommandLineSetting, + [System.String] + $CommandLineSetting, + [Parameter()] - [string] $RequiredCommandLine, + [System.String] + $RequiredCommandLine, + [Parameter()] - [uint32] $IconIndex, + [System.UInt32] + $IconIndex, + [Parameter()] - [string] $IconPath, + [System.String] + $IconPath, + [Parameter()] - [string[]] $UserGroups, + [System.String[]] + $UserGroups, + [Parameter()] - [boolean] $ShowInWebAccess + [System.Boolean] + $ShowInWebAccess + ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + try { $null = Get-RDSessionCollection -CollectionName $CollectionName -ErrorAction Stop @@ -166,37 +219,64 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateLength(1, 256)] - [string] $CollectionName, + [System.String] + $CollectionName, + [Parameter(Mandatory = $true)] - [string] $DisplayName, + [System.String] + $DisplayName, + [Parameter(Mandatory = $true)] - [string] $FilePath, + [System.String] + $FilePath, + [Parameter(Mandatory = $true)] - [string] $Alias, + [System.String] + $Alias, + [Parameter()] [ValidateSet('Present', 'Absent')] - [string]$Ensure = 'Present', + [System.String] + $Ensure = 'Present', + [Parameter()] - [string] $FileVirtualPath, + [System.String] + $FileVirtualPath, + [Parameter()] - [string] $FolderName, + [System.String] + $FolderName, + [Parameter()] [ValidateSet('Allow', 'DoNotAllow', 'Require')] - [string] $CommandLineSetting, + [System.String] + $CommandLineSetting, + [Parameter()] - [string] $RequiredCommandLine, + [System.String] + $RequiredCommandLine, + [Parameter()] - [uint32] $IconIndex, + [System.UInt32] + $IconIndex, + [Parameter()] - [string] $IconPath, + [System.String] + $IconPath, + [Parameter()] - [string[]] $UserGroups, + [System.String[]] + $UserGroups, + [Parameter()] - [boolean] $ShowInWebAccess + [System.Boolean] + $ShowInWebAccess ) Write-Verbose 'Testing if RemoteApp is published.' + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + try { $null = Get-RDSessionCollection -CollectionName $CollectionName -ErrorAction Stop @@ -207,15 +287,13 @@ function Test-TargetResource } $getTargetResourceResult = Get-TargetResource @PSBoundParameters - [System.Management.Automation.PSCmdlet]::CommonParameters | ForEach-Object -Process { - $null = $PSBoundParameters.Remove($_) - } $testDscParameterStateSplat = @{ CurrentValues = $getTargetResourceResult DesiredValues = $PSBoundParameters TurnOffTypeChecking = $true SortArrayValues = $true + ExcludeProperties = [System.Management.Automation.PSCmdlet]::CommonParameters } Test-DscParameterState @testDscParameterStateSplat diff --git a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 index c333d11..bf6f457 100644 --- a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 +++ b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 @@ -1,287 +1,619 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDRemoteApp' - -# #region HEADER - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDRemoteApp' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' +# $testInvalidCollectionSplat = @{ +# CollectionName = $testInvalidCollectionName +# DisplayName = 'name' +# FilePath = 'path' +# Alias = 'alias' # } -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment +# $sourceRemoteAppValues = @{ +# CollectionName = 'TestCollection' +# DisplayName = 'MyCalc (1.0.0)' +# FilePath = 'c:\windows\system32\calc.exe' +# Alias = 'Test-MyCalc-(1.0.0)' +# Ensure = 'Present' +# FileVirtualPath = 'c:\windows\system32\calc.exe' +# FolderName = 'Test' +# CommandLineSetting = 'DoNotAllow' +# RequiredCommandLine = 'my-cmd' +# IconIndex = 0 +# IconPath = 'c:\windows\system32\calc.exe' +# UserGroups = 'DOMAIN\MyAppGroup_DLG' +# ShowInWebAccess = $true # } -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDRemoteApp' - -# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' -# $testInvalidCollectionSplat = @{ -# CollectionName = $testInvalidCollectionName -# DisplayName = 'name' -# FilePath = 'path' -# Alias = 'alias' -# } - -# $sourceRemoteAppValues = @{ -# CollectionName = 'TestCollection' -# DisplayName = 'MyCalc (1.0.0)' -# FilePath = 'c:\windows\system32\calc.exe' -# Alias = 'Test-MyCalc-(1.0.0)' -# Ensure = 'Present' -# FileVirtualPath = 'c:\windows\system32\calc.exe' -# FolderName = 'Test' -# CommandLineSetting = 'DoNotAllow' -# RequiredCommandLine = 'my-cmd' -# IconIndex = 0 -# IconPath = 'c:\windows\system32\calc.exe' -# UserGroups = 'DOMAIN\MyAppGroup_DLG' -# ShowInWebAccess = $true -# } - -# $xRDRemoteAppSplat = $sourceRemoteAppValues.Clone() - -# $getRDRemoteApp = $xRDRemoteAppSplat.Clone() -# $null = $getRDRemoteApp.Remove('Ensure') - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Get-TargetResource @testInvalidCollectionSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' -# It 'Should only accept valid values for CommandLineSetting' { -# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting -# } - -# Context 'When Get-TargetResource is called' { - -# Mock -CommandName Get-RDRemoteApp -# Mock -CommandName Get-RDSessionCollection - -# It 'Should return Ensure Absent, given the RemoteApp is not created yet' { -# (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Absent' -# } - -# Mock -CommandName Get-RDRemoteApp -MockWith { -# $getRDRemoteApp -# } - -# It 'Should return Ensure Present, given the RemoteApp is created' { -# (Get-TargetResource @xRDRemoteAppSplat).Ensure | Should Be 'Present' -# } - -# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') -# $xRDRemoteAppSplat.UserGroups = $userGroups -# It 'Should not generate an error, given that an array of UserGroups was specified' { -# { Get-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw -# } -# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups - -# [System.Array] $commonParameters = [System.Management.Automation.PSCmdlet]::OptionalCommonParameters -# $commonParameters += [System.Management.Automation.PSCmdlet]::CommonParameters - -# $allParameters = (Get-Command Get-TargetResource).Parameters.Keys | -# Where-Object -FilterScript { $_ -notin $commonParameters } | -# ForEach-Object -Process { -# @{ -# Property = $_ -# Value = $xRDRemoteAppSplat[$_] -# } -# } - -# $getTargetResourceResult = Get-TargetResource @xRDRemoteAppSplat -# It 'Should return property with value in Get-TargetResource' { -# param( -# $Property, -# $Value -# ) - -# $getTargetResourceResult.$Property | Should Be $Value -# } -TestCases $allParameters - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# Write-Error 'Collection not found!' -# } - -# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { -# { Get-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | -# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' -# } -# } -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Set-TargetResource @testInvalidCollectionSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' -# It 'Should only accept valid values for CommandLineSetting' { -# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting -# } - -# Context 'When Set-TargetResource actions are performed' { - -# Mock -CommandName Get-RDSessionCollection -# Mock -CommandName Get-RDRemoteApp -# Mock -CommandName New-RDRemoteApp -# Mock -CommandName Remove-RDRemoteApp -# Mock -CommandName Set-RDRemoteApp - -# It 'Should call New-RDRemoteApp, given that the RemoteApp does not exist yet and Ensure is set to Present' { -# Set-TargetResource @xRDRemoteAppSplat -# Assert-MockCalled -CommandName New-RDRemoteApp -Times 1 -Scope It -# } - -# Mock -CommandName Get-RDRemoteApp -MockWith { $getRDRemoteApp } - -# It 'Should call Set-RDRemoteApp, given that the RemoteApp does exist and Ensure is set to Present' { -# Set-TargetResource @xRDRemoteAppSplat -# Assert-MockCalled -CommandName Set-RDRemoteApp -Times 1 -Scope It -# } - -# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') -# $xRDRemoteAppSplat.UserGroups = $userGroups -# It 'Should not generate an error, given that an array of UserGroups was specified' { -# { Set-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw -# } -# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups - -# $xRDRemoteAppSplat.Ensure = 'Absent' -# It 'Should call Remove-RDRemoteApp, given that the RemoteApp exists and Ensure is set to Absent' { -# Set-TargetResource @xRDRemoteAppSplat -# Assert-MockCalled -CommandName Remove-RDRemoteApp -Times 1 -Scope It -# } -# $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# Write-Error 'Collection not found!' -# } - -# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { -# { Set-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | -# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' -# } -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when CollectionName length is greater than 256' { -# { Test-TargetResource @testInvalidCollectionSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = 'Invalid' -# It 'Should only accept valid values for CommandLineSetting' { -# { Get-TargetResource @xRDRemoteAppSplat } | Should Throw -# } - -# $xRDRemoteAppSplat.CommandLineSetting = $sourceRemoteAppValues.CommandLineSetting -# } - -# Context 'Test output validation' { -# Mock -CommandName Get-RDSessionCollection -# Mock -CommandName Get-RDRemoteApp - -# It 'Should return false, given that the RemoteApp does not exist yet and Ensure is set to Present' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $false -# } - -# Mock -CommandName Get-RDRemoteApp -MockWith { -# $getRDRemoteApp -# } - -# It 'Should return true, given that the RemoteApp does exist and Ensure is set to Present' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $true -# } - -# $xRDRemoteAppSplat.Ensure = 'Absent' -# It 'Should return false, given that the RemoteApp exists and Ensure is set to Absent' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $false -# } -# $xRDRemoteAppSplat.Ensure = $sourceRemoteAppValues.Ensure - -# $userGroups = @('DOMAIN\RemoteApp_UserGroup1', 'DOMAIN\RemoteApp_UserGroup2') -# $xRDRemoteAppSplat.UserGroups = $userGroups -# It 'Should not generate an error, given that an array of UserGroups was specified' { -# { Test-TargetResource @xRDRemoteAppSplat } | Should -Not -Throw -# } - -# It 'Should return false, due to a mismatch in UserGroups' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $false -# } - -# $getRDRemoteApp.UserGroups = $userGroups -# Mock -CommandName Get-RDRemoteApp -MockWith { -# $getRDRemoteApp -# } - -# It 'Should return true, given that the comparison of UserGroups arrays succeeds' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $true -# } -# $xRDRemoteAppSplat.UserGroups = $sourceRemoteAppValues.UserGroups -# $getRDRemoteApp.UserGroups = $sourceRemoteAppValues.UserGroups - -# $getRDRemoteApp.CommandLineSetting = 'Allow' -# Mock -CommandName Get-RDRemoteApp -MockWith { -# $getRDRemoteApp -# } - -# It 'Should return false, given that the RemoteApp exists and Ensure is set to Present and a single setting is misconfigured' { -# Test-TargetResource @xRDRemoteAppSplat | Should Be $false -# } - -# Mock -CommandName Get-RDSessionCollection -MockWith { -# Write-Error 'Collection not found!' -# } - -# It 'Should generate an error, given that the CollectionName is not found in the SessionDeployment' { -# { Test-TargetResource @xRDRemoteAppSplat -ErrorAction Stop } | -# Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Collection not found!' -# } -# } -# } -# #endregion -# } -# } -# finally -# { -# Invoke-TestCleanup -# } +# $xRDRemoteAppSplat = $sourceRemoteAppValues.Clone() + +# $getRDRemoteApp = $xRDRemoteAppSplat.Clone() +# $null = $getRDRemoteApp.Remove('Ensure') + +Describe 'MSFT_xRDRemoteApp\Get-TargetResource' -Tag 'Get' { + Context 'When the resource exists' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-RDRemoteApp -MockWith { + @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = @('DOMAIN\MyAppGroup_DLG') + ShowInWebAccess = $true + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $result = Get-TargetResource @testParams + + $result.CollectionName | Should -Be $testParams.CollectionName + $result.DisplayName | Should -Be $testParams.DisplayName + $result.FilePath | Should -Be $testParams.FilePath + $result.Alias | Should -Be $testParams.Alias + $result.Ensure | Should -Be 'Present' + $result.FileVirtualPath | Should -Be $testParams.FileVirtualPath + $result.FolderName | Should -Be $testParams.FolderName + $result.CommandLineSetting | Should -Be $testParams.CommandLineSetting + $result.RequiredCommandLine | Should -Be $testParams.RequiredCommandLine + $result.IconIndex | Should -Be $testParams.IconIndex + $result.IconPath | Should -Be $testParams.IconPath + $result.UserGroups | Should -Be $testParams.UserGroups + $result.ShowInWebAccess | Should -Be $testParams.ShowInWebAccess + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 1 -Scope It + } + } + + Context 'When the resource does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-RDRemoteApp + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $result = Get-TargetResource @testParams + + $result.CollectionName | Should -Be $testParams.CollectionName + $result.DisplayName | Should -BeNullOrEmpty + $result.FilePath | Should -BeNullOrEmpty + $result.Alias | Should -Be $testParams.Alias + $result.Ensure | Should -Be 'Absent' + $result.FileVirtualPath | Should -BeNullOrEmpty + $result.FolderName | Should -BeNullOrEmpty + $result.CommandLineSetting | Should -BeNullOrEmpty + $result.RequiredCommandLine | Should -BeNullOrEmpty + $result.IconIndex | Should -BeNullOrEmpty + $result.IconPath | Should -BeNullOrEmpty + $result.UserGroups | Should -BeNullOrEmpty + $result.ShowInWebAccess | Should -BeNullOrEmpty + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 1 -Scope It + } + } + + Context 'When the collection does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection -MockWith { + throw 'Mock Error' + } + + Mock -CommandName Get-RDRemoteApp + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + { Get-TargetResource @testParams } | Should -Throw -ExpectedMessage "Failed to lookup RD Session Collection $($testParams.CollectionName). Error: Mock Error" + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + } + } +} + +Describe 'MSFT_xRDRemoteApp\Set-TargetResource' -Tag 'Set' { + Context 'When the resource should be created' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-RDRemoteApp + Mock -CommandName New-RDRemoteApp + Mock -CommandName Remove-RDRemoteApp + Mock -CommandName Set-RDRemoteApp + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDRemoteApp -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Remove-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDRemoteApp -Exactly -Times 0 -Scope It + } + } + + Context 'When the resource should be removed' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-RDRemoteApp -MockWith { + @{ + Name = 'SomeValue' + } + } + + Mock -CommandName New-RDRemoteApp + Mock -CommandName Remove-RDRemoteApp + Mock -CommandName Set-RDRemoteApp + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Absent' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Remove-RDRemoteApp -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDRemoteApp -Exactly -Times 0 -Scope It + } + } + + Context 'When the resource should be updated' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-RDRemoteApp -MockWith { + @{ + Name = 'SomeValue' + } + } + + Mock -CommandName New-RDRemoteApp + Mock -CommandName Remove-RDRemoteApp + Mock -CommandName Set-RDRemoteApp + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 1 -Scope It + Should -Invoke -CommandName New-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Remove-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDRemoteApp -Exactly -Times 1 -Scope It + } + } + + Context 'When the collection does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection -MockWith { + throw 'Mock Error' + } + + Mock -CommandName Get-RDRemoteApp + Mock -CommandName New-RDRemoteApp + Mock -CommandName Remove-RDRemoteApp + Mock -CommandName Set-RDRemoteApp + } + + It 'Should generate an error' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + { Set-TargetResource @testParams } | Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Mock Error' + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName New-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Remove-RDRemoteApp -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDRemoteApp -Exactly -Times 0 -Scope It + } + } +} + +Describe 'MSFT_xRDRemoteApp\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = @('DOMAIN\MyAppGroup_DLG') + ShowInWebAccess = $true + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + Test-TargetResource @testParams | Should -BeTrue + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + + Context 'When the resource is not in the desired state' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection + Mock -CommandName Get-TargetResource -MockWith { + @{ + CollectionName = 'TestCollection' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Alias = 'Test-MyCalc-(1.0.0)' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = @('DOMAIN\MyAppGroup_DLG') + ShowInWebAccess = $true + } + } + } + + BeforeDiscovery { + $testCases = @( + @{ + Parameter = 'DisplayName' + Value = 'MyCalc (1.1.0)' + } + @{ + Parameter = 'FilePath' + Value = 'c:\windows\system32\calc.ex' + } + @{ + Parameter = 'FileVirtualPath' + Value = 'c:\windows\system32\calc.ex' + } + @{ + Parameter = 'FolderName' + Value = 'Test_Different' + } + @{ + Parameter = 'CommandLineSetting' + Value = 'Allow' + } + @{ + Parameter = 'RequiredCommandLine' + Value = 'my-cmd-other' + } + @{ + Parameter = 'IconIndex' + Value = 1 + } + @{ + Parameter = 'IconPath' + Value = 'c:\windows\system32\calc.ex' + } + @{ + Parameter = 'UserGroups' + Value = @('DOMAIN\MyAppGroup_DLG', 'DOMAIN\AnotherGroup_DLG') + } + @{ + Parameter = 'ShowInWebAccess' + Value = $false + } + ) + } + + Context 'When the Parameter is different' -ForEach $testCases { + It 'Should return the correct result' { + InModuleScope -Parameters $_ -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + $testParams[$Parameter] = $Value + + Test-TargetResource @testParams | Should -BeFalse + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 1 -Scope It + } + } + } + + Context 'When the collection does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDSessionCollection -MockWith { + throw 'Mock Error' + } + + Mock -CommandName Get-TargetResource + } + + It 'Should generate an error' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + CollectionName = 'TestCollection' + Alias = 'Test-MyCalc-(1.0.0)' + DisplayName = 'MyCalc (1.0.0)' + FilePath = 'c:\windows\system32\calc.exe' + Ensure = 'Present' + FileVirtualPath = 'c:\windows\system32\calc.exe' + FolderName = 'Test' + CommandLineSetting = 'DoNotAllow' + RequiredCommandLine = 'my-cmd' + IconIndex = 0 + IconPath = 'c:\windows\system32\calc.exe' + UserGroups = 'DOMAIN\MyAppGroup_DLG' + ShowInWebAccess = $true + } + + { Test-TargetResource @testParams } | Should -Throw -ExpectedMessage 'Failed to lookup RD Session Collection TestCollection. Error: Mock Error' + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDSessionCollection -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-TargetResource -Exactly -Times 0 -Scope It + } + } +} From ae4e5ca3e0db5208f5cdbc0ebf8bef16abb64d49 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 10:09:08 +0000 Subject: [PATCH 25/44] Fix HQRM --- source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index 6ea1b4b..71772c5 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -219,10 +219,10 @@ function Test-TargetResource $GatewayExternalFqdn # only for RDS-Gateway ) - $target = Get-TargetResource @PSBoundParameters + Write-Verbose 'Checking for existence of RDS Server.' $testDscParameterStateSplat = @{ - CurrentValues = $target + CurrentValues = Get-TargetResource @PSBoundParameters DesiredValues = $PSBoundParameters TurnOffTypeChecking = $false SortArrayValues = $true From 0b15dd78e393d027dacbbb48f9c0f6988bedaad3 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 10:09:28 +0000 Subject: [PATCH 26/44] Fix types --- .../MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 | 7 +-- tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 | 47 ++++--------------- 2 files changed, 13 insertions(+), 41 deletions(-) diff --git a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 index ce71327..3ef2758 100644 --- a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 +++ b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 @@ -97,9 +97,9 @@ function Get-TargetResource FolderName = $remoteApp.FolderName CommandLineSetting = $remoteApp.CommandLineSetting RequiredCommandLine = $remoteApp.RequiredCommandLine - IconIndex = $remoteApp.IconIndex + IconIndex = [System.UInt32] $remoteApp.IconIndex IconPath = $remoteApp.IconPath - UserGroups = $remoteApp.UserGroups + UserGroups = [System.String[]] $remoteApp.UserGroups ShowInWebAccess = $remoteApp.ShowInWebAccess } @@ -291,9 +291,10 @@ function Test-TargetResource $testDscParameterStateSplat = @{ CurrentValues = $getTargetResourceResult DesiredValues = $PSBoundParameters - TurnOffTypeChecking = $true + TurnOffTypeChecking = $false SortArrayValues = $true ExcludeProperties = [System.Management.Automation.PSCmdlet]::CommonParameters + Verbose = $VerbosePreference } Test-DscParameterState @testDscParameterStateSplat diff --git a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 index bf6f457..56c1e75 100644 --- a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 +++ b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 @@ -56,35 +56,6 @@ AfterAll { Get-Module -Name $script:dscResourceName -All | Remove-Module -Force } -# $testInvalidCollectionName = 'InvalidCollectionNameLongerThan256-12345678910111213141516171819202122232425262728142124124124awffjwifhw28qfhw27[q9aqfj2wai9fua29fua2fna29fja2fj29f2u192u4-[12fj2390fau2-9fu-9fu1-2ur1-2u149u2mfaweifjwifjw19wu-u2394u12-f2u1223fu-1f1239fy193413403mgjefas902311' -# $testInvalidCollectionSplat = @{ -# CollectionName = $testInvalidCollectionName -# DisplayName = 'name' -# FilePath = 'path' -# Alias = 'alias' -# } - -# $sourceRemoteAppValues = @{ -# CollectionName = 'TestCollection' -# DisplayName = 'MyCalc (1.0.0)' -# FilePath = 'c:\windows\system32\calc.exe' -# Alias = 'Test-MyCalc-(1.0.0)' -# Ensure = 'Present' -# FileVirtualPath = 'c:\windows\system32\calc.exe' -# FolderName = 'Test' -# CommandLineSetting = 'DoNotAllow' -# RequiredCommandLine = 'my-cmd' -# IconIndex = 0 -# IconPath = 'c:\windows\system32\calc.exe' -# UserGroups = 'DOMAIN\MyAppGroup_DLG' -# ShowInWebAccess = $true -# } - -# $xRDRemoteAppSplat = $sourceRemoteAppValues.Clone() - -# $getRDRemoteApp = $xRDRemoteAppSplat.Clone() -# $null = $getRDRemoteApp.Remove('Ensure') - Describe 'MSFT_xRDRemoteApp\Get-TargetResource' -Tag 'Get' { Context 'When the resource exists' { BeforeAll { @@ -101,9 +72,9 @@ Describe 'MSFT_xRDRemoteApp\Get-TargetResource' -Tag 'Get' { FolderName = 'Test' CommandLineSetting = 'DoNotAllow' RequiredCommandLine = 'my-cmd' - IconIndex = 0 + IconIndex = [System.UInt32] 0 IconPath = 'c:\windows\system32\calc.exe' - UserGroups = @('DOMAIN\MyAppGroup_DLG') + UserGroups = [System.String[]] @('DOMAIN\MyAppGroup_DLG') ShowInWebAccess = $true } } @@ -140,7 +111,7 @@ Describe 'MSFT_xRDRemoteApp\Get-TargetResource' -Tag 'Get' { $result.FolderName | Should -Be $testParams.FolderName $result.CommandLineSetting | Should -Be $testParams.CommandLineSetting $result.RequiredCommandLine | Should -Be $testParams.RequiredCommandLine - $result.IconIndex | Should -Be $testParams.IconIndex + $result.IconIndex | Should -Be 0 $result.IconPath | Should -Be $testParams.IconPath $result.UserGroups | Should -Be $testParams.UserGroups $result.ShowInWebAccess | Should -Be $testParams.ShowInWebAccess @@ -440,9 +411,9 @@ Describe 'MSFT_xRDRemoteApp\Test-TargetResource' -Tag 'Test' { FolderName = 'Test' CommandLineSetting = 'DoNotAllow' RequiredCommandLine = 'my-cmd' - IconIndex = 0 + IconIndex = [System.UInt32] 0 IconPath = 'c:\windows\system32\calc.exe' - UserGroups = @('DOMAIN\MyAppGroup_DLG') + UserGroups = [System.String[]] @('DOMAIN\MyAppGroup_DLG') ShowInWebAccess = $true } } @@ -464,11 +435,11 @@ Describe 'MSFT_xRDRemoteApp\Test-TargetResource' -Tag 'Test' { RequiredCommandLine = 'my-cmd' IconIndex = 0 IconPath = 'c:\windows\system32\calc.exe' - UserGroups = 'DOMAIN\MyAppGroup_DLG' + UserGroups = @('DOMAIN\MyAppGroup_DLG') ShowInWebAccess = $true } - Test-TargetResource @testParams | Should -BeTrue + Test-TargetResource @testParams -Verbose | Should -BeTrue } Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It @@ -492,9 +463,9 @@ Describe 'MSFT_xRDRemoteApp\Test-TargetResource' -Tag 'Test' { FolderName = 'Test' CommandLineSetting = 'DoNotAllow' RequiredCommandLine = 'my-cmd' - IconIndex = 0 + IconIndex = [System.UInt32] 0 IconPath = 'c:\windows\system32\calc.exe' - UserGroups = @('DOMAIN\MyAppGroup_DLG') + UserGroups = [System.String[]] @('DOMAIN\MyAppGroup_DLG') ShowInWebAccess = $true } } From 0e9834b2fe566016f5434eb8a158f8e8d4a78df0 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:53:10 +0000 Subject: [PATCH 27/44] Clean up --- .../MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 | 7 ++----- .../MSFT_xRDSessionCollection.psm1 | 17 +++++++++-------- tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 | 4 ++-- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 index 3ef2758..8a04ea2 100644 --- a/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 +++ b/source/DSCResources/MSFT_xRDRemoteApp/MSFT_xRDRemoteApp.psm1 @@ -286,18 +286,15 @@ function Test-TargetResource throw "Failed to lookup RD Session Collection $CollectionName. Error: $_" } - $getTargetResourceResult = Get-TargetResource @PSBoundParameters - $testDscParameterStateSplat = @{ - CurrentValues = $getTargetResourceResult + CurrentValues = Get-TargetResource @PSBoundParameters DesiredValues = $PSBoundParameters TurnOffTypeChecking = $false SortArrayValues = $true - ExcludeProperties = [System.Management.Automation.PSCmdlet]::CommonParameters Verbose = $VerbosePreference } - Test-DscParameterState @testDscParameterStateSplat + return Test-DscParameterState @testDscParameterStateSplat } Export-ModuleMember -Function *-TargetResource diff --git a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 index e3d1192..7cb9412 100644 --- a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 @@ -210,14 +210,15 @@ function Test-TargetResource Write-Verbose 'Checking for existence of RDSH collection.' - $desiredState = $PSBoundParameters - $currentState = Get-TargetResource @PSBoundParameters - - return Test-DscParameterState ` - -CurrentValues $currentState ` - -DesiredValues $desiredState ` - -SortArrayValues ` - -Verbose:$VerbosePreference + $testDscParameterStateSplat = @{ + CurrentValues = Get-TargetResource @PSBoundParameters + DesiredValues = $PSBoundParameters + TurnOffTypeChecking = $false + SortArrayValues = $true + Verbose = $VerbosePreference + } + + return Test-DscParameterState @testDscParameterStateSplat } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 index 56c1e75..0fc4078 100644 --- a/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 +++ b/tests/Unit/MSFT_xRDRemoteApp.Tests.ps1 @@ -161,7 +161,7 @@ Describe 'MSFT_xRDRemoteApp\Get-TargetResource' -Tag 'Get' { $result.FolderName | Should -BeNullOrEmpty $result.CommandLineSetting | Should -BeNullOrEmpty $result.RequiredCommandLine | Should -BeNullOrEmpty - $result.IconIndex | Should -BeNullOrEmpty + $result.IconIndex | Should -Be 0 $result.IconPath | Should -BeNullOrEmpty $result.UserGroups | Should -BeNullOrEmpty $result.ShowInWebAccess | Should -BeNullOrEmpty @@ -439,7 +439,7 @@ Describe 'MSFT_xRDRemoteApp\Test-TargetResource' -Tag 'Test' { ShowInWebAccess = $true } - Test-TargetResource @testParams -Verbose | Should -BeTrue + Test-TargetResource @testParams | Should -BeTrue } Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It From db9808e8cdc15b9768bc826a8adb949bf1969021 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:53:20 +0000 Subject: [PATCH 28/44] Migrate LicenseConfiguration --- .../MSFT_xRDLicenseConfiguration.psm1 | 108 +++--- .../MSFT_xRDLicenseConfiguration.Tests.ps1 | 331 ++++++++++++------ 2 files changed, 265 insertions(+), 174 deletions(-) diff --git a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 index 9975664..8d77b2e 100644 --- a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 @@ -9,8 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -22,19 +20,22 @@ function Get-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter()] - [string[]] + [System.String[]] $LicenseServer, [Parameter(Mandatory = $true)] [ValidateSet('PerUser', 'PerDevice', 'NotConfigured')] - [string] + [System.String] $LicenseMode ) - $result = $null + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + + Write-Verbose "Getting RD License server configuration from broker '$ConnectionBroker'..." @@ -43,22 +44,21 @@ function Get-TargetResource if ($config) # Microsoft.RemoteDesktopServices.Management.LicensingSetting { Write-Verbose 'configuration retrieved successfully:' + $result = @{ + ConnectionBroker = $ConnectionBroker + LicenseServer = [System.String[]] $config.LicenseServer + LicenseMode = $config.Mode.ToString() # Microsoft.RemoteDesktopServices.Management.LicensingMode .ToString() + } + + Write-Verbose ">> RD License mode: $($result.LicenseMode)" + Write-Verbose ">> RD License servers: $($result.LicenseServer -join '; ')" } else { - Write-Verbose "Failed to retrieve RD License configuration from broker '$ConnectionBroker'." - throw ("Failed to retrieve RD License configuration from broker '$ConnectionBroker'.") - } - $result = @{ - ConnectionBroker = $ConnectionBroker - LicenseServer = $config.LicenseServer - LicenseMode = $config.Mode.ToString() # Microsoft.RemoteDesktopServices.Management.LicensingMode .ToString() + $result = $null } - Write-Verbose ">> RD License mode: $($result.LicenseMode)" - Write-Verbose ">> RD License servers: $($result.LicenseServer -join '; ')" - - $result + return $result } ######################################################################## @@ -71,35 +71,37 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter()] - [string[]] + [System.String[]] $LicenseServer, [Parameter(Mandatory = $true)] # required parameter in Set-RDLicenseConfiguration [ValidateSet('PerUser', 'PerDevice', 'NotConfigured')] - [string] + [System.String] $LicenseMode ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Write-Verbose 'Starting RD License server configuration...' Write-Verbose ">> RD Connection Broker: $($ConnectionBroker.ToLower())" + $setLicenseConfigParams = @{ + ConnectionBroker = $ConnectionBroker + Mode = $LicenseMode + } + if ($LicenseServer) { Write-Verbose ">> RD License servers: $($LicenseServer -join '; ')" - - Write-Verbose 'Calling Set-RDLicenseConfiguration cmdlet...' - Set-RDLicenseConfiguration -ConnectionBroker $ConnectionBroker -LicenseServer $LicenseServer -Mode $LicenseMode -Force - } - else - { - Write-Verbose 'Calling Set-RDLicenseConfiguration cmdlet...' - Set-RDLicenseConfiguration -ConnectionBroker $ConnectionBroker -Mode $LicenseMode -Force + $setLicenseConfigParams.LicenseServer = $LicenseServer } - Write-Verbose 'Set-RDLicenseConfiguration done.' + Write-Verbose 'Calling Set-RDLicenseConfiguration cmdlet...' + Set-RDLicenseConfiguration @setLicenseConfigParams -Force } @@ -114,52 +116,30 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] $ConnectionBroker, + [System.String] + $ConnectionBroker, [Parameter()] - [string[]] + [System.String[]] $LicenseServer, [Parameter(Mandatory = $true)] [ValidateSet('PerUser', 'PerDevice', 'NotConfigured')] - [string] $LicenseMode + [System.String] + $LicenseMode ) - $config = Get-TargetResource @PSBoundParameters + Write-Verbose 'Testing RD license servers' - if ($config) - { - Write-Verbose "Verifying RD Licensing mode: $($config.LicenseMode -eq $LicenseMode)" - - Write-Verbose 'Verifying RD license servers...' - $noChange = $true - if ($LicenseServer) - { - foreach ($server in $config.LicenseServer) - { - if ($LicenseServer -notcontains $server) - { - $noChange = $false - Write-Verbose "License Server '$server' in the current configuration will be removed." - } - } - if ($LicenseServer.Count -ne $config.LicenseServer.Count) - { - $noChange = $false - } - } - - - $result = ($config.LicenseMode -eq $LicenseMode) -and $noChange - } - else - { - Write-Verbose "Failed to retrieve RD License server configuration from broker '$ConnectionBroker'." - $result = $false + $testDscParameterStateSplat = @{ + CurrentValues = Get-TargetResource @PSBoundParameters + DesiredValues = $PSBoundParameters + TurnOffTypeChecking = $false + SortArrayValues = $true + Verbose = $VerbosePreference } - Write-Verbose "Test-TargetResource returning: $result" - return $result + return Test-DscParameterState @testDscParameterStateSplat } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 index c566c55..dd145c6 100644 --- a/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 +++ b/tests/Unit/MSFT_xRDLicenseConfiguration.Tests.ps1 @@ -1,110 +1,221 @@ -# $script:DSCModuleName = 'xRemoteDesktopSessionHost' -# $script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:DSCResourceName = 'MSFT_xRDLicenseConfiguration' - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { -# Mock Get-RDLicenseConfiguration -MockWith { return $null } -# It 'Should error if unable to get RD License config.' { -# { Get-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'NotConfigured' } | Should throw -# } -# } -# } -# #endregion - -# #region Function Test-TargetResource -# Describe "$($script:DSCResourceName)\Test-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# Mock -CommandName Get-TargetResource -MockWith { -# return @{ -# 'ConnectionBroker' = 'connectionbroker.lan' -# 'LicenseServer' = @('One', 'Two') -# 'LicenseMode' = 'PerUser' -# } -# } -ModuleName MSFT_xRDLicenseConfiguration - -# It "Should return false if there's a change in license servers." { -# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer 'One' | Should Be $false -# } - -# Mock Get-TargetResource -MockWith { -# return @{ -# 'ConnectionBroker' = 'connectionbroker.lan' -# 'LicenseServer' = @('One', 'Two') -# 'LicenseMode' = 'PerUser' -# } -# } - -# It "Should return false if there's a change in license mode." { -# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerDevice' -LicenseServer @('One', 'Two') | Should Be $false -# } - -# It 'Should return true if there are no changes in license mode.' { -# Test-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode 'PerUser' -LicenseServer @('One', 'Two') | Should Be $true -# } -# } -# } -# #endregion - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { - -# Context 'Configuration changes performed by Set' { - -# Mock -CommandName Set-RDLicenseConfiguration - -# It 'Given license servers, Set-RDLicenseConfiguration is called with LicenseServer parameter' { -# Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice -LicenseServer 'LicenseServer1' -# Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { -# $LicenseServer -eq 'LicenseServer1' -# } -# } - -# It 'Given no license servers, Set-RDLicenseConfiguration is called without LicenseServer parameter' { -# Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -LicenseMode PerDevice -# Assert-MockCalled -CommandName Set-RDLicenseConfiguration -Times 1 -ParameterFilter { -# $LicenseServer -eq $null -# } -Scope It -# } -# } -# } -# #endregion -# } -# } -# finally -# { -# Invoke-TestCleanup -# } +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDLicenseConfiguration' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDLicenseConfiguration\Get-TargetResource' -Tag 'Get' { + Context 'When the resource exists' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDLicenseConfiguration -MockWith { + [PSCustomObject] @{ + LicenseServer = [System.String[]] @('LicenseServer1', 'LicenseServer2') + Mode = [Microsoft.RemoteDesktopServices.Management.LicensingMode]::PerUser + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.LicenseServer | Should -Be $testParams.LicenseServer + $result.LicenseMode | Should -Be $testParams.LicenseMode + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Get-RDLicenseConfiguration -Exactly -Times 1 -Scope It + } + } + + Context 'When the resource does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDLicenseConfiguration + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.LicenseServer | Should -BeNullOrEmpty + $result.LicenseMode | Should -BeNullOrEmpty + } + } + } +} + +Describe 'MSFT_xRDLicenseConfiguration\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = [System.String[]] @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + + Context 'When the resource is not in the desired state' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = [System.String[]] @('LicenseServer1') + LicenseMode = 'PerDevice' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } +} + +Describe 'MSFT_xRDLicenseConfiguration\Set-TargetResource' -Tag 'Set' { + Context 'When the resource is updated' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Set-RDLicenseConfiguration + } + + Context 'When parameter ''LicenseServer'' is specified' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseServer = @('LicenseServer1', 'LicenseServer2') + LicenseMode = 'PerUser' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDLicenseConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -ne $LicenseServer + } + } + } + + Context 'When parameter ''LicenseServer'' is not specified' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + LicenseMode = 'PerUser' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDLicenseConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -eq $LicenseServer + } + } + } + } +} From 7d95dd13abda7aafc5cc42277bf5c79009dda7fb Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:18:05 +0000 Subject: [PATCH 29/44] Rearrange assert --- .../MSFT_xRDCertificateConfiguration.psm1 | 4 ++-- .../MSFT_xRDLicenseConfiguration.psm1 | 9 ++++----- .../MSFT_xRDSessionCollection.psm1 | 4 ++-- .../MSFT_xRDSessionCollectionConfiguration.psm1 | 4 ++-- .../MSFT_xRDSessionDeployment.psm1 | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 index d0a50f0..cae7080 100644 --- a/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDCertificateConfiguration/MSFT_xRDCertificateConfiguration.psm1 @@ -39,12 +39,12 @@ function Get-TargetResource $Credential ) - Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - Write-Verbose -Message ( $script:localizedData.VerboseGetCertificate -f $Role, $ConnectionBroker ) + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + Get-RDCertificate -Role $Role -ConnectionBroker $ConnectionBroker } diff --git a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 index 8d77b2e..edf7c95 100644 --- a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 @@ -33,12 +33,10 @@ function Get-TargetResource $LicenseMode ) - Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - - - Write-Verbose "Getting RD License server configuration from broker '$ConnectionBroker'..." + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + $config = Get-RDLicenseConfiguration -ConnectionBroker $ConnectionBroker -ea SilentlyContinue if ($config) # Microsoft.RemoteDesktopServices.Management.LicensingSetting @@ -84,9 +82,10 @@ function Set-TargetResource $LicenseMode ) + Write-Verbose 'Starting RD License server configuration...' + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - Write-Verbose 'Starting RD License server configuration...' Write-Verbose ">> RD Connection Broker: $($ConnectionBroker.ToLower())" $setLicenseConfigParams = @{ diff --git a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 index 7cb9412..cdbbbd2 100644 --- a/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollection/MSFT_xRDSessionCollection.psm1 @@ -40,10 +40,10 @@ function Get-TargetResource $Force ) - Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - Write-Verbose -Message 'Getting information about RDSH collection.' + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + $params = @{ ConnectionBroker = $ConnectionBroker CollectionName = $CollectionName diff --git a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 index 70e4b54..81f4dec 100644 --- a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 @@ -291,11 +291,11 @@ function Set-TargetResource [System.String[]] $ExcludeFilePath ) + + Write-Verbose 'Setting DSC collection properties' Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - Write-Verbose 'Setting DSC collection properties' - try { $null = Get-RDSessionCollection -CollectionName $CollectionName -ErrorAction Stop diff --git a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 index 09305bc..f70cf5f 100644 --- a/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 +++ b/source/DSCResources/MSFT_xRDSessionDeployment/MSFT_xRDSessionDeployment.psm1 @@ -31,10 +31,10 @@ function Get-TargetResource $WebAccessServer ) - Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - Write-Verbose 'Getting list of RD Server roles.' + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + # Start service RDMS is needed because otherwise a reboot loop could happen due to # the RDMS Service being on Delay-Start by default, and DSC kicks in too quickly after a reboot. if ((Get-Service -Name RDMS -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Status) -ne 'Running') From 4bba71142f71ca1dc8b54de0377cd7a795484636 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:18:47 +0000 Subject: [PATCH 30/44] Migrate GatewayConfiguration --- .../MSFT_xRDGatewayConfiguration.psm1 | 220 ++-- .../MSFT_xRDGatewayConfiguration.Tests.ps1 | 1016 +++++++++-------- 2 files changed, 645 insertions(+), 591 deletions(-) diff --git a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 index b3f13ef..e86d37d 100644 --- a/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDGatewayConfiguration/MSFT_xRDGatewayConfiguration.psm1 @@ -9,74 +9,6 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -Assert-Module -ModuleName 'RemoteDesktop' -ImportModule - -function ValidateCustomModeParameters -{ - param - ( - [Parameter()] - [ValidateSet('DoNotUse', 'Custom', 'Automatic')] - [string] - $mode, - - [Parameter()] - [string] - $ExternalFqdn, - - [Parameter()] - [string] - $LogonMethod, - - [Parameter()] - [bool] - $UseCachedCredentials, - - [Parameter()] - [bool] - $BypassLocal - ) - - Write-Verbose 'validating parameters...' - - $customModeParams = @{ - ExternalFqdn = $ExternalFqdn - LogonMethod = $LogonMethod - UseCachedCredentials = $UseCachedCredentials - BypassLocal = $BypassLocal - } - - if ($mode -eq 'Custom') - { - # ensure all 4 parameters were passed in, otherwise Set-RdDeploymentGatewayConfiguration will fail - - $nulls = $customModeParams.GetEnumerator() | Where-Object { $null -eq $_.Value } - - if ($nulls.Count -gt 0) - { - $nulls | ForEach-Object { Write-Verbose ">> '$($_.Key)' parameter is empty" } - - Write-Warning "[PARAMETER VALIDATION FAILURE] i'm gonna throw, right now..." - - throw ("Requested gateway mode is 'Custom', you must pass in the following parameters: $($nulls.Key -join ', ').") - } - } - else - { - # give warning about incorrect usage of the resource (do not fail) - - $parametersWithValues = $customModeParams.GetEnumerator() | Where-Object { $_.Value } - - if ($parametersWithValues.Count -gt 0) - { - $parametersWithValues | ForEach-Object { Write-Verbose ">> '$($_.Key)' was specified, the value is: '$($_.Value)'" } - - Write-Warning ("[WARNING]: Requested gateway mode is '$mode', the following parameters can only be used with Gateway mode 'Custom': " + - "$($parametersWithValues.Key -join ', '). These parameters will be ignored in the call to Set-RdDeploymentGatewayConfiguration to avoid error!") - } - } -} - ####################################################################### # The Get-TargetResource cmdlet. ####################################################################### @@ -88,41 +20,43 @@ function Get-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter()] - [string] + [System.String] $GatewayServer, [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ExternalFqdn, [Parameter()] [ValidateSet('DoNotUse', 'Custom', 'Automatic')] - [string] + [System.String] $GatewayMode, [Parameter()] [ValidateSet('Password', 'Smartcard', 'AllowUserToSelectDuringConnection')] - [string] + [System.String] $LogonMethod, [Parameter()] - [bool] + [System.Boolean] $UseCachedCredentials, [Parameter()] - [bool] + [System.Boolean] $BypassLocal ) - $result = $null - Write-Verbose "Getting RD Gateway configuration from broker '$ConnectionBroker'..." + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + + $result = $null + $config = Get-RDDeploymentGatewayConfiguration -ConnectionBroker $ConnectionBroker -ea SilentlyContinue if ($config) @@ -131,16 +65,13 @@ function Get-TargetResource Write-Verbose ">> RD Gateway mode: $($config.GatewayMode)" - $result = - @{ + $result = @{ ConnectionBroker = $ConnectionBroker - GatewayMode = $config.Gatewaymode.ToString() # Microsoft.RemoteDesktopServices.Management.GatewayUsage .ToString() + GatewayMode = $config.GatewayMode.ToString() # Microsoft.RemoteDesktopServices.Management.GatewayUsage .ToString() } if ($config.GatewayMode -eq 'Custom') { - # assert-expression ($config -is [Microsoft.RemoteDesktopServices.Management.CustomGatewaySettings]) - $result.GatewayExternalFqdn = $config.GatewayExternalFqdn $result.LogonMethod = $config.LogonMethod $result.UseCachedCredentials = $config.UseCachedCredentials @@ -170,40 +101,60 @@ function Set-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter()] - [string] + [System.String] $GatewayServer, [Parameter()] [ValidateSet('DoNotUse', 'Custom', 'Automatic')] - [string] + [System.String] $GatewayMode, [Parameter()] - [string] + [System.String] $ExternalFqdn, [Parameter()] [ValidateSet('Password', 'Smartcard', 'AllowUserToSelectDuringConnection')] - [string] + [System.String] $LogonMethod, [Parameter()] - [bool] + [System.Boolean] $UseCachedCredentials, [Parameter()] - [bool] + [System.Boolean] $BypassLocal ) Write-Verbose "Starting RD Gateway configuration for the RD deployment at broker '$ConnectionBroker'..." + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule + + $customModeParams = @( + 'ExternalFqdn', + 'LogonMethod', + 'UseCachedCredentials', + 'BypassLocal' + ) + # validate parameters - ValidateCustomModeParameters -mode $GatewayMode -ExternalFqdn $ExternalFqdn -LogonMethod $LogonMethod -UseCachedCredentials $UseCachedCredentials -BypassLocal $BypassLocal + if ($GatewayMode -eq 'Custom') + { + Assert-BoundParameter -BoundParameterList $PSBoundParameters -RequiredParameter $customModeParams + } + elseif ($PSBoundParameters.Keys.Where({ $_ -in $customModeParams })) + { + Write-Warning ('[WARNING]: Requested gateway mode is ''{0}'', the following parameters can only be used with Gateway mode ''Custom'': ''{1}''. These parameters will be ignored in the call to Set-RdDeploymentGatewayConfiguration to avoid error!' -f @( + $GatewayMode, + ($customModeParams -join ', ') + ) + ) + } if ($GatewayServer) { @@ -240,17 +191,22 @@ function Set-TargetResource { Write-Verbose "Adding RD Gateway server '$GatewayServer' to the deployment..." - Add-RDServer -server $GatewayServer -role RDS-Gateway -gatewayexternalfqdn $ExternalFqdn -connectionbroker $connectionBroker + Add-RDServer -Server $GatewayServer -Role RDS-Gateway -GatewayExternalFqdn $ExternalFqdn -ConnectionBroker $connectionBroker Write-Verbose 'Add-RDServer done.' } } - - Write-Verbose 'Calling Set-RdDeploymentGatewayConfiguration cmdlet...' + Write-Verbose 'Calling Set-RDDeploymentGatewayConfiguration cmdlet...' Write-Verbose ">> requested GatewayMode: $GatewayMode" + $setRdDeploymentGatewayConfigurationParams = @{ + ConnectionBroker = $ConnectionBroker + GatewayMode = $GatewayMode + Force = $true + } + if ($GatewayMode -eq 'Custom') { Write-Verbose ">> GatewayExternalFqdn: '$ExternalFqdn'" @@ -258,24 +214,16 @@ function Set-TargetResource Write-Verbose ">> UseCachedCredentials: $UseCachedCredentials" Write-Verbose ">> BypassLocal: $BypassLocal" - $setRdDeploymentGatewayConfigurationParams = @{ - ConnectionBroker = $ConnectionBroker - GatewayMode = $GatewayMode - GatewayExternalFqdn = $ExternalFqdn - LogonMethod = $LogonMethod - UseCachedCredentials = $UseCachedCredentials - BypassLocal = $BypassLocal - Force = $true - ErrorAction = 'Stop' - } - Set-RDDeploymentGatewayConfiguration @setRdDeploymentGatewayConfigurationParams - } - else # 'DoNotUse' or 'Automatic' - { - Set-RdDeploymentGatewayConfiguration -ConnectionBroker $ConnectionBroker -GatewayMode $GatewayMode -force + $setRdDeploymentGatewayConfigurationParams.GatewayExternalFqdn = $ExternalFqdn + $setRdDeploymentGatewayConfigurationParams.LogonMethod = $LogonMethod + $setRdDeploymentGatewayConfigurationParams.UseCachedCredentials = $UseCachedCredentials + $setRdDeploymentGatewayConfigurationParams.BypassLocal = $BypassLocal + $setRdDeploymentGatewayConfigurationParams.ErrorAction = 'Stop' } - Write-Verbose 'Set-RdDeploymentGatewayConfiguration done.' + Set-RDDeploymentGatewayConfiguration @setRdDeploymentGatewayConfigurationParams + + Write-Verbose 'Set-RDDeploymentGatewayConfiguration done.' } ####################################################################### @@ -289,63 +237,61 @@ function Test-TargetResource ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ConnectionBroker, [Parameter()] - [string] + [System.String] $GatewayServer, [Parameter()] [ValidateNotNullOrEmpty()] - [string] + [System.String] $ExternalFqdn, [Parameter()] [ValidateSet('DoNotUse', 'Custom', 'Automatic')] - [string] + [System.String] $GatewayMode, [Parameter()] [ValidateSet('Password', 'Smartcard', 'AllowUserToSelectDuringConnection')] - [string] + [System.String] $LogonMethod, [Parameter()] - [bool] + [System.Boolean] $UseCachedCredentials, [Parameter()] - [bool] + [System.Boolean] $BypassLocal ) - $config = Get-TargetResource @PSBoundParameters + Write-Verbose 'Testing RD Gateway usage name' - if ($config) - { - Write-Verbose 'Verifying RD Gateway usage name...' + $excludeProperties = @() - if ($config.GatewayMode -eq 'Custom' -and $config.GatewayMode -ieq $GatewayMode) - { - $result = $config.BypassLocal -eq $BypassLocal -and - $config.UseCachedCredentials -eq $UseCachedCredentials -and - $config.LogonMethod -eq $LogonMethod -and - $config.GatewayExternalFqdn -eq $ExternalFqdn - } - else - { - $result = ($config.GatewayMode -ieq $GatewayMode) - } - } - else + if ($GatewayMode -ne 'Custom') { - Write-Verbose 'Failed to retrieve RD Gateway configuration.' - $result = $false + $excludeProperties = @( + 'ExternalFqdn', + 'LogonMethod', + 'UseCachedCredentials', + 'BypassLocal' + ) + } + + $testDscParameterStateSplat = @{ + CurrentValues = Get-TargetResource @PSBoundParameters + DesiredValues = $PSBoundParameters + TurnOffTypeChecking = $false + SortArrayValues = $true + ExcludeProperties = $excludeProperties + Verbose = $VerbosePreference } - Write-Verbose "Test-TargetResource returning: $result" - return $result + return Test-DscParameterState @testDscParameterStateSplat } Export-ModuleMember -Function *-TargetResource diff --git a/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 b/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 index 2b48715..70ffa03 100644 --- a/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 +++ b/tests/Unit/MSFT_xRDGatewayConfiguration.Tests.ps1 @@ -1,454 +1,562 @@ -# $script:dscModuleName = 'xRemoteDesktopSessionHost' -# $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' - -# #region HEADER - -# function Invoke-TestSetup -# { -# try -# { -# Import-Module -Name DscResource.Test -Force -# } -# catch [System.IO.FileNotFoundException] -# { -# throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' -# } - -# $script:testEnvironment = Initialize-TestEnvironment ` -# -DSCModuleName $script:dscModuleName ` -# -DSCResourceName $script:dscResourceName ` -# -ResourceType 'Mof' ` -# -TestType 'Unit' -# } - -# function Invoke-TestCleanup -# { -# Restore-TestEnvironment -TestEnvironment $script:testEnvironment -# } - -# Invoke-TestSetup - -# try -# { -# InModuleScope $script:dscResourceName { -# $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'testbroker.fqdn' -# Gatewaymode = 'DoNotUse' -# } -# } - -# It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { -# (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# Gatewaymode = 'Custom' -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# ConnectionBroker = 'testbroker.fqdn' -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } -# } - -# $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn -# It 'Given a configured gateway, Get-TargetResource outputs property ' { -# param( -# [string]$Property -# ) - -# $getResult.$property | Should Not BeNullOrEmpty -# } -TestCases @( -# @{ -# Property = 'GatewayExternalFqdn' -# } -# @{ -# Property = 'BypassLocal' -# } -# @{ -# Property = 'LogonMethod' -# } -# @{ -# Property = 'UseCachedCredentials' -# } -# ) -# } - -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'testbroker.fqdn' -# Gatewaymode = 'DoNotUse' -# } -# } - -# $testSplat = @{ -# ConnectionBroker = 'testbroker.fqdn' -# GatewayMode = 'DoNotUse' -# ExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } - -# It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { -# Test-TargetResource @testSplat | Should be $true -# } - -# $testSplat.GatewayMode = 'Custom' -# It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# Gatewaymode = 'Custom' -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# ConnectionBroker = 'testbroker.fqdn' -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } -# } - -# $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.LogonMethod = 'Password' -# $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.ExternalFqdn = 'testgateway.external.fqdn' -# $testSplat.BypassLocal = $false -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.BypassLocal = $true -# $testSplat.UseCachedCredentials = $false -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.UseCachedCredentials = $true -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { -# Test-TargetResource @testSplat | Should be $true -# } -# } -# #endregion - - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when if GatewayMode is Custom and a parameter is missing.' { -# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | -# Should -Throw -# } -# } - -# $setSplat = @{ -# ConnectionBroker = 'testbroker.fqdn' -# GatewayServer = 'my.gateway.fqdn' -# GatewayMode = 'Custom' -# ExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } - -# Context 'Configuration changes performed by Set' { - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'my.gateway.fqdn' -# Roles = @( -# 'RDS-WEB-ACCESS', -# 'RDS-GATEWAY' -# ) -# } -# } -# Mock -CommandName Add-RDServer -# Mock -CommandName Set-RdDeploymentGatewayConfiguration - -# It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It -# } - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'testbroker.fqdn' -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } -# It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It -# } - -# It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { -# $ConnectionBroker -eq 'testbroker.fqdn' -and -# $GatewayMode -eq 'Custom' -and -# $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and -# $LogonMethod -eq 'Password' -and -# $UseCachedCredentials -eq $true -and -# $BypassLocal -eq $true -and -# $Force -eq $true -# } -# } - -# $setSplat.GatewayMode = 'DoNotUse' -# It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { -# $ConnectionBroker -eq 'testbroker.fqdn' -and -# $GatewayMode -eq 'DoNotUse' -and -# $Force -eq $true -# } -# } -# } -# } -# #endregion - -# } -# } -# finally -# { -# Invoke-TestCleanup -# } - -# #endregion HEADER - - -# try -# { -# Invoke-TestSetup - -# InModuleScope $script:DSCResourceName { -# $script:DSCResourceName = 'MSFT_xRDGatewayConfiguration' - -# Import-Module RemoteDesktop -Force - -# #region Function Get-TargetResource -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'testbroker.fqdn' -# Gatewaymode = 'DoNotUse' -# } -# } - -# It 'Given Gatway Configuration is DoNotUse Get-TargetResource returns GatewayMode DoNotUse' { -# (Get-TargetResource -ConnectionBroker testbroker.fqdn).GatewayMode | Should Be 'DoNotUse' -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# Gatewaymode = 'Custom' -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# ConnectionBroker = 'testbroker.fqdn' -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } -# } - -# $getResult = Get-TargetResource -ConnectionBroker testbroker.fqdn -# It 'Given a configured gateway, Get-TargetResource outputs property ' { -# param( -# [string]$Property -# ) - -# $getResult.$property | Should Not BeNullOrEmpty -# } -TestCases @( -# @{ -# Property = 'GatewayExternalFqdn' -# } -# @{ -# Property = 'BypassLocal' -# } -# @{ -# Property = 'LogonMethod' -# } -# @{ -# Property = 'UseCachedCredentials' -# } -# ) -# } - -# Describe "$($script:DSCResourceName)\Get-TargetResource" { - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'testbroker.fqdn' -# Gatewaymode = 'DoNotUse' -# } -# } - -# $testSplat = @{ -# ConnectionBroker = 'testbroker.fqdn' -# GatewayMode = 'DoNotUse' -# ExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } - -# It 'Given configured GateWayMode DoNotUse and desired GateWayMode DoNotUse test returns true' { -# Test-TargetResource @testSplat | Should be $true -# } - -# $testSplat.GatewayMode = 'Custom' -# It 'Given configured GateWayMode DoNotUse and desired GateWayMode Custom test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { -# [pscustomobject]@{ -# Gatewaymode = 'Custom' -# GatewayExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# ConnectionBroker = 'testbroker.fqdn' -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } -# } - -# $testSplat.LogonMethod = 'AllowUserToSelectDuringConnection' -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired LogonMethod AllowUserToSelectDuringConnection and current LogonMethod Password, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.LogonMethod = 'Password' -# $testSplat.ExternalFqdn = 'testgateway.new.external.fqdn' -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with different GatewayExternalFqdn, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.ExternalFqdn = 'testgateway.external.fqdn' -# $testSplat.BypassLocal = $false -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired BypassLocal false and current BypassLocal true, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.BypassLocal = $true -# $testSplat.UseCachedCredentials = $false -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with desired UseCachedCredentials false and current UseCachedCredentials true, test returns false' { -# Test-TargetResource @testSplat | Should be $false -# } - -# $testSplat.UseCachedCredentials = $true -# It 'Given configured GateWayMode Custom and desired GateWayMode Custom, with all properties validated, test returns true' { -# Test-TargetResource @testSplat | Should be $true -# } -# } -# #endregion - - -# #region Function Set-TargetResource -# Describe "$($script:DSCResourceName)\Set-TargetResource" { -# Context 'Parameter Values,Validations and Errors' { - -# It 'Should error when if GatewayMode is Custom and a parameter is missing.' { -# { Set-TargetResource -ConnectionBroker 'connectionbroker.lan' -GatewayMode 'Custom' -ErrorAction Stop } | -# Should -Throw -# } -# } - -# $setSplat = @{ -# ConnectionBroker = 'testbroker.fqdn' -# GatewayServer = 'my.gateway.fqdn' -# GatewayMode = 'Custom' -# ExternalFqdn = 'testgateway.external.fqdn' -# BypassLocal = $true -# LogonMethod = 'Password' -# UseCachedCredentials = $true -# } - -# Context 'Configuration changes performed by Set' { - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'my.gateway.fqdn' -# Roles = @( -# 'RDS-WEB-ACCESS', -# 'RDS-GATEWAY' -# ) -# } -# } -# Mock -CommandName Add-RDServer -# Mock -CommandName Set-RdDeploymentGatewayConfiguration - -# It 'Given the role RDS-GATEWAY is already installed, Add-RDServer is not called' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Add-RDServer -Times 0 -Scope It -# } - -# Mock -CommandName Get-RDServer -MockWith { -# [pscustomobject]@{ -# Server = 'testbroker.fqdn' -# Roles = @( -# 'RDS-WEB-ACCESS' -# ) -# } -# } -# It 'Given the role RDS-GATEWAY is missing, Add-RDServer is called' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Add-RDServer -Times 1 -Scope It -# } - -# It 'Given GateWayMode Custom, Set-RdDeploymentGatewayConfiguration is called with all required parameters' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { -# $ConnectionBroker -eq 'testbroker.fqdn' -and -# $GatewayMode -eq 'Custom' -and -# $GatewayExternalFqdn -eq 'testgateway.external.fqdn' -and -# $LogonMethod -eq 'Password' -and -# $UseCachedCredentials -eq $true -and -# $BypassLocal -eq $true -and -# $Force -eq $true -# } -# } - -# $setSplat.GatewayMode = 'DoNotUse' -# It 'Given GateWayMode DoNotUse, Set-RdDeploymentGatewayConfiguration is called with only ConnectionBroker, Gatewaymode and Force parameters' { -# Set-TargetResource @setSplat -# Assert-MockCalled -CommandName Set-RdDeploymentGatewayConfiguration -Times 1 -Scope It -ParameterFilter { -# $ConnectionBroker -eq 'testbroker.fqdn' -and -# $GatewayMode -eq 'DoNotUse' -and -# $Force -eq $true -# } -# } -# } -# } -# #endregion - -# } -# } -# finally -# { -# #region FOOTER -# Invoke-TestCleanup -# #endregion -# } +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[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' 3>&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 = 'xRemoteDesktopSessionHost' + $script:dscResourceName = 'MSFT_xRDGatewayConfiguration' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Load stub cmdlets and classes. + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\RemoteDesktop.stubs.psm1') + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload stub module + Remove-Module -Name RemoteDesktop.stubs -Force + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force +} + +Describe 'MSFT_xRDGatewayConfiguration\Get-TargetResource' -Tag 'Get' { + Context 'When the resource exists' { + BeforeAll { + Mock -CommandName Assert-Module + } + + Context 'When ''GatewayMode'' is not Custom' { + BeforeAll { + Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { + @{ + GatewayMode = [Microsoft.RemoteDesktopServices.Management.GatewayUsage]::Automatic + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.GatewayMode | Should -Be 'Automatic' + } + } + } + + Context 'When ''GatewayMode'' is Custom' { + BeforeAll { + Mock -CommandName Get-RDDeploymentGatewayConfiguration -MockWith { + @{ + GatewayMode = [Microsoft.RemoteDesktopServices.Management.GatewayUsage]::Custom + GatewayExternalFqdn = 'gateway.external.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -Be $testParams.ConnectionBroker + $result.GatewayMode | Should -Be 'Custom' + $result.GatewayExternalFqdn | Should -Be 'gateway.external.fqdn' + $result.LogonMethod | Should -Be 'Password' + $result.UseCachedCredentials | Should -BeTrue + $result.BypassLocal | Should -BeFalse + } + } + } + } + + Context 'When the resource does not exist' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Get-RDDeploymentGatewayConfiguration + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + } + + $result = Get-TargetResource @testParams + + $result.ConnectionBroker | Should -BeNullOrEmpty + $result.GatewayMode | Should -BeNullOrEmpty + $result.GatewayExternalFqdn | Should -BeNullOrEmpty + $result.LogonMethod | Should -BeNullOrEmpty + $result.UseCachedCredentials | Should -BeNullOrEmpty + $result.BypassLocal | Should -BeNullOrEmpty + } + } + } +} + +Describe 'MSFT_xRDGatewayConfiguration\Set-TargetResource' -Tag 'Set' { + Context 'When ''GatewayMode'' is not Custom' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Assert-BoundParameter -RemoveParameterType @('RequiredBehavior') + Mock -CommandName Set-RDDeploymentGatewayConfiguration + Mock -CommandName Get-RDServer + Mock -CommandName Add-RDServer + } + + Context 'When there are no Gateway Servers to check' { + Context 'When there are no RDServers to add' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'DoNotUse' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -eq $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + } + } + + Context 'When there are RDServers to add' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'DoNotUse' + GatewayServer = 'rdgateway.lan' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -eq $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + } + + Context 'When there are Gateway Servers to check' { + BeforeAll { + Mock -CommandName Get-RDServer -MockWith { + @( + [PSCustomObject] @{ + Server = 'rdgateway1.lan' + Roles = 'RDS-Gateway' + } + [PSCustomObject] @{ + Server = 'rdlic.lan' + Roles = 'RDS-Licensing' + } + [PSCustomObject] @{ + Server = 'rdweb.lan' + Roles = 'RDS-Web-Access' + } + ) + } + } + + Context 'When there are no RDServers to add' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'DoNotUse' + GatewayServer = 'rdgateway1.lan' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -eq $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + } + } + + Context 'When there are RDServers to add' { + BeforeAll { + Mock -CommandName Get-RDServer -MockWith { + @( + [PSCustomObject] @{ + Server = 'rdgateway2.lan' + Roles = 'RDS-Gateway' + } + [PSCustomObject] @{ + Server = 'rdlic.lan' + Roles = 'RDS-Licensing' + } + [PSCustomObject] @{ + Server = 'rdweb.lan' + Roles = 'RDS-Web-Access' + } + ) + } + } + + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'DoNotUse' + GatewayServer = 'rdgateway1.lan' + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -eq $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + } + } + + Context 'When ''GatewayMode'' is Custom' { + BeforeAll { + Mock -CommandName Assert-Module + Mock -CommandName Assert-BoundParameter -RemoveParameterType @('RequiredBehavior') + Mock -CommandName Set-RDDeploymentGatewayConfiguration + Mock -CommandName Get-RDServer + Mock -CommandName Add-RDServer + } + + Context 'When parameters are missing' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'Custom' + } + + { Set-TargetResource @testParams } | Should -Throw + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Get-RDServer -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + } + } + + Context 'When there are no RDServers to add' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'Custom' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -ne $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 0 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 0 -Scope It + } + } + + Context 'When there are RDServers to add' { + It 'Should call the correct mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'testbroker.lan' + GatewayMode = 'Custom' + GatewayServer = 'rdgateway.lan' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + $null = Set-TargetResource @testParams + } + + Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Assert-BoundParameter -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Set-RDDeploymentGatewayConfiguration -Exactly -Times 1 -Scope It -ParameterFilter { + $null -ne $GatewayExternalFqdn + } + Should -Invoke -CommandName Get-RDServer -Exactly -Times 1 -Scope It + Should -Invoke -CommandName Add-RDServer -Exactly -Times 1 -Scope It + } + } + } +} + +Describe 'MSFT_xRDLicenseConfiguration\Test-TargetResource' -Tag 'Test' { + Context 'When the resource is in the desired state' { + Context 'When ''GatewayMode'' is not Custom' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + + Context 'When ''GatewayMode'' is not Custom but unsupported parameters are supplied' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + + Context 'When ''GatewayMode'' is Custom' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Custom' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Custom' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + } + + Context 'When the resource is not in the desired state' { + Context 'When ''GatewayMode'' is not Custom' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'DoNotUse' + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + + Context 'When ''GatewayMode'' is not Custom but unsupported parameters are supplied' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Automatic' + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'DoNotUse' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + + Context 'When ''GatewayMode'' is Custom' { + BeforeAll { + Mock -CommandName Get-TargetResource -MockWith { + @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Custom' + ExternalFqdn = 'some.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $true + BypassLocal = $false + } + } + } + + It 'Should return the correct result' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams = @{ + ConnectionBroker = 'connectionbroker.lan' + GatewayMode = 'Custom' + ExternalFqdn = 'someother.fqdn' + LogonMethod = 'Password' + UseCachedCredentials = $false + BypassLocal = $false + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + } +} From 1eb7c58523eb769e0f67db70bdbf7f77f1aa37a7 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:26:43 +0000 Subject: [PATCH 31/44] Catch exception --- tests/Unit/MSFT_xRDServer.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/MSFT_xRDServer.Tests.ps1 b/tests/Unit/MSFT_xRDServer.Tests.ps1 index 85efe93..a8fa5b5 100644 --- a/tests/Unit/MSFT_xRDServer.Tests.ps1 +++ b/tests/Unit/MSFT_xRDServer.Tests.ps1 @@ -405,7 +405,7 @@ Describe 'MSFT_xRDServer\Set-TargetResource' -Tag 'Set' { GatewayExternalFqdn = 'gateway.external.fqdn' } - $null = Set-TargetResource @testParams + { Set-TargetResource @testParams } | Should -Throw } Should -Invoke -CommandName Assert-Module -Exactly -Times 1 -Scope It From 12bb279f99c3db0dc34f1bb1a39f5fc3bd009253 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:28:51 +0000 Subject: [PATCH 32/44] Fix error --- source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index 71772c5..d432296 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -177,7 +177,7 @@ function Set-TargetResource Write-Verbose "'Add-RDServer' threw $($rdsErrors.Count) errors." foreach ($rdsError in $rdsErrors) { - Write-Error -ErrorRecord $rdsError + New-InvalidOperationException -Message 'Add-RDServer errored' -ErrorRecord $rdsError } return } From 9bfbbbbf5c607dd1c449d2e9673d0e3de54eb1cf Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:40:42 +0000 Subject: [PATCH 33/44] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07a3b1d..9c03c30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Enable DocGenerator and move docs to wiki. Fixes [issue #101](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/101). - Renamed `master` branch to `main`. Fixes [issue #116](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/116). - Migrated tests to Pester 5. Fixes [issue #119](https://github.com/dsccommunity/xRemoteDesktopSessionHost/issues/119). +- Enable ModuleFast. +- Use pwsh for unit tests. +- Rename nested module to xRemoteDesktopSessionHost.Common. +- Make xRemoteDesktopSessionHost.Common a buildable module. +- DSCResources + - Use `Test-DscParameterState` where possible. + - Update formatting. + - Use fully qualified types. ## [2.1.0] - 2022-08-07 From cf79492682e51e49aef80ba521cfb1bf03a96060 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:42:03 +0000 Subject: [PATCH 34/44] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c03c30..37fbd67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `Test-DscParameterState` where possible. - Update formatting. - Use fully qualified types. + - Add changes from [PR #124](https://github.com/dsccommunity/xRemoteDesktopSessionHost/pulls/124) ## [2.1.0] - 2022-08-07 From d38d0a7c1edba8973f2c4e1759f56b290ae03674 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:50:50 +0000 Subject: [PATCH 35/44] Add empty Integration test file --- tests/Integration/MSFT_xRDServer.Integration.Tests.ps1 | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/Integration/MSFT_xRDServer.Integration.Tests.ps1 diff --git a/tests/Integration/MSFT_xRDServer.Integration.Tests.ps1 b/tests/Integration/MSFT_xRDServer.Integration.Tests.ps1 new file mode 100644 index 0000000..e69de29 From fc3affa9de630a73b15064bd1d791f8932b9e209 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:39:32 +0000 Subject: [PATCH 36/44] Update changelog link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37fbd67..fd92813 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,7 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use `Test-DscParameterState` where possible. - Update formatting. - Use fully qualified types. - - Add changes from [PR #124](https://github.com/dsccommunity/xRemoteDesktopSessionHost/pulls/124) + - Add changes from [PR #124](https://github.com/dsccommunity/xRemoteDesktopSessionHost/pull/124) ## [2.1.0] - 2022-08-07 From 41e6fbde1716de4e4e26143d12a61ace1f0a6a95 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:44:16 +0000 Subject: [PATCH 37/44] Remove commented code --- .../MSFT_xRDConnectionBrokerHAMode.Tests.ps1 | 64 ------------------- 1 file changed, 64 deletions(-) diff --git a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 index a2f1ca1..3eb19c0 100644 --- a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 +++ b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 @@ -56,70 +56,6 @@ AfterAll { Get-Module -Name $script:dscResourceName -All | Remove-Module -Force } -# Describe 'Testing MSFT_xRDConnectionBrokerHAMode' { -# Mock -CommandName Import-Module -MockWith {} -ParameterFilter { $Name -eq 'RemoteDesktop' } - -# Mock -CommandName Set-RDConnectionBrokerHighAvailability -ParameterFilter { $ClientAccessName -eq 'rdsfarm.contoso.com' } - -# Context 'Connection Broker not in HA mode' { -# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'RDCB1' -# ActiveManagementServer = '' -# ClientAccessName = '' -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# } -# } - -# $resourceNotConfiguredSplat = @{ -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# DatabaseSecondaryConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1.contoso.com;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# ClientAccessName = 'rdsfarm.contoso.com' -# DatabaseFilePath = 'C:\RDFiles\RemoteDesktopDeployment.mdf' -# } - -# It 'Get-TargetResource returns no active management server' { -# (Get-TargetResource @resourceNotConfiguredSplat).ActiveManagementServer | Should -BeNullOrEmpty -# } - -# It 'Test-TargetResource returns false' { -# Test-TargetResource @resourceNotConfiguredSplat | Should -BeFalse -# } - -# It 'Set-TargetResource runs Set-RDConnectionBrokerHighAvailability' { -# Set-TargetResource @resourceNotConfiguredSplat -# Assert-MockCalled -CommandName Set-RDConnectionBrokerHighAvailability -Times 1 -Exactly -ParameterFilter { -# $DatabaseConnectionString -eq 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -and -# $ClientAccessName -eq 'rdsfarm.contoso.com' -# } -# } -# } - -# Context 'Connection Broker in HA mode' { -# Mock -CommandName Get-RDConnectionBrokerHighAvailability -MockWith { -# [pscustomobject]@{ -# ConnectionBroker = 'RDCB1' -# ActiveManagementServer = 'RDCB1' -# ClientAccessName = 'rdsfarm.contoso.com' -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# } -# } - -# $resourceConfiguredSplat = @{ -# DatabaseConnectionString = 'DRIVER=SQL Server Native Client 11.0;SERVER=RDDB1;Trusted_Connection=Yes;APP=Remote Desktop Services Connection Broker;Database=RDS' -# ClientAccessName = 'rdsfarm.contoso.com' -# } - -# It 'Get-TargetResource returns an active management server' { -# (Get-TargetResource @resourceConfiguredSplat).ActiveManagementServer | Should -Be 'RDCB1' -# } - -# It 'Test-TargetResource returns true' { -# Test-TargetResource @resourceConfiguredSplat | Should -BeTrue -# } -# } -# } - Describe 'MSFT_xRDConnectionBrokerHAMode\Get-TargetResource' -Tag 'Get' { Context 'When the resource is in the desired state' { Context 'When the connection broker is not local' { From d7c76f8878b43779ccf9f0d534a348c51f5f5985 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:50:24 +0000 Subject: [PATCH 38/44] Update from comments --- ...xRemoteDesktopSessionHostOsRequirement.ps1 | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 index a827233..a417fe1 100644 --- a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 @@ -1,4 +1,21 @@ +<# + .SYNOPSIS + Verifies that the operating system meets the Remote Desktop Session Host requirement. + + .DESCRIPTION + Returns $true when Get-xRemoteDesktopSessionHostOsVersion reports at least Windows Server 2012 (6.2.9200.0); otherwise returns $false. + + .OUTPUTS + System.Boolean + + Indicates whether the OS version is supported. +#> + function Test-xRemoteDesktopSessionHostOsRequirement { - return (Get-xRemoteDesktopSessionHostOsVersion) -ge ([Version]::new(6, 2, 9200, 0)) + [CmdletBinding()] + [OutputType([System.Boolean])] + param () + + return (Get-xRemoteDesktopSessionHostOsVersion) -ge ([System.Version]::new(6, 2, 9200, 0)) } From f010ebf3b583396be79b56c1f9619651080e634f Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:06:08 +0000 Subject: [PATCH 39/44] Add example --- .../Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 index a417fe1..9b57686 100644 --- a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 @@ -9,6 +9,11 @@ System.Boolean Indicates whether the OS version is supported. + + .EXAMPLE + Test-xRemoteDesktopSessionHostOsRequirement + + Returns $true if the OS is Windows Server 2012 or later, otherwise $false. #> function Test-xRemoteDesktopSessionHostOsRequirement From eb1a0a2f5e73b05e994751cfe9f37a15a29f70ec Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:06:19 +0000 Subject: [PATCH 40/44] Fix test title --- tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 index 3eb19c0..ff86ae3 100644 --- a/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 +++ b/tests/Unit/MSFT_xRDConnectionBrokerHAMode.Tests.ps1 @@ -208,7 +208,7 @@ Describe 'MSFT_xRDConnectionBrokerHAMode\Test-TargetResource' -Tag 'Test' { } } - Context 'When the resource is in the desired state' { + Context 'When the resource is not in the desired state' { BeforeAll { Mock -CommandName Get-TargetResource -MockWith { @{ From 6927af8940506a12d756ba0c2d355e047d5ac8d6 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:13:21 +0000 Subject: [PATCH 41/44] Fix indent --- .../Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 index 9b57686..2476cf2 100644 --- a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Test-xRemoteDesktopSessionHostOsRequirement.ps1 @@ -11,9 +11,9 @@ Indicates whether the OS version is supported. .EXAMPLE - Test-xRemoteDesktopSessionHostOsRequirement + Test-xRemoteDesktopSessionHostOsRequirement - Returns $true if the OS is Windows Server 2012 or later, otherwise $false. + Returns $true if the OS is Windows Server 2012 or later, otherwise $false. #> function Test-xRemoteDesktopSessionHostOsRequirement From 3318ec3a8d8a5f8fd4075b4cbf9882d623b0ba7e Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:13:42 +0000 Subject: [PATCH 42/44] Add param block and output type --- .../Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 index 928cf38..f48a087 100644 --- a/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 +++ b/source/Modules/xRemoteDesktopSessionHost.Common/Public/Get-xRemoteDesktopSessionHostOsVersion.ps1 @@ -1,4 +1,8 @@ function Get-xRemoteDesktopSessionHostOsVersion { + [CmdletBinding()] + [OutputType([System.Version])] + param () + return [Environment]::OSVersion.Version } From 84a6ac214cdeedf6186d0d8f1f3885be1531a782 Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 2 Nov 2025 12:58:27 +0000 Subject: [PATCH 43/44] Use Get-ComputerName --- .../MSFT_xRDConnectionBrokerHAMode.psm1 | 2 +- source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 index e399177..e595e1f 100644 --- a/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 +++ b/source/DSCResources/MSFT_xRDConnectionBrokerHAMode/MSFT_xRDConnectionBrokerHAMode.psm1 @@ -11,7 +11,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' -$localhost = [System.Net.Dns]::GetHostEntry(($env:COMPUTERNAME)).HostName +$localhost = Get-ComputerName -FullyQualifiedDomainName ####################################################################### # The Get-TargetResource cmdlet. diff --git a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 index d432296..4bd9382 100644 --- a/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 +++ b/source/DSCResources/MSFT_xRDServer/MSFT_xRDServer.psm1 @@ -9,7 +9,7 @@ if (-not (Test-xRemoteDesktopSessionHostOsRequirement)) throw 'The minimum OS requirement was not met.' } -$localhost = [System.Net.Dns]::GetHostEntry(($env:COMPUTERNAME)).HostName +$localhost = Get-ComputerName -FullyQualifiedDomainName ####################################################################### # The Get-TargetResource cmdlet. From 3c02d006dc730e6b2c6750bd256f78ec256cfd1e Mon Sep 17 00:00:00 2001 From: Daniel Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 2 Nov 2025 12:58:38 +0000 Subject: [PATCH 44/44] Fix trailing whitespace --- .../MSFT_xRDLicenseConfiguration.psm1 | 2 +- .../MSFT_xRDSessionCollectionConfiguration.psm1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 index edf7c95..bffe15f 100644 --- a/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDLicenseConfiguration/MSFT_xRDLicenseConfiguration.psm1 @@ -83,7 +83,7 @@ function Set-TargetResource ) Write-Verbose 'Starting RD License server configuration...' - + Assert-Module -ModuleName 'RemoteDesktop' -ImportModule Write-Verbose ">> RD Connection Broker: $($ConnectionBroker.ToLower())" diff --git a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 index 81f4dec..f9dcee2 100644 --- a/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 +++ b/source/DSCResources/MSFT_xRDSessionCollectionConfiguration/MSFT_xRDSessionCollectionConfiguration.psm1 @@ -291,7 +291,7 @@ function Set-TargetResource [System.String[]] $ExcludeFilePath ) - + Write-Verbose 'Setting DSC collection properties' Assert-Module -ModuleName 'RemoteDesktop' -ImportModule