Skip to content

Commit 1af2c8b

Browse files
Steve Lee (POWERSHELL HE/HIM) (from Dev Box)SteveL-MSFT
authored andcommitted
Enable psadapter to work in single mode
1 parent 139a3cf commit 1af2c8b

7 files changed

Lines changed: 92 additions & 44 deletions

File tree

adapters/powershell/PowerShell_adapter.dsc.resource.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"Single"
2424
]
2525
},
26-
"config": "full"
26+
"config": "single"
2727
},
2828
"get": {
2929
"executable": "pwsh",

adapters/powershell/Tests/powershellgroup.config.tests.ps1

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,18 +247,27 @@ Describe 'PowerShell adapter resource tests' {
247247
$out.results.result.actualState.result.properties.HashTableProp.Name | Should -BeExactly 'DSCv3'
248248
}
249249

250-
It 'Config calling PS Resource directly works for <operation>' -TestCases @(
251-
@{ Operation = 'get' }
252-
@{ Operation = 'set' }
253-
@{ Operation = 'test' }
250+
It 'Config calling PS Resource directly works for <operation> with metadata <metadata> and adapter <adapter>' -TestCases @(
251+
@{ Operation = 'get'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.DSC/PowerShell' }
252+
@{ Operation = 'set'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.DSC/PowerShell' }
253+
@{ Operation = 'test'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.DSC/PowerShell' }
254+
@{ Operation = 'get'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.Adapter/PowerShell' }
255+
@{ Operation = 'set'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.Adapter/PowerShell' }
256+
@{ Operation = 'test'; metadata = 'Micrososft.DSC'; adapter = 'Microsoft.Adapter/PowerShell' }
257+
@{ Operation = 'get'; metadata = 'Ignored' }
258+
@{ Operation = 'set'; metadata = 'Ignored' }
259+
@{ Operation = 'test'; metadata = 'Ignored' }
254260
) {
255-
param($Operation)
261+
param($Operation, $metadata, $adapter)
256262

257263
$yaml = @"
258264
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
259265
resources:
260266
- name: Class-resource Info
261267
type: TestClassResource/TestClassResource
268+
metadata:
269+
${metadata}:
270+
requireAdapter: $adapter
262271
properties:
263272
Name: 'TestClassResource1'
264273
HashTableProp:
@@ -281,6 +290,12 @@ Describe 'PowerShell adapter resource tests' {
281290
$out.results[0].result.actualState.InDesiredState | Should -BeFalse -Because $text
282291
}
283292
}
293+
if ($metadata -eq 'Micrososft.DSC') {
294+
"$TestDrive/tracing.txt" | Should -FileContentMatch "Using adapter 'Microsoft.Adapter/PowerShell'"
295+
}
296+
else {
297+
"$TestDrive/tracing.txt" | Should -Not -FileContentMatch "Using adapter 'Microsoft.Adapter/PowerShell'"
298+
}
284299
}
285300

286301
It 'Config works with credential object' {

adapters/powershell/WindowsPowerShell_adapter.dsc.resource.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"Single"
2424
]
2525
},
26-
"config": "full"
26+
"config": "single"
2727
},
2828
"get": {
2929
"executable": "powershell",
@@ -41,7 +41,7 @@
4141
}
4242
],
4343
"input": "stdin"
44-
},
44+
},
4545
"set": {
4646
"executable": "powershell",
4747
"args": [

adapters/powershell/psDscAdapter/powershell.resource.ps1

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ param(
66
[ValidateSet('List', 'Get', 'Set', 'Test', 'Export', 'Validate', 'ClearCache')]
77
[string]$Operation,
88
[Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $true, HelpMessage = 'Configuration or resource input in JSON format.')]
9-
[string]$jsonInput = '@{}',
9+
[string]$jsonInput = '{}',
1010
[Parameter()]
1111
[string]$ResourceType
1212
)
@@ -78,7 +78,7 @@ if ('Validate' -ne $Operation) {
7878
}
7979

8080
if ($jsonInput) {
81-
if ($jsonInput -ne '@{}') {
81+
if ($jsonInput -ne '{}') {
8282
$inputobj_pscustomobj = $jsonInput | ConvertFrom-Json
8383
}
8484
$new_psmodulepath = $inputobj_pscustomobj.psmodulepath
@@ -159,16 +159,23 @@ switch ($Operation) {
159159
}
160160
}
161161
{ @('Get','Set','Test','Export') -contains $_ } {
162+
$desiredState = $psDscAdapter.invoke( { param($jsonInput) Get-DscResourceObject -jsonInput $jsonInput }, $jsonInput )
163+
if ($null -eq $desiredState) {
164+
Write-DscTrace -Operation Error -message 'Failed to create configuration object from provided input JSON.'
165+
exit 1
166+
}
167+
162168
if ($ResourceType) {
163-
$dscResourceCache = Invoke-DscCacheRefresh -ResourceType $ResourceType.Split('/')[0]
169+
Write-DscTrace -Operation Debug -Message "Using resource type override: $ResourceType"
170+
$dscResourceCache = Invoke-DscCacheRefresh -Module $ResourceType.Split('/')[0]
164171
if ($null -eq $dscResourceCache) {
165172
Write-DscTrace -Operation Error -Message ("DSC resource '{0}' module not found." -f $ResourceType)
166173
exit 1
167174
}
168175

176+
$desiredState.Type = $ResourceType
169177
$inDesiredState = $true
170-
$desiredState = $psDscAdapter.invoke( { param($jsonInput) Get-DscResourceObject -jsonInput $jsonInput -single }, $jsonInput )
171-
$actualState = $psDscAdapter.invoke( { param($op, $ds, $dscResourceCache) Invoke-DscOperation -Operation $op -DesiredState $desiredState -dscResourceCache $dscResourceCache }, $Operation, $desiredState, $dscResourceCache)
178+
$actualState = $psDscAdapter.invoke( { param($op, $ds, $dscResourceCache) Invoke-DscOperation -Operation $op -DesiredState $ds -dscResourceCache $dscResourceCache }, $Operation, $desiredState, $dscResourceCache)
172179
if ($null -eq $actualState) {
173180
Write-DscTrace -Operation Error -Message 'Incomplete GET for resource ' + $desiredState.Name
174181
exit 1
@@ -178,21 +185,15 @@ switch ($Operation) {
178185
}
179186

180187
if ($Operation -eq 'Test') {
181-
$result = @{ result = $actualState; _inDesiredState = $inDesiredState } | ConvertTo-Json -Depth 10 -Compress
188+
$result = @{ desiredState = $desiredState.Properties; actualState = $actualState.Properties; _inDesiredState = $inDesiredState } | ConvertTo-Json -Depth 10 -Compress
182189
}
183190
else {
184-
$result = @{ result = $actualState } | ConvertTo-Json -Depth 10 -Compress
191+
$result = $actualState.Properties | ConvertTo-Json -Depth 10 -Compress
185192
}
186193
Write-DscTrace -Operation Debug -Message "jsonOutput=$result"
187194
return $result
188195
}
189196

190-
$desiredState = $psDscAdapter.invoke( { param($jsonInput) Get-DscResourceObject -jsonInput $jsonInput }, $jsonInput )
191-
if ($null -eq $desiredState) {
192-
Write-DscTrace -Operation Error -message 'Failed to create configuration object from provided input JSON.'
193-
exit 1
194-
}
195-
196197
# only need to cache the resources that are used
197198
$dscResourceModules = $desiredState | ForEach-Object { $_.Type.Split('/')[0] }
198199
if ($null -eq $dscResourceModules) {

dsc/tests/dsc_adapter.tests.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,5 +87,25 @@ Describe 'Tests for adapter support' {
8787
}
8888
}
8989
}
90+
91+
92+
It 'Specifying invalid adapter via metadata fails' {
93+
$config_yaml = @"
94+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
95+
resources:
96+
- name: Test
97+
type: Microsoft.DSC.Debug/Echo
98+
properties:
99+
output: '1'
100+
metadata:
101+
Microsoft.DSC:
102+
requireAdapter: InvalidAdapter/Invalid
103+
"@
104+
$out = dsc config get -i $config_yaml 2>$TestDrive/error.log
105+
$LASTEXITCODE | Should -Be 2 -Because (Get-Content $TestDrive/error.log | Out-String)
106+
$errorContent = Get-Content $TestDrive/error.log -Raw
107+
$errorContent | Should -Match "Adapter resource 'InvalidAdapter/Invalid' not found" -Because $errorContent
108+
$out | Should -BeNullOrEmpty -Because $errorContent
109+
}
90110
}
91111
}

lib/dsc-lib/src/configure/config_doc.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,33 @@ pub enum RestartRequired {
6262

6363
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
6464
pub struct MicrosoftDscMetadata {
65-
/// Version of DSC
66-
#[serde(skip_serializing_if = "Option::is_none")]
67-
pub version: Option<String>,
68-
/// The operation being performed
65+
/// The duration of the configuration operation
6966
#[serde(skip_serializing_if = "Option::is_none")]
70-
pub operation: Option<Operation>,
71-
/// The type of execution
72-
#[serde(rename = "executionType", skip_serializing_if = "Option::is_none")]
73-
pub execution_type: Option<ExecutionKind>,
74-
/// The start time of the configuration operation
75-
#[serde(rename = "startDatetime", skip_serializing_if = "Option::is_none")]
76-
pub start_datetime: Option<String>,
67+
pub duration: Option<String>,
7768
/// The end time of the configuration operation
7869
#[serde(rename = "endDatetime", skip_serializing_if = "Option::is_none")]
7970
pub end_datetime: Option<String>,
80-
/// The duration of the configuration operation
71+
/// The type of execution
72+
#[serde(rename = "executionType", skip_serializing_if = "Option::is_none")]
73+
pub execution_type: Option<ExecutionKind>,
74+
/// The operation being performed
8175
#[serde(skip_serializing_if = "Option::is_none")]
82-
pub duration: Option<String>,
83-
/// The security context of the configuration operation, can be specified to be required
84-
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
85-
pub security_context: Option<SecurityContextKind>,
76+
pub operation: Option<Operation>,
77+
/// Specify specific adapter type used for implicit operations
78+
#[serde(rename = "requireAdapter", skip_serializing_if = "Option::is_none")]
79+
pub require_adapter: Option<String>,
8680
/// Indicates what needs to be restarted after the configuration operation
8781
#[serde(rename = "restartRequired", skip_serializing_if = "Option::is_none")]
8882
pub restart_required: Option<Vec<RestartRequired>>,
83+
/// The security context of the configuration operation, can be specified to be required
84+
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
85+
pub security_context: Option<SecurityContextKind>,
86+
/// The start time of the configuration operation
87+
#[serde(rename = "startDatetime", skip_serializing_if = "Option::is_none")]
88+
pub start_datetime: Option<String>,
89+
/// Version of DSC
90+
#[serde(skip_serializing_if = "Option::is_none")]
91+
pub version: Option<String>,
8992
}
9093

9194
impl MicrosoftDscMetadata {

lib/dsc-lib/src/configure/mod.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,15 @@ impl Configurator {
367367
return Err(DscError::ResourceNotFound(resource.resource_type.to_string(), resource.api_version.as_deref().unwrap_or("").to_string()));
368368
};
369369
let properties = self.get_properties(&resource, &dsc_resource.kind)?;
370-
let filter = add_metadata(dsc_resource, properties, resource.metadata.clone())?;
370+
let mut dsc_resource = dsc_resource.clone();
371+
if let Some(resource_metadata) = &resource.metadata {
372+
if let Some(microsoft_metadata) = &resource_metadata.microsoft {
373+
if let Some(require_adapter) = &microsoft_metadata.require_adapter {
374+
dsc_resource.require_adapter = Some(require_adapter.clone());
375+
}
376+
}
377+
}
378+
let filter = add_metadata(&dsc_resource, properties, resource.metadata.clone())?;
371379
let start_datetime = chrono::Local::now();
372380
let mut get_result = match dsc_resource.get(&filter) {
373381
Ok(result) => result,
@@ -957,14 +965,15 @@ impl Configurator {
957965
Metadata {
958966
microsoft: Some(
959967
MicrosoftDscMetadata {
960-
version: Some(version),
961-
operation: Some(operation),
962-
execution_type: Some(self.context.execution_type.clone()),
963-
start_datetime: Some(self.context.start_datetime.to_rfc3339()),
964-
end_datetime: Some(end_datetime.to_rfc3339()),
968+
require_adapter: None,
965969
duration: Some(end_datetime.signed_duration_since(self.context.start_datetime).to_string()),
966-
security_context: Some(self.context.security_context.clone()),
970+
end_datetime: Some(end_datetime.to_rfc3339()),
971+
execution_type: Some(self.context.execution_type.clone()),
972+
operation: Some(operation),
967973
restart_required: self.context.restart_required.clone(),
974+
security_context: Some(self.context.security_context.clone()),
975+
start_datetime: Some(self.context.start_datetime.to_rfc3339()),
976+
version: Some(version),
968977
}
969978
),
970979
other: Map::new(),

0 commit comments

Comments
 (0)