From 61f4b85b2637403aa7ec3717e8556066fb467e68 Mon Sep 17 00:00:00 2001 From: Michal Machniak Date: Mon, 8 Dec 2025 22:45:12 +0100 Subject: [PATCH 01/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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/10] 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