Skip to content
Open
48 changes: 48 additions & 0 deletions adapters/powershell/Tests/win_powershell_script_resources.dsc.yaml
Original file line number Diff line number Diff line change
@@ -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')]"
36 changes: 35 additions & 1 deletion adapters/powershell/psDscAdapter/win_psDscAdapter.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -368,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 }
}
Expand Down