Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions adapters/powershell/PowerShell_adapter.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"Get",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin"
Expand All @@ -55,6 +58,9 @@
"Set",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand All @@ -74,6 +80,9 @@
"Test",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand All @@ -92,6 +101,9 @@
"Export",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand Down
14 changes: 14 additions & 0 deletions adapters/powershell/Tests/.project.data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"Name": "PSTestClassResource",
Comment thread
SteveL-MSFT marked this conversation as resolved.
Outdated
"Kind": "Resource",
"IsRust": false,
"TestOnly": true,
"CopyFiles": {
"All": [
"PSAdaptedTestClassResource.psd1",
"PSAdaptedTestClassResource.psm1",
"PSAdaptedTestClassResource.dsc.adaptedResource.json",
"WinPSAdaptedTestClassResource.dsc.adaptedResource.json"
]
}
}
34 changes: 34 additions & 0 deletions adapters/powershell/Tests/PSAdapted.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe 'Tests for PS adapted manifests' {
It 'Adapted resource is found' {
$out = dsc resource list 'PSAdapted*' | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.count | Should -Be 2
$out[0].type | Should -BeExactly 'PSAdaptedTestClassResource/PSAdaptedTestClass'
$out[0].kind | Should -BeExactly 'resource'
$out[0].path | Should -Match 'PSAdaptedTestClassResource\.psd1$'
$out[0].requireAdapter | Should -BeExactly 'Microsoft.Adapter/PowerShell'
$out[0].schema | Should -Not -BeNullOrEmpty
$out[1].type | Should -BeExactly 'PSAdaptedTestClassResource/WinPSAdaptedTestClass'
$out[1].kind | Should -BeExactly 'resource'
$out[1].path | Should -Match 'PSAdaptedTestClassResource\.psd1$'
$out[1].requireAdapter | Should -BeExactly 'Microsoft.Adapter/WindowsPowerShell'
$out[1].schema | Should -Not -BeNullOrEmpty
Comment thread
SteveL-MSFT marked this conversation as resolved.
Outdated
}

It 'Get operation works for adapted resource' {
$out = dsc resource get -r 'PSAdaptedTestClassResource/PSAdaptedTestClass' -i '{"name":"hello"}' | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.actualState.Name | Should -BeExactly 'hello' -Because ($out | ConvertTo-Json)
$out.actualState.Value | Should -Be 42
}

It 'WinPS adapted resource is not supported' -Skip:(!$IsWindows) {
$null = dsc resource get -r 'PSAdaptedTestClassResource/WinPSAdaptedTestClass' -i '{"name":"world"}' 2>$TestDrive/error.log
$LASTEXITCODE | Should -Be 2
$errorContent = Get-Content -Path "$TestDrive\error.log" -Raw
$errorContent | Should -Match 'Adapted resource manifests are not supported on Windows PowerShell.'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/adaptedresource/manifest.json",
"type": "PSAdaptedTestClassResource/PSAdaptedTestClass",
"kind": "resource",
"version": "0.1.0",
"capabilities": [
"get"
],
"description": "An adapted resource for testing.",
"author": "DSC Team",
"requireAdapter": "Microsoft.Adapter/PowerShell",
"path": "PSAdaptedTestClassResource.psd1",
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/resources/PSAdaptedTestClass/v0.1.0/schema.json",
"title": "PSAdaptedTestClass",
"description": "An adapted resource for testing.",
"type": "object",
"required": [],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "This is property one of the adapted resource."
},
"value": {
"type": "integer",
"title": "Value",
"description": "The value of the adapted resource instance."
}
}
}
}
}
25 changes: 25 additions & 0 deletions adapters/powershell/Tests/PSAdaptedTestClassResource.psd1
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

@{
RootModule = 'PSAdaptedTestClassResource.psm1'
ModuleVersion = '0.1.0'
GUID = '6592806d-ceef-4949-b576-b5bf32eefcef'
Author = 'Microsoft'
CompanyName = 'Microsoft Corporation'
Copyright = '(c) Microsoft. All rights reserved.'
FunctionsToExport = @()
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
DscResourcesToExport = @('PSAdaptedTestClass')

# 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 = @{
DscCapabilities = @(
'get'
)
}
}
}
52 changes: 52 additions & 0 deletions adapters/powershell/Tests/PSAdaptedTestClassResource.psm1
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[DscResource()]
class PSAdaptedTestClass
{
[DscProperty(Key)]
[string] $Name

[DscProperty()]
[int] $Value

[void] Set()
{
}

[bool] Test()
{
return $true
}

[PSAdaptedTestClass] Get()
{
$this.Value = 42
return $this
}
}

[DscResource()]
class WinPSAdaptedTestClass
{
[DscProperty(Key)]
[string] $Name

[DscProperty()]
[int] $Value

[void] Set()
{
}

[bool] Test()
{
return $true
}

[WinPSAdaptedTestClass] Get()
{
$this.Value = 17
return $this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/adaptedresource/manifest.json",
"type": "PSAdaptedTestClassResource/WinPSAdaptedTestClass",
"kind": "resource",
"version": "0.1.0",
"capabilities": [
"get"
],
"description": "An adapted resource for testing.",
"author": "DSC Team",
"requireAdapter": "Microsoft.Adapter/WindowsPowerShell",
"path": "PSAdaptedTestClassResource.psd1",
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/resources/WinPSAdaptedTestClass/v0.1.0/schema.json",
"title": "WinPSAdaptedTestClass",
"description": "An adapted resource for testing.",
"type": "object",
"required": [],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"title": "Name",
"description": "This is property one of the adapted resource."
},
"value": {
"type": "integer",
"title": "Value",
"description": "The value of the adapted resource instance."
}
}
}
}
}
12 changes: 12 additions & 0 deletions adapters/powershell/WindowsPowerShell_adapter.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@
"Get",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin"
Expand All @@ -55,6 +58,9 @@
"Set",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand All @@ -74,6 +80,9 @@
"Test",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand All @@ -92,6 +101,9 @@
"Export",
{
"resourceTypeArg": "-ResourceType"
},
{
"resourcePathArg": "-ResourcePath"
}
],
"input": "stdin",
Expand Down
16 changes: 14 additions & 2 deletions adapters/powershell/psDscAdapter/powershell.resource.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ param(
[Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $true, HelpMessage = 'Configuration or resource input in JSON format.')]
[string]$jsonInput = '{}',
[Parameter()]
[string]$ResourceType
[string]$ResourceType,
[Parameter()]
[string]$ResourcePath
)

$traceQueue = [System.Collections.Concurrent.ConcurrentQueue[object]]::new()
Expand Down Expand Up @@ -57,6 +59,8 @@ $ps = [PowerShell]::Create().AddScript({
[Parameter()]
[string]$ResourceType,
[Parameter()]
[string]$ResourcePath,
[Parameter()]
[string]$ScriptRoot
)

Expand Down Expand Up @@ -223,6 +227,14 @@ $ps = [PowerShell]::Create().AddScript({
{ @('Get','Set','Test','Export') -contains $_ } {
if ($ResourceType) {
Write-Debug -Debug ("Using resource type override: $ResourceType")
if ($ResourcePath) {
if ($PSVersionTable.PSVersion.Major -le 5) {
throw 'Adapted resource manifests are not supported on Windows PowerShell.'
}
Write-Debug -Debug ("Using resource path override: $ResourcePath")
Import-Module -Name $ResourcePath -Global -Force -ErrorAction Stop
Comment thread
SteveL-MSFT marked this conversation as resolved.
}

$dscResourceCache = Invoke-DscCacheRefresh -Module $ResourceType.Split('/')[0]
if ($null -eq $dscResourceCache) {
throw ("DSC resource '{0}' module not found." -f $ResourceType)
Expand Down Expand Up @@ -339,7 +351,7 @@ $ps = [PowerShell]::Create().AddScript({
[string] $requireAdapter
[string] $description
}
}).AddParameter('Operation', $Operation).AddParameter('jsonInput', $jsonInput).AddParameter('ResourceType', $ResourceType).AddParameter('ScriptRoot', $PSScriptRoot)
}).AddParameter('Operation', $Operation).AddParameter('jsonInput', $jsonInput).AddParameter('ResourceType', $ResourceType).AddParameter('ResourcePath', $ResourcePath).AddParameter('ScriptRoot', $PSScriptRoot)

enum DscTraceLevel {
Error
Expand Down
38 changes: 37 additions & 1 deletion adapters/powershell/psDscAdapter/psDscAdapter.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ function Import-PSDSCModule {
function Get-DSCResourceModules {
$listPSModuleFolders = $env:PSModulePath.Split([IO.Path]::PathSeparator)
$dscModulePsd1List = [System.Collections.Generic.HashSet[System.String]]::new()

# Include any imported modules that contain DSC Resources
foreach ($importedModule in Get-Module) {
if ($importedModule.ExportedDscResources.Count -gt 0) {
# See if the module has a psd1 and then add that to the list, otherwise ignore
foreach ($psd1 in (Get-ChildItem -LiteralPath $importedModule.ModuleBase -Filter "$($importedModule.Name).psd1" -ErrorAction Ignore)) {
Write-Debug -Debug ("Adding imported module with DSC resources: $psd1")
$dscModulePsd1List.Add($psd1) | Out-Null
}
}
}

foreach ($folder in $listPSModuleFolders) {
if (!(Test-Path -LiteralPath $folder -ErrorAction Ignore)) {
continue
Expand Down Expand Up @@ -229,6 +241,26 @@ function Invoke-DscCacheRefresh {
$Module
)

# if the module is already imported and has DSC resources, we can skip refreshing the cache and return the resources directly
$importedModule = Get-Module -Name $Module -ErrorAction Ignore
if ($null -ne $importedModule -and $importedModule.ExportedDscResources.Count -gt 0) {
[dscResourceCacheEntry[]]$dscResourceCacheEntries = [System.Collections.Generic.List[Object]]::new()
$DscResources = [System.Collections.Generic.List[DscResourceInfo]]::new()
[System.Collections.Generic.List[DscResourceInfo]]$r = LoadPowerShellClassResourcesFromModule -moduleInfo $importedModule
if ($r) {
$DscResources.AddRange($r)
}
foreach ($dscResource in $DscResources) {
$moduleName = $dscResource.ModuleName
$dscResourceCacheEntries += [dscResourceCacheEntry]@{
Type = "$moduleName/$($dscResource.Name)"
DscResourceInfo = $dscResource
Comment thread
SteveL-MSFT marked this conversation as resolved.
Outdated
LastWriteTimes = (Get-Date) # use current time since we aren't caching this data
Comment thread
SteveL-MSFT marked this conversation as resolved.
Outdated
}
}
return $dscResourceCacheEntries
}
Comment thread
SteveL-MSFT marked this conversation as resolved.

$refreshCache = $false

$cacheFilePath = if ($IsWindows) {
Expand Down Expand Up @@ -561,7 +593,11 @@ function GetTypeInstanceFromModule {
[Parameter(Mandatory = $true)]
[string] $classname
)
$instance = & (Import-Module $modulename -PassThru) ([scriptblock]::Create("'$classname' -as 'type'"))
$module = Get-Module -Name $modulename -ErrorAction Ignore
if ($null -eq $module) {
$module = Import-Module -Name $modulename -ErrorAction Stop -PassThru
}
$instance = & ($module) ([scriptblock]::Create("'$classname' -as 'type'"))
return $instance
}

Expand Down
Loading