From 61f4b85b2637403aa7ec3717e8556066fb467e68 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Mon, 8 Dec 2025 22:45:12 +0100 Subject: [PATCH 01/24] Add to win_psDscAdapter function for extraxting string from PSCredentaials --- .../psDscAdapter/win_psDscAdapter.psm1 | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index c206d89d9..6833a45eb 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -298,6 +298,36 @@ function Get-DscResourceObject { return $desiredState } +## Primitive value for PScredentials + +function Get-PrimitiveValueString { + param([Parameter(Mandatory)][object]$Value) + + # If already a string → done + if ($Value -is [string]) { + return $Value + } + + # If it's a hashtable or PSCustomObject → unwrap + if ($Value -is [hashtable] -or $Value -is [pscustomobject]) { + $props = $Value.psobject.Properties + + # Case 1: { secureString = "admin" } + if ($props.Name -contains 'secureString') { + return $Value.secureString + } + + # Case 2: nested object e.g. @{ value = @{ secureString = "admin" }} + if ($props.Count -eq 1) { + return Get-PrimitiveValueString $props.Value + } + + "Cannot extract primitive value from nested object: $($Value | Out-String)" | Write-DscTrace -Operation Error + } + + "Unsupported type '$($Value.GetType().FullName)' for primitive extraction" | Write-DscTrace -Operation Error +} + # Get the actual state using DSC Get method from any type of DSC resource function Invoke-DscOperation { param( From cd5df41c2f00573839a84fd2c8b0ca228fde0f40 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Mon, 8 Dec 2025 23:10:49 +0100 Subject: [PATCH 02/24] Add to win_psDscAdapter for ScriptBase resources PSCredentails fix convert to System.Management.Automation.PSCredentia --- .../win_powershell_script_resources.dsc.yaml | 48 +++++++++++++++++++ .../psDscAdapter/win_psDscAdapter.psm1 | 6 ++- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml diff --git a/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml b/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml new file mode 100644 index 000000000..45ba1f068 --- /dev/null +++ b/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml @@ -0,0 +1,48 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +parameters: + SafemodeAdministratorPassword: + type: string + showSecrets: + type: bool + defaultValue: true + cred: + type: secureObject +metadata: + Microsoft.DSC: + requiredSecurityContext: elevated # this is the default and just used as an example indicating this config works for admins and non-admins +resources: +- name: Use class PowerShell resources + type: Microsoft.Windows/WindowsPowerShell + properties: + resources: + - name: Windows Features Install ADS + type: PSDesiredStateConfiguration/WindowsFeature + properties: + ensure: Present + name: AD-Domain-Services + - name: Windows Features Install ADS RSAT + type: PSDesiredStateConfiguration/WindowsFeature + properties: + ensure: Present + name: RSAT-AD-PowerShell + dependsOn: + - "[resourceId('PSDesiredStateConfiguration/WindowsFeature','Windows Features Install ADS')]" + - name: Active Directory Forest + type: ActiveDirectoryDsc/ADDomain + properties: + DomainName: contoso.com + Credential: + Username: "[parameters('cred').username]" + Password: "[parameters('cred').password]" + SafemodeAdministratorPassword: + Username: "[parameters('cred').username]" + Password: "[parameters('cred').password]" + ForestMode: WinThreshold +- name: secureObject + type: Microsoft.DSC.Debug/Echo + properties: + output: "[parameters('cred')]" + showSecrets: "[parameters('showSecrets')]" \ No newline at end of file diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index 6833a45eb..3bb36b7ee 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -398,7 +398,11 @@ function Invoke-DscOperation { "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 } - $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($_.Value.Username, (ConvertTo-SecureString -AsPlainText $_.Value.Password -Force)) + + $username = Get-PrimitiveValueString $_.Value.Username + $password = $_.Value.Password | ConvertTo-SecureString -AsPlainText -Force + $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) + } else { $property.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 0a7c6aa5b2f8d8c364ae8c0439d17caf1955a3ac Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 18 Dec 2025 10:15:06 +0100 Subject: [PATCH 03/24] Fix win_psDscAdapter for ScriptBase resources PSCredentails fix convert to System.Management.Automation.PSCredentia - code optimalization remove unused function --- .../psDscAdapter/win_psDscAdapter.psm1 | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index 3bb36b7ee..864099437 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -298,35 +298,6 @@ function Get-DscResourceObject { return $desiredState } -## Primitive value for PScredentials - -function Get-PrimitiveValueString { - param([Parameter(Mandatory)][object]$Value) - - # If already a string → done - if ($Value -is [string]) { - return $Value - } - - # If it's a hashtable or PSCustomObject → unwrap - if ($Value -is [hashtable] -or $Value -is [pscustomobject]) { - $props = $Value.psobject.Properties - - # Case 1: { secureString = "admin" } - if ($props.Name -contains 'secureString') { - return $Value.secureString - } - - # Case 2: nested object e.g. @{ value = @{ secureString = "admin" }} - if ($props.Count -eq 1) { - return Get-PrimitiveValueString $props.Value - } - - "Cannot extract primitive value from nested object: $($Value | Out-String)" | Write-DscTrace -Operation Error - } - - "Unsupported type '$($Value.GetType().FullName)' for primitive extraction" | Write-DscTrace -Operation Error -} # Get the actual state using DSC Get method from any type of DSC resource function Invoke-DscOperation { @@ -399,7 +370,7 @@ function Invoke-DscOperation { exit 1 } - $username = Get-PrimitiveValueString $_.Value.Username + $username = $_.Value.Username.secureString $password = $_.Value.Password | ConvertTo-SecureString -AsPlainText -Force $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) From 82aeec4e43c6026a604c8c1de3fefe8d6a3cfb00 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 18 Dec 2025 10:24:17 +0100 Subject: [PATCH 04/24] psDscAdapter for ClassBase resources PSCredentails fix convert to System.Management.Automation.PSCredential --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 1c22fb0b5..b251121e0 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -467,7 +467,9 @@ function Invoke-DscOperation { "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 } - $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($_.Value.Username, (ConvertTo-SecureString -AsPlainText $_.Value.Password -Force)) + $username = $_.Value.secureObject.username + $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force + $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } From 506ba9f83ca15c97078334e81694979dfd6d9503 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 18 Dec 2025 10:30:57 +0100 Subject: [PATCH 05/24] Add for testClass testing yaml with secureObject - main file and parameters --- .../Tests/class_ps_resources_secret.dsc.yaml | 29 +++++++++++ .../class_ps_resources_secret.parameters.yaml | 4 ++ .../win_powershell_script_resources.dsc.yaml | 48 ------------------- 3 files changed, 33 insertions(+), 48 deletions(-) create mode 100644 adapters/powershell/Tests/class_ps_resources_secret.dsc.yaml create mode 100644 adapters/powershell/Tests/class_ps_resources_secret.parameters.yaml delete mode 100644 adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml diff --git a/adapters/powershell/Tests/class_ps_resources_secret.dsc.yaml b/adapters/powershell/Tests/class_ps_resources_secret.dsc.yaml new file mode 100644 index 000000000..59f7625ae --- /dev/null +++ b/adapters/powershell/Tests/class_ps_resources_secret.dsc.yaml @@ -0,0 +1,29 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +parameters: + showSecrets: + type: bool + defaultValue: true + cred: + type: secureObject +metadata: + Microsoft.DSC: + requiredSecurityContext: elevated # this is the default and just used as an example indicating this config works for admins and non-admins +resources: +- name: Working with classic DSC resources + type: Microsoft.DSC/PowerShell + properties: + resources: + - name: Class-resource Info + type: TestClassResource/TestClassResource + properties: + Name: TestClassResource1 + Prop1: ValueForProp1 + Credential: "[parameters('cred')]" +- name: SecureObject + type: Microsoft.DSC.Debug/Echo + properties: + output: "[parameters('cred')]" + showSecrets: "[parameters('showSecrets')]" diff --git a/adapters/powershell/Tests/class_ps_resources_secret.parameters.yaml b/adapters/powershell/Tests/class_ps_resources_secret.parameters.yaml new file mode 100644 index 000000000..3397a1029 --- /dev/null +++ b/adapters/powershell/Tests/class_ps_resources_secret.parameters.yaml @@ -0,0 +1,4 @@ +parameters: + cred: + username: admin + password: {To be Ovveride} \ No newline at end of file diff --git a/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml b/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml deleted file mode 100644 index 45ba1f068..000000000 --- a/adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json -parameters: - SafemodeAdministratorPassword: - type: string - showSecrets: - type: bool - defaultValue: true - cred: - type: secureObject -metadata: - Microsoft.DSC: - requiredSecurityContext: elevated # this is the default and just used as an example indicating this config works for admins and non-admins -resources: -- name: Use class PowerShell resources - type: Microsoft.Windows/WindowsPowerShell - properties: - resources: - - name: Windows Features Install ADS - type: PSDesiredStateConfiguration/WindowsFeature - properties: - ensure: Present - name: AD-Domain-Services - - name: Windows Features Install ADS RSAT - type: PSDesiredStateConfiguration/WindowsFeature - properties: - ensure: Present - name: RSAT-AD-PowerShell - dependsOn: - - "[resourceId('PSDesiredStateConfiguration/WindowsFeature','Windows Features Install ADS')]" - - name: Active Directory Forest - type: ActiveDirectoryDsc/ADDomain - properties: - DomainName: contoso.com - Credential: - Username: "[parameters('cred').username]" - Password: "[parameters('cred').password]" - SafemodeAdministratorPassword: - Username: "[parameters('cred').username]" - Password: "[parameters('cred').password]" - ForestMode: WinThreshold -- name: secureObject - type: Microsoft.DSC.Debug/Echo - properties: - output: "[parameters('cred')]" - showSecrets: "[parameters('showSecrets')]" \ No newline at end of file From 49c381c52c786c9952d4246c8ddbaa6f981307cf Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 18 Dec 2025 10:41:49 +0100 Subject: [PATCH 06/24] Fix win_psDscAdapter for ClassBase resources PSCredentails fix convert to System.Management.Automation.PSCredential --- adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index 864099437..a922140e6 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -427,7 +427,11 @@ function Invoke-DscOperation { "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 } - $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($_.Value.Username, (ConvertTo-SecureString -AsPlainText $_.Value.Password -Force)) + + $username = $_.Value.secureObject.username + $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force + + $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 403e6b570d87fda67fa82e3b72ba740ef4e1dab2 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 18 Dec 2025 10:53:33 +0100 Subject: [PATCH 07/24] Fix win_psDscAdapter for ClassBase resources PSCredentails fix convert to System.Management.Automation.PSCredential - if change to recognize user information --- adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index a922140e6..cf9fff468 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -423,11 +423,11 @@ function Invoke-DscOperation { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name Write-DscTrace -Operation Debug -Message "Property type: $($validateProperty.PropertyType)" if ($validateProperty.PropertyType -eq 'PSCredential') { - if (-not $_.Value.Username -or -not $_.Value.Password) { + if (-not $_.Value.secureObject.Username -or -not $_.Value.secureObject.Password) { "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 } - + $username = $_.Value.secureObject.username $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force From 352dd699c93314bd1bae128b1794539632ba12c4 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Sun, 21 Dec 2025 22:43:32 +0100 Subject: [PATCH 08/24] Ad unit test for credentaials in powershellgroup.config.tests.ps1 - valid secure object --- .../Tests/powershellgroup.config.tests.ps1 | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/adapters/powershell/Tests/powershellgroup.config.tests.ps1 b/adapters/powershell/Tests/powershellgroup.config.tests.ps1 index 09df8968f..525a69764 100644 --- a/adapters/powershell/Tests/powershellgroup.config.tests.ps1 +++ b/adapters/powershell/Tests/powershellgroup.config.tests.ps1 @@ -286,19 +286,28 @@ Describe 'PowerShell adapter resource tests' { It 'Config works with credential object' { $yaml = @" `$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json + parameters: + Credential: + type: secureObject + defaultValue: + username: User + password: Password resources: - - name: Class-resource Info - type: TestClassResource/TestClassResource + - name: Working with classic DSC resources + type: Microsoft.DSC/PowerShell properties: - Name: 'TestClassResource' - Credential: - UserName: 'User' - Password: 'Password' + resources: + - name: Class-resource Info + type: TestClassResource/TestClassResource + properties: + Name: TestClassResource1 + Prop1: ValueForProp1 + Credential: "[parameters('Credential')]" "@ $out = dsc config get -i $yaml | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 - $out.results.result.actualstate.Credential.UserName | Should -Be 'User' - $out.results.result.actualState.result.Credential.Password.Length | Should -Not -BeNullOrEmpty + $out.results.result.actualstate.result.properties.Credential.UserName | Should -Be 'User' + $out.results.result.actualState.result.properties.Credential.Password.Length | Should -Not -BeNullOrEmpty } It 'Config does not work when credential properties are missing required fields' { From 4af004e043e307028cfe379868cba21e37bac70c Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Mon, 22 Dec 2025 22:30:03 +0100 Subject: [PATCH 09/24] Ad unit test for credentaials in win_powershellgroup.tests.ps1 - valid secure object --- adapters/powershell/Tests/win_powershellgroup.tests.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index f5c505478..1e32df218 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -167,6 +167,12 @@ resources: It 'Config works with credential object' { $yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json + parameters: + Credential: + type: secureObject + defaultValue: + username: MyUser + password: MyPassword resources: - name: Cred test type: PSClassResource/PSClassResource From dd7425e34e5cdb12fd1c9b64087aff9f74dba6a3 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Mon, 22 Dec 2025 22:31:53 +0100 Subject: [PATCH 10/24] Ad unit test for credentaials in win_powershellgroup.tests.ps1 - valid secure object --- adapters/powershell/Tests/win_powershellgroup.tests.ps1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index 1e32df218..01e06d1a2 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -178,9 +178,7 @@ resources: type: PSClassResource/PSClassResource properties: Name: Test - Credential: - UserName: 'MyUser' - Password: 'MyPassword' + Credential: "[parameters('Credential')]" '@ $out = dsc -l debug config set -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json From b8cff4a5c9fb47d0fd4e3a5a35d30277747bbcfb Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Sun, 11 Jan 2026 23:57:57 +0100 Subject: [PATCH 11/24] Add script base DSC resources for testing credentials --- .../CredentialValidation.psm1 | 60 ++++++++++++++++++ .../CredentialValidation.schema.mof | 6 ++ .../TestScriptBaseDSC/TestScriptBaseDSC.psd1 | 62 +++++++++++++++++++ .../TestScriptBaseDSC/TestScriptBaseDSC.psm1 | 2 + 4 files changed, 130 insertions(+) create mode 100644 adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 create mode 100644 adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.schema.mof create mode 100644 adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psd1 create mode 100644 adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psm1 diff --git a/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 new file mode 100644 index 000000000..c1692d7ba --- /dev/null +++ b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 @@ -0,0 +1,60 @@ +function Get-TargetResource { + [OutputType([Hashtable])] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + ) + Write-Verbose "[GET] Get Function running" + return @{ + Name = $Name + Credential = $Credential + } + +} + +function Test-TargetResource { + [OutputType([System.Boolean])] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + + ) + Write-Verbose "[TEST]Checking credentials" + Write-Verbose "[TEST]Checking credentials UserName: $($Credential.UserName)" + Write-Verbose "[TEST]Checking credentials Password: $($Credential.Password)" + + if ($null -eq $Credential) { + throw 'Credential property is required' + return $false + } + + if ($Credential.UserName -ne 'MyUser') { + throw 'Invalid user name' + return $false + } else { + return $true + } + +} + +function Set-TargetResource { + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + + ) + Write-Verbose "[SET]Credential cannot be remediated by DSC." +} \ No newline at end of file diff --git a/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.schema.mof b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.schema.mof new file mode 100644 index 000000000..bb957b8f4 --- /dev/null +++ b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.schema.mof @@ -0,0 +1,6 @@ +[ClassVersion("1.0.0.0"), FriendlyName("CredentialValidation")] +class CredentialValidation : OMI_BaseResource +{ + [Key] string Name; + [Required, Description("Test Credentials for Script Base"), EmbeddedInstance("MSFT_Credential")] String Credential; +}; \ No newline at end of file diff --git a/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psd1 b/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psd1 new file mode 100644 index 000000000..3532b1706 --- /dev/null +++ b/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psd1 @@ -0,0 +1,62 @@ +@{ + # Script module or binary module file associated with this manifest. + RootModule = 'TestScriptBaseDSC.psm1' + + # Version number of this module. + moduleVersion = '0.0.1' + + # ID used to uniquely identify this module + GUID = 'c3775be8-84a1-43f5-a99c-1b9f2d6bc178' + + # Author of this module + Author = '' + + # Company or vendor of this module + CompanyName = '' + + # Copyright statement for this module + Copyright = '' + + # Description of the functionality provided by this module + Description = '' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.0' + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() + + # Dsc Resources to export from this module + DscResourcesToExport = @('CredentialValidation') + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = '' + + # A URL to an icon representing this module. + IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = '' + + # Set to a prerelease string value if the release should be a prerelease. + Prerelease = '' + } # End of PSData hashtable + } # End of PrivateData hashtable +} diff --git a/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psm1 b/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psm1 new file mode 100644 index 000000000..454e63eee --- /dev/null +++ b/adapters/powershell/Tests/TestScriptBaseDSC/TestScriptBaseDSC.psm1 @@ -0,0 +1,2 @@ +# Root module for CredentialValidationDsc +# No code required \ No newline at end of file From 63cd935d64aa7176c2f61c4c299e6600ad4b2308 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 00:21:38 +0100 Subject: [PATCH 12/24] Add script base DSC resources for testing credentials and fix win_PsDsc for script resources --- .../CredentialValidation.psm1 | 32 +++++++++++++++++-- .../psDscAdapter/win_psDscAdapter.psm1 | 6 ++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 index c1692d7ba..610433094 100644 --- a/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 +++ b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 @@ -1,4 +1,10 @@ +$VerbosePreference = 'SilentlyContinue' +$InformationPreference = 'SilentlyContinue' +$ProgressPreference = 'Continue' +$ErrorActionPreference = 'SilentlyContinue' + function Get-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [OutputType([Hashtable])] param ( [Parameter(Mandatory)] @@ -17,6 +23,7 @@ function Get-TargetResource { } function Test-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [OutputType([System.Boolean])] param ( [Parameter(Mandatory)] @@ -33,19 +40,24 @@ function Test-TargetResource { if ($null -eq $Credential) { throw 'Credential property is required' + $inDesiredState = $false return $false } if ($Credential.UserName -ne 'MyUser') { throw 'Invalid user name' - return $false + $inDesiredState = $false } else { - return $true + $inDesiredState = $true } + + + return $inDesiredState } function Set-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] [CmdletBinding()] param ( [Parameter(Mandatory)] @@ -56,5 +68,21 @@ function Set-TargetResource { $Credential ) + + if ($null -eq $Credential) { + throw 'Credential property is required' + $inDesiredState = $false + return $false + } + + if ($Credential.UserName -ne 'MyUser') { + throw 'Invalid user name' + $inDesiredState = $false + } else { + $inDesiredState = $true + } + + + return $inDesiredState Write-Verbose "[SET]Credential cannot be remediated by DSC." } \ No newline at end of file diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index cf9fff468..35da8067e 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -365,13 +365,13 @@ function Invoke-DscOperation { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name Write-DscTrace -Operation Debug -Message "Property type: $($validateProperty.PropertyType)" if ($validateProperty -and $validateProperty.PropertyType -eq '[PSCredential]') { - if (-not $_.Value.Username -or -not $_.Value.Password) { + if (-not $_.Value.secureObject.Username -or -not $_.Value.secureObject.Password) { "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 } - $username = $_.Value.Username.secureString - $password = $_.Value.Password | ConvertTo-SecureString -AsPlainText -Force + $username = $_.Value.secureObject.username + $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) } else { From 73dacdefeabc90c61e57571917288f5699b46027 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 00:43:18 +0100 Subject: [PATCH 13/24] Script base yaml DSC resources for credential valid --- .../Tests/script_ps_resources_secret.dsc.yaml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 adapters/powershell/Tests/script_ps_resources_secret.dsc.yaml diff --git a/adapters/powershell/Tests/script_ps_resources_secret.dsc.yaml b/adapters/powershell/Tests/script_ps_resources_secret.dsc.yaml new file mode 100644 index 000000000..485764024 --- /dev/null +++ b/adapters/powershell/Tests/script_ps_resources_secret.dsc.yaml @@ -0,0 +1,20 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +parameters: + cred: + type: secureObject + defaultValue: + username: MyUser + password: Password +resources: +- name: Working with classic DSC resources + type: Microsoft.Windows/WindowsPowerShell + properties: + resources: + - name: Script-resource Info + type: TestScriptBaseDSC/CredentialValidation + properties: + Name: TestScriptResource1 + Credential: "[parameters('cred')]" From 34b6b735bcbf4df1a530764800adbdbab2b7c134 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 11:45:47 +0100 Subject: [PATCH 14/24] Add script base resource test in win_powershellgroup.tests.ps1 --- .../Tests/win_powershellgroup.tests.ps1 | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index 01e06d1a2..a0de6e0d9 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -204,6 +204,88 @@ resources: (Get-Content -Path "$testdrive/error.log" -Raw) | Should -BeLike "*ERROR*Credential object 'Credential' requires both 'username' and 'password' properties*" -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) } + ## Scipt base resources test + + It 'Config works with credential object with Script base resources' { + +$inDesiredState = $true +$yaml = @' +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +resources: +- name: Working with classic DSC resources + type: Microsoft.Windows/WindowsPowerShell + properties: + resources: + - name: Script-resource Info + type: TestScriptBaseDSC/CredentialValidation + properties: + Name: TestScriptResource1 + Credential: + username: MyUser + password: Password +'@ + + +$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json +$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) +$out.results[0].result.inDesiredState | Should -Be $inDesiredState + +} + + It 'Config dont works with credential object with Script base resources - wrong user' { + +$inDesiredState = $true +$yaml = @' +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +resources: +- name: Working with classic DSC resources + type: Microsoft.Windows/WindowsPowerShell + properties: + resources: + - name: Script-resource Info + type: TestScriptBaseDSC/CredentialValidation + properties: + Name: TestScriptResource1 + Credential: + username: MyUser1 + password: Password +'@ + + +$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json +$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) +$out.results[0].result.inDesiredState | Should -Be $inDesiredState + +} + + It 'Config dont works with credential object with Script base resources - No passowrd' { + +$inDesiredState = $true +$yaml = @' +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +resources: +- name: Working with classic DSC resources + type: Microsoft.Windows/WindowsPowerShell + properties: + resources: + - name: Script-resource Info + type: TestScriptBaseDSC/CredentialValidation + properties: + Name: TestScriptResource1 + Credential: + username: MyUser + Notpassword: Password +'@ + + +$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json +$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) +$out.results[0].result.inDesiredState | Should -Be $inDesiredState + +} + + + It 'List works with class-based PS DSC resources' { $out = dsc resource list --adapter Microsoft.Windows/WindowsPowerShell 2> "$testdrive/error.log" | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) From 877ee8bc3c6413eb6a2d539f752cedfceb0ae230 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 12:01:02 +0100 Subject: [PATCH 15/24] Add to win_psDscAdapter.psm1 support for secure object and test value parameters - credentials --- .../psDscAdapter/win_psDscAdapter.psm1 | 85 ++++++++++++++++--- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index 35da8067e..f86210992 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -365,14 +365,45 @@ function Invoke-DscOperation { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name Write-DscTrace -Operation Debug -Message "Property type: $($validateProperty.PropertyType)" if ($validateProperty -and $validateProperty.PropertyType -eq '[PSCredential]') { - if (-not $_.Value.secureObject.Username -or -not $_.Value.secureObject.Password) { - "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error + + "$($_.Value)" | Write-DscTrace -Operation Warn + + $hasSecureCred = + $_.Value.secureObject.Username -and + $_.Value.secureObject.Password + + $hasTextCred = + $_.Value.Username -and + $_.Value.Password + + if (-not $hasSecureCred -and -not $hasTextCred) { + "$($_.Value)" | Write-DscTrace -Operation Warn + "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | + Write-DscTrace -Operation Error exit 1 } - $username = $_.Value.secureObject.username - $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force - $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) + if ($hasSecureCred) { + "Credential object '$($_.Name)' - SecureObject" | Write-DscTrace -Operation Info + + $username = $_.Value.secureObject.Username + $password = $_.Value.secureObject.Password | + ConvertTo-SecureString -AsPlainText -Force + + $property.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } + elseif ($hasTextCred) { + "Credential object '$($_.Name)' - Text" | Write-DscTrace -Operation Info + + $username = $_.Value.Username + $password = $_.Value.Password | + ConvertTo-SecureString -AsPlainText -Force + + $property.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } + } else { $property.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } @@ -423,15 +454,43 @@ function Invoke-DscOperation { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name Write-DscTrace -Operation Debug -Message "Property type: $($validateProperty.PropertyType)" if ($validateProperty.PropertyType -eq 'PSCredential') { - if (-not $_.Value.secureObject.Username -or -not $_.Value.secureObject.Password) { - "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error - exit 1 - } + + $hasSecureCred = + $_.Value.secureObject.Username -and + $_.Value.secureObject.Password + + $hasTextCred = + $_.Value.Username -and + $_.Value.Password + + if (-not $hasSecureCred -and -not $hasTextCred) { + "$($_.Value)" | Write-DscTrace -Operation Warn + "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | + Write-DscTrace -Operation Error + exit 1 + } + + if ($hasSecureCred) { + "Credential object '$($_.Name)' - SecureObject" | Write-DscTrace -Operation Info + + $username = $_.Value.secureObject.Username + $password = $_.Value.secureObject.Password | + ConvertTo-SecureString -AsPlainText -Force + + $dscResourceInstance.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } + elseif ($hasTextCred) { + "Credential object '$($_.Name)' - Text" | Write-DscTrace -Operation Info + + $username = $_.Value.Username + $password = $_.Value.Password | + ConvertTo-SecureString -AsPlainText -Force + + $dscResourceInstance.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } - $username = $_.Value.secureObject.username - $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force - - $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 7984d06a5b2a71d85f8a171abdac403c9af5fcab Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 12:05:14 +0100 Subject: [PATCH 16/24] Add to psDscAdapter.psm1 support for secure object and test value parameters - credentials --- .../powershell/psDscAdapter/psDscAdapter.psm1 | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index b251121e0..ac4253321 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -463,13 +463,41 @@ function Invoke-DscOperation { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name if ($_.Value -is [System.Management.Automation.PSCustomObject]) { if ($validateProperty -and $validateProperty.PropertyType -in @('PSCredential', 'System.Management.Automation.PSCredential')) { - if (-not $_.Value.Username -or -not $_.Value.Password) { - "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error + $hasSecureCred = + $_.Value.secureObject.Username -and + $_.Value.secureObject.Password + + $hasTextCred = + $_.Value.Username -and + $_.Value.Password + + if (-not $hasSecureCred -and -not $hasTextCred) { + "$($_.Value)" | Write-DscTrace -Operation Warn + "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | + Write-DscTrace -Operation Error exit 1 } - $username = $_.Value.secureObject.username - $password = $_.Value.secureObject.password | ConvertTo-SecureString -AsPlainText -Force - $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) + + if ($hasSecureCred) { + "Credential object '$($_.Name)' - SecureObject" | Write-DscTrace -Operation Info + + $username = $_.Value.secureObject.Username + $password = $_.Value.secureObject.Password | + ConvertTo-SecureString -AsPlainText -Force + + $dscResourceInstance.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } + elseif ($hasTextCred) { + "Credential object '$($_.Name)' - Text" | Write-DscTrace -Operation Info + + $username = $_.Value.Username + $password = $_.Value.Password | + ConvertTo-SecureString -AsPlainText -Force + + $dscResourceInstance.$($_.Name) = + [System.Management.Automation.PSCredential]::new($username, $password) + } } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } From 661259bb14b3c4abe0622b1b7a451c3513cfc5da Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Tue, 13 Jan 2026 23:42:17 +0100 Subject: [PATCH 17/24] Change faild test win_powershellgroup.tests it false --- adapters/powershell/Tests/win_powershellgroup.tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index a0de6e0d9..24a5be59c 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -234,7 +234,7 @@ $out.results[0].result.inDesiredState | Should -Be $inDesiredState It 'Config dont works with credential object with Script base resources - wrong user' { -$inDesiredState = $true +$inDesiredState = $false $yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json resources: @@ -260,7 +260,7 @@ $out.results[0].result.inDesiredState | Should -Be $inDesiredState It 'Config dont works with credential object with Script base resources - No passowrd' { -$inDesiredState = $true +$inDesiredState = $false $yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json resources: From f6b366e426795572d17f685141cbab350fa0b7ec Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 15 Jan 2026 12:48:10 +0100 Subject: [PATCH 18/24] Add to psDscAdapter.psm1 remove output of credntials --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 1 - adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 | 3 --- 2 files changed, 4 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index ac4253321..f47a5126a 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -472,7 +472,6 @@ function Invoke-DscOperation { $_.Value.Password if (-not $hasSecureCred -and -not $hasTextCred) { - "$($_.Value)" | Write-DscTrace -Operation Warn "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index f86210992..1563ec450 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -366,8 +366,6 @@ function Invoke-DscOperation { Write-DscTrace -Operation Debug -Message "Property type: $($validateProperty.PropertyType)" if ($validateProperty -and $validateProperty.PropertyType -eq '[PSCredential]') { - "$($_.Value)" | Write-DscTrace -Operation Warn - $hasSecureCred = $_.Value.secureObject.Username -and $_.Value.secureObject.Password @@ -377,7 +375,6 @@ function Invoke-DscOperation { $_.Value.Password if (-not $hasSecureCred -and -not $hasTextCred) { - "$($_.Value)" | Write-DscTrace -Operation Warn "Credential object '$($_.Name)' requires both 'username' and 'password' properties" | Write-DscTrace -Operation Error exit 1 From 6763c235aca850ad2894a8990f0dcf1bc269a859 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 15 Jan 2026 12:51:54 +0100 Subject: [PATCH 19/24] Remove spaces --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 2 +- adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index f47a5126a..e4070ce53 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -488,7 +488,7 @@ function Invoke-DscOperation { [System.Management.Automation.PSCredential]::new($username, $password) } elseif ($hasTextCred) { - "Credential object '$($_.Name)' - Text" | Write-DscTrace -Operation Info + "Credential object - Text" | Write-DscTrace -Operation Info $username = $_.Value.Username $password = $_.Value.Password | diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index 1563ec450..2cc5490be 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -400,8 +400,6 @@ function Invoke-DscOperation { $property.$($_.Name) = [System.Management.Automation.PSCredential]::new($username, $password) } - - } else { $property.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 779e2590d4d7ae72bd16a7263192814fd8509ab3 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 15 Jan 2026 12:52:29 +0100 Subject: [PATCH 20/24] Add to psDscAdapter.psm1 remove output of credntials --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index e4070ce53..f47a5126a 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -488,7 +488,7 @@ function Invoke-DscOperation { [System.Management.Automation.PSCredential]::new($username, $password) } elseif ($hasTextCred) { - "Credential object - Text" | Write-DscTrace -Operation Info + "Credential object '$($_.Name)' - Text" | Write-DscTrace -Operation Info $username = $_.Value.Username $password = $_.Value.Password | From 2d10057c93d615ec24372c0e753b8bd947487950 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 15 Jan 2026 12:57:37 +0100 Subject: [PATCH 21/24] Add to unit test for PowerShell adapter in verions 5.1 --- .../Tests/win_powershellgroup.tests.ps1 | 271 +++++++++++++++--- 1 file changed, 233 insertions(+), 38 deletions(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index 24a5be59c..09b6f4a73 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -118,6 +118,207 @@ class PSClassResource { New-Item -Path $modulePath -ItemType File -Value $module -Force | Out-Null } + ## Add script base Classs for testing + + $moduleFileScriptRootPSD1 = @" + @{ + # Script module or binary module file associated with this manifest. + RootModule = 'TestScriptBaseDSC.psm1' + + # Version number of this module. + moduleVersion = '0.0.1' + + # ID used to uniquely identify this module + GUID = 'c3775be8-84a1-43f5-a99c-1b9f2d6bc178' + + # Author of this module + Author = '' + + # Company or vendor of this module + CompanyName = '' + + # Copyright statement for this module + Copyright = '' + + # Description of the functionality provided by this module + Description = '' + + # Minimum version of the Windows PowerShell engine required by this module + PowerShellVersion = '5.0' + + # Cmdlets to export from this module + CmdletsToExport = @() + + # Variables to export from this module + VariablesToExport = @() + + # Aliases to export from this module + AliasesToExport = @() + + # Dsc Resources to export from this module + DscResourcesToExport = @('CredentialValidation') + + # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. + PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource') + + # A URL to the license for this module. + LicenseUri = '' + + # A URL to the main website for this project. + ProjectUri = '' + + # A URL to an icon representing this module. + IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = '' + + # Set to a prerelease string value if the release should be a prerelease. + Prerelease = '' + } # End of PSData hashtable + } # End of PrivateData hashtable + } + + "@ + $moduleScriptRootPSM1 = @" + # Root module for CredentialValidationDsc + # No code required + "@ + + $moduleScriptCredentialValidationPSM1 = @' + + + $VerbosePreference = 'SilentlyContinue' + $InformationPreference = 'SilentlyContinue' + $ProgressPreference = 'Continue' + $ErrorActionPreference = 'SilentlyContinue' + + function Get-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + [OutputType([Hashtable])] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + ) + Write-Verbose "[GET] Get Function running" + return @{ + Name = $Name + Credential = $Credential + } + + } + + function Test-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + [OutputType([System.Boolean])] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + + ) + Write-Verbose "[TEST]Checking credentials" + Write-Verbose "[TEST]Checking credentials UserName: $($Credential.UserName)" + Write-Verbose "[TEST]Checking credentials Password: $($Credential.Password)" + + if ($null -eq $Credential) { + throw 'Credential property is required' + $inDesiredState = $false + return $false + } + + if ($Credential.UserName -ne 'MyUser') { + throw 'Invalid user name' + $inDesiredState = $false + } else { + $inDesiredState = $true + } + + + return $inDesiredState + + } + + function Set-TargetResource { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '')] + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string] $Name, + + [Parameter(Mandatory = $true)] + [System.Management.Automation.PSCredential] + $Credential + + ) + + if ($null -eq $Credential) { + throw 'Credential property is required' + $inDesiredState = $false + return $false + } + + if ($Credential.UserName -ne 'MyUser') { + throw 'Invalid user name' + $inDesiredState = $false + } else { + $inDesiredState = $true + } + + + return $inDesiredState + Write-Verbose "[SET]Credential cannot be remediated by DSC." + } + + '@ + $moduleScriptCredentialValidationSchemaMof = @" + [ClassVersion("1.0.0.0"), FriendlyName("CredentialValidation")] + class CredentialValidation : OMI_BaseResource + { + [Key] string Name; + [Required, Description("Test Credentials for Script Base"), EmbeddedInstance("MSFT_Credential")] String Credential; + }; + + "@ + + + + $modulePathRootPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'TestScriptBaseDSC.psm1' + if (-not (Test-Path -Path $modulePathRootPSM1)) { + New-Item -Path $modulePathRootPSM1 -ItemType File -Value $moduleScriptRootPSM1 -Force | Out-Null + } + + + $modulePathRootPSD1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'TestScriptBaseDSC.psd1' + if (-not (Test-Path -Path $modulePathRootPSD1)) { + New-Item -Path $modulePathRootPSD1 -ItemType File -Value $moduleFileScriptRootPSD1 -Force | Out-Null + } + + + $modulePathScriptCredentialValidationPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'DSCResources' 'CredentialValidation' 'CredentialValidation.psm1' + if (-not (Test-Path -Path $modulePathScriptCredentialValidationPSM1)) { + Write-Host "File will be created: $modulePathScriptCredentialValidationPSM1" + New-Item -Path $modulePathScriptCredentialValidationPSM1 -ItemType File -Value $moduleScriptCredentialValidationPSM1 -Force | Out-Null + } + + $modulePathScriptCredentialValidationSchemaMof = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'DSCResources' 'CredentialValidation' 'CredentialValidation.schema.mof' + if (-not (Test-Path -Path $modulePathScriptCredentialValidationSchemaMof)) { + Write-Host "File will be created: $modulePathScriptCredentialValidationSchemaMof" + New-Item -Path $modulePathScriptCredentialValidationSchemaMof -ItemType File -Value $moduleScriptCredentialValidationSchemaMof -Force | Out-Null + } + $env:PSModulePath = $windowsPowerShellPath + [System.IO.Path]::PathSeparator + $env:PSModulePath + [System.IO.Path]::PathSeparator } @@ -165,20 +366,16 @@ resources: } It 'Config works with credential object' { - $yaml = @' - $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json - parameters: - Credential: - type: secureObject - defaultValue: - username: MyUser - password: MyPassword - resources: - - name: Cred test - type: PSClassResource/PSClassResource - properties: - Name: Test - Credential: "[parameters('Credential')]" + $yaml = @' + $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json + resources: + - name: Cred test + type: PSClassResource/PSClassResource + properties: + Name: Test + Credential: + UserName: 'MyUser' + Password: 'MyPassword' '@ $out = dsc -l debug config set -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json @@ -204,12 +401,13 @@ resources: (Get-Content -Path "$testdrive/error.log" -Raw) | Should -BeLike "*ERROR*Credential object 'Credential' requires both 'username' and 'password' properties*" -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) } - ## Scipt base resources test + ## Scipt base resources test running - It 'Config works with credential object with Script base resources' { +It 'Valide credentials with Script base resources' { $inDesiredState = $true -$yaml = @' + +$yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json resources: - name: Working with classic DSC resources @@ -221,21 +419,20 @@ resources: properties: Name: TestScriptResource1 Credential: - username: MyUser - password: Password + username: MyUser + password: Password '@ - -$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json +$out = dsc -l trace config test -i $yaml 2>"$testdrive/error.log" | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) $out.results[0].result.inDesiredState | Should -Be $inDesiredState - } - It 'Config dont works with credential object with Script base resources - wrong user' { +It 'Not Valide credentials with Script base resources - wrong username' { $inDesiredState = $false -$yaml = @' + +$yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json resources: - name: Working with classic DSC resources @@ -247,21 +444,19 @@ resources: properties: Name: TestScriptResource1 Credential: - username: MyUser1 - password: Password + username: User + password: Password '@ - -$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json +$out = dsc -l trace config test -i $yaml 2>"$testdrive/error.log" | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) $out.results[0].result.inDesiredState | Should -Be $inDesiredState - } - It 'Config dont works with credential object with Script base resources - No passowrd' { +It 'Not Valide credentials with Script base resources - wrong properties' { -$inDesiredState = $false -$yaml = @' + +$yaml = @' $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json resources: - name: Working with classic DSC resources @@ -273,14 +468,14 @@ resources: properties: Name: TestScriptResource1 Credential: - username: MyUser - Notpassword: Password + username: MyUser + Notpassword: Password '@ - -$out = dsc -l debug config test -i $yaml 2> "$testdrive/error.log" | ConvertFrom-Json -$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) -$out.results[0].result.inDesiredState | Should -Be $inDesiredState +$out = dsc -l trace config test -i $yaml 2>"$testdrive/error.log" | Out-String +$LASTEXITCODE | Should -Be 2 +$out | Should -BeNullOrEmpty +(Get-Content -Path "$testdrive/error.log" -Raw) | Should -BeLike "*ERROR*Credential object 'Credential' requires both 'username' and 'password' properties*" -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) } From 3c57cf8fdaea9db07de413773090ce0c8e1ce3e4 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Wed, 21 Jan 2026 09:45:13 +0100 Subject: [PATCH 22/24] Fix test file parsing --- .../powershell/Tests/win_powershellgroup.tests.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index 09b6f4a73..a10a414e2 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -121,7 +121,7 @@ class PSClassResource { ## Add script base Classs for testing $moduleFileScriptRootPSD1 = @" - @{ +@{ # Script module or binary module file associated with this manifest. RootModule = 'TestScriptBaseDSC.psm1' @@ -183,12 +183,12 @@ class PSClassResource { } # End of PSData hashtable } # End of PrivateData hashtable } +"@ - "@ $moduleScriptRootPSM1 = @" # Root module for CredentialValidationDsc # No code required - "@ +"@ $moduleScriptCredentialValidationPSM1 = @' @@ -282,7 +282,8 @@ class PSClassResource { Write-Verbose "[SET]Credential cannot be remediated by DSC." } - '@ +'@ + $moduleScriptCredentialValidationSchemaMof = @" [ClassVersion("1.0.0.0"), FriendlyName("CredentialValidation")] class CredentialValidation : OMI_BaseResource @@ -291,7 +292,7 @@ class PSClassResource { [Required, Description("Test Credentials for Script Base"), EmbeddedInstance("MSFT_Credential")] String Credential; }; - "@ +"@ From 354259172f6bd927306b15dd0e9252c42384d540 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Wed, 21 Jan 2026 23:56:25 +0100 Subject: [PATCH 23/24] Fix DSC - test and class path creation for subfolder --- .../Tests/win_powershellgroup.tests.ps1 | 38 ++++--------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index a10a414e2..fb99e8468 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -296,25 +296,25 @@ class PSClassResource { - $modulePathRootPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'TestScriptBaseDSC.psm1' + $modulePathRootPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' '0.0.1' 'TestScriptBaseDSC.psm1' if (-not (Test-Path -Path $modulePathRootPSM1)) { New-Item -Path $modulePathRootPSM1 -ItemType File -Value $moduleScriptRootPSM1 -Force | Out-Null } - $modulePathRootPSD1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'TestScriptBaseDSC.psd1' + $modulePathRootPSD1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' '0.0.1' 'TestScriptBaseDSC.psd1' if (-not (Test-Path -Path $modulePathRootPSD1)) { New-Item -Path $modulePathRootPSD1 -ItemType File -Value $moduleFileScriptRootPSD1 -Force | Out-Null } - $modulePathScriptCredentialValidationPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'DSCResources' 'CredentialValidation' 'CredentialValidation.psm1' + $modulePathScriptCredentialValidationPSM1 = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' '0.0.1' 'DSCResources' 'CredentialValidation' 'CredentialValidation.psm1' if (-not (Test-Path -Path $modulePathScriptCredentialValidationPSM1)) { Write-Host "File will be created: $modulePathScriptCredentialValidationPSM1" New-Item -Path $modulePathScriptCredentialValidationPSM1 -ItemType File -Value $moduleScriptCredentialValidationPSM1 -Force | Out-Null } - $modulePathScriptCredentialValidationSchemaMof = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' 'DSCResources' 'CredentialValidation' 'CredentialValidation.schema.mof' + $modulePathScriptCredentialValidationSchemaMof = Join-Path $windowsPowerShellPath 'TestScriptBaseDSC' '0.0.1' 'DSCResources' 'CredentialValidation' 'CredentialValidation.schema.mof' if (-not (Test-Path -Path $modulePathScriptCredentialValidationSchemaMof)) { Write-Host "File will be created: $modulePathScriptCredentialValidationSchemaMof" New-Item -Path $modulePathScriptCredentialValidationSchemaMof -ItemType File -Value $moduleScriptCredentialValidationSchemaMof -Force | Out-Null @@ -404,7 +404,7 @@ resources: ## Scipt base resources test running -It 'Valide credentials with Script base resources' { +It 'Config works with credential object Script base resources' { $inDesiredState = $true @@ -425,35 +425,11 @@ resources: '@ $out = dsc -l trace config test -i $yaml 2>"$testdrive/error.log" | ConvertFrom-Json -$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) -$out.results[0].result.inDesiredState | Should -Be $inDesiredState +$LASTEXITCODE | Should -Be 0 -Because ($out.results[0].result.inDesiredState | Should -Be $inDesiredState) } -It 'Not Valide credentials with Script base resources - wrong username' { - -$inDesiredState = $false - -$yaml = @' -$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json -resources: -- name: Working with classic DSC resources - type: Microsoft.Windows/WindowsPowerShell - properties: - resources: - - name: Script-resource Info - type: TestScriptBaseDSC/CredentialValidation - properties: - Name: TestScriptResource1 - Credential: - username: User - password: Password -'@ - -$out = dsc -l trace config test -i $yaml 2>"$testdrive/error.log" | ConvertFrom-Json -$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path "$testdrive/error.log" -Raw | Out-String) -$out.results[0].result.inDesiredState | Should -Be $inDesiredState -} +# This works It 'Not Valide credentials with Script base resources - wrong properties' { From 4400c8f13be7897bf625cd6e60d60754bc8d2dd9 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Thu, 22 Jan 2026 09:02:14 +0100 Subject: [PATCH 24/24] Run test --- adapters/powershell/Tests/win_powershellgroup.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index fb99e8468..b934c256b 100644 --- a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 +++ b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 @@ -118,7 +118,7 @@ class PSClassResource { New-Item -Path $modulePath -ItemType File -Value $module -Force | Out-Null } - ## Add script base Classs for testing + ## Add script base Classs for testing and credential object $moduleFileScriptRootPSD1 = @" @{