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