From 6ca0a7eee5738c9b5b7c7651de36e5ef54fa2984 Mon Sep 17 00:00:00 2001 From: "G.Reijn" Date: Fri, 24 Oct 2025 10:41:16 +0200 Subject: [PATCH 1/5] Add support for `[SecureString]` in PowerShell adapter --- .../TestClassResource/0.0.1/TestClassResource.psm1 | 3 +++ .../Tests/powershellgroup.resource.tests.ps1 | 7 +++++++ adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 13 +++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 b/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 index d36757d84..4e0808c8e 100644 --- a/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 +++ b/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 @@ -40,6 +40,9 @@ class TestClassResource : BaseTestClass [DscProperty()] [Ensure] $Ensure + [DscProperty()] + [SecureString] $SecureStringProp + [string] $NonDscProperty # This property shouldn't be in results data hidden diff --git a/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 b/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 index 4fd12b4e4..9efd22a72 100644 --- a/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 +++ b/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 @@ -376,4 +376,11 @@ Describe 'PowerShell adapter resource tests' { $LASTEXITCODE | Should -Be 7 Get-Content -Path $TestDrive/error.log | Should -Match 'Resource not found: TestClassResource/TestClassResource 0.0.2' } + + It 'Can process SecureString property' { + $r = '{"Name":"TestClassResource1","SecureStringProp":"MySecretValue"}' | dsc resource get -r 'TestClassResource/TestClassResource' -f - + $LASTEXITCODE | Should -Be 0 + $res = $r | ConvertFrom-Json + $res.actualState.SecureStringProp | Should -Not -BeNullOrEmpty + } } diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 61051beb9..4643fb6d2 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -424,21 +424,30 @@ function Invoke-DscOperation { # set each property of $dscResourceInstance to the value of the property in the $desiredState INPUT object $DesiredState.properties.psobject.properties | ForEach-Object -Process { # handle input objects by converting them to a hash table + $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name if ($_.Value -is [System.Management.Automation.PSCustomObject]) { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name - if ($validateProperty -and $validateProperty.PropertyType -eq 'PSCredential') { + if ($validateProperty -and $validateProperty.PropertyType -like "*PSCredential") { if (-not $_.Value.Username -or -not $_.Value.Password) { "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)) } + elseif ($validateProperty -and $validateProperty.PropertyType -like '*SecureString') { + + $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force + } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } } else { - $dscResourceInstance.$($_.Name) = $_.Value + if ($validateProperty -and $validateProperty.PropertyType -like '*SecureString' -and -not [string]::IsNullOrEmpty($_.Value)) { + $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force + } else { + $dscResourceInstance.$($_.Name) = $_.Value + } } } } From a13082c5090e50fe05290c447a54b29631fa9f13 Mon Sep 17 00:00:00 2001 From: "G.Reijn" Date: Fri, 24 Oct 2025 10:42:22 +0200 Subject: [PATCH 2/5] Remove elseif --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 4643fb6d2..f5e8f13dd 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -434,10 +434,6 @@ function Invoke-DscOperation { } $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($_.Value.Username, (ConvertTo-SecureString -AsPlainText $_.Value.Password -Force)) } - elseif ($validateProperty -and $validateProperty.PropertyType -like '*SecureString') { - - $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force - } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 8895b3c73d983099127501f208f8da441874f8fe Mon Sep 17 00:00:00 2001 From: "G.Reijn" Date: Fri, 24 Oct 2025 10:41:16 +0200 Subject: [PATCH 3/5] Add support for `[SecureString]` in PowerShell adapter --- .../TestClassResource/0.0.1/TestClassResource.psm1 | 3 +++ .../Tests/powershellgroup.resource.tests.ps1 | 7 +++++++ adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 13 +++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 b/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 index d36757d84..4e0808c8e 100644 --- a/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 +++ b/adapters/powershell/Tests/TestClassResource/0.0.1/TestClassResource.psm1 @@ -40,6 +40,9 @@ class TestClassResource : BaseTestClass [DscProperty()] [Ensure] $Ensure + [DscProperty()] + [SecureString] $SecureStringProp + [string] $NonDscProperty # This property shouldn't be in results data hidden diff --git a/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 b/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 index 4fd12b4e4..9efd22a72 100644 --- a/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 +++ b/adapters/powershell/Tests/powershellgroup.resource.tests.ps1 @@ -376,4 +376,11 @@ Describe 'PowerShell adapter resource tests' { $LASTEXITCODE | Should -Be 7 Get-Content -Path $TestDrive/error.log | Should -Match 'Resource not found: TestClassResource/TestClassResource 0.0.2' } + + It 'Can process SecureString property' { + $r = '{"Name":"TestClassResource1","SecureStringProp":"MySecretValue"}' | dsc resource get -r 'TestClassResource/TestClassResource' -f - + $LASTEXITCODE | Should -Be 0 + $res = $r | ConvertFrom-Json + $res.actualState.SecureStringProp | Should -Not -BeNullOrEmpty + } } diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 61051beb9..4643fb6d2 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -424,21 +424,30 @@ function Invoke-DscOperation { # set each property of $dscResourceInstance to the value of the property in the $desiredState INPUT object $DesiredState.properties.psobject.properties | ForEach-Object -Process { # handle input objects by converting them to a hash table + $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name if ($_.Value -is [System.Management.Automation.PSCustomObject]) { $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name - if ($validateProperty -and $validateProperty.PropertyType -eq 'PSCredential') { + if ($validateProperty -and $validateProperty.PropertyType -like "*PSCredential") { if (-not $_.Value.Username -or -not $_.Value.Password) { "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)) } + elseif ($validateProperty -and $validateProperty.PropertyType -like '*SecureString') { + + $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force + } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } } else { - $dscResourceInstance.$($_.Name) = $_.Value + if ($validateProperty -and $validateProperty.PropertyType -like '*SecureString' -and -not [string]::IsNullOrEmpty($_.Value)) { + $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force + } else { + $dscResourceInstance.$($_.Name) = $_.Value + } } } } From cc35c355468c1cabf2546472c280870b7cbdd9c1 Mon Sep 17 00:00:00 2001 From: "G.Reijn" Date: Fri, 24 Oct 2025 10:42:22 +0200 Subject: [PATCH 4/5] Remove elseif --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 4643fb6d2..f5e8f13dd 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -434,10 +434,6 @@ function Invoke-DscOperation { } $dscResourceInstance.$($_.Name) = [System.Management.Automation.PSCredential]::new($_.Value.Username, (ConvertTo-SecureString -AsPlainText $_.Value.Password -Force)) } - elseif ($validateProperty -and $validateProperty.PropertyType -like '*SecureString') { - - $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force - } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } } From 699d809b11a2b0fe09c2d88a99a33f9671926817 Mon Sep 17 00:00:00 2001 From: "G.Reijn" Date: Sat, 25 Oct 2025 02:26:57 +0200 Subject: [PATCH 5/5] Remove redundant check and validation --- adapters/powershell/psDscAdapter/psDscAdapter.psm1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index f5e8f13dd..5f1542c84 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -426,8 +426,7 @@ function Invoke-DscOperation { # handle input objects by converting them to a hash table $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name if ($_.Value -is [System.Management.Automation.PSCustomObject]) { - $validateProperty = $cachedDscResourceInfo.Properties | Where-Object -Property Name -EQ $_.Name - if ($validateProperty -and $validateProperty.PropertyType -like "*PSCredential") { + 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 exit 1 @@ -439,7 +438,7 @@ function Invoke-DscOperation { } } else { - if ($validateProperty -and $validateProperty.PropertyType -like '*SecureString' -and -not [string]::IsNullOrEmpty($_.Value)) { + if ($validateProperty -and $validateProperty.PropertyType -in @('SecureString', 'System.Security.SecureString') -and -not [string]::IsNullOrEmpty($_.Value)) { $dscResourceInstance.$($_.Name) = ConvertTo-SecureString -AsPlainText $_.Value -Force } else { $dscResourceInstance.$($_.Name) = $_.Value