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..610433094 --- /dev/null +++ b/adapters/powershell/Tests/TestScriptBaseDSC/DSCResources/CredentialValidation/CredentialValidation.psm1 @@ -0,0 +1,88 @@ +$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." +} \ 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 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/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' { 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')]" diff --git a/adapters/powershell/Tests/win_powershellgroup.tests.ps1 b/adapters/powershell/Tests/win_powershellgroup.tests.ps1 index f5c505478..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,16 +366,16 @@ resources: } It 'Config works with credential object' { - $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' + $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 @@ -200,6 +401,86 @@ 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 running + +It 'Valide credentials 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 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 '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 +} + +It 'Not Valide credentials with Script base resources - wrong properties' { + + +$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 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) + +} + + + 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) diff --git a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 index 1c22fb0b5..f47a5126a 100644 --- a/adapters/powershell/psDscAdapter/psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/psDscAdapter.psm1 @@ -463,11 +463,40 @@ 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) { + "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)) + + 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 } diff --git a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 index c206d89d9..2cc5490be 100644 --- a/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 +++ b/adapters/powershell/psDscAdapter/win_psDscAdapter.psm1 @@ -298,6 +298,7 @@ function Get-DscResourceObject { return $desiredState } + # Get the actual state using DSC Get method from any type of DSC resource function Invoke-DscOperation { param( @@ -364,11 +365,41 @@ 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) { - "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) { + "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)) + + 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 } } @@ -418,11 +449,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.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)) + + $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) + } + } else { $dscResourceInstance.$($_.Name) = $_.Value.psobject.properties | ForEach-Object -Begin { $propertyHash = @{} } -Process { $propertyHash[$_.Name] = $_.Value } -End { $propertyHash } }