Skip to content

Commit e57fac8

Browse files
authored
Merge pull request #405 from SteveL-MSFT/result-metadata
add metadata to configuration result output
2 parents 61dfb8b + ed435ff commit e57fac8

File tree

11 files changed

+218
-25
lines changed

11 files changed

+218
-25
lines changed

dsc/examples/require_admin.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
55
metadata:
66
Microsoft.DSC:
7-
requiredSecurityContext: Elevated
7+
securityContext: Elevated
88
resources:
99
- name: os
1010
type: Microsoft/OSInfo

dsc/examples/require_nonadmin.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
44
metadata:
55
Microsoft.DSC:
6-
requiredSecurityContext: Restricted
6+
securityContext: Restricted
77
resources:
88
- name: os
99
type: Microsoft/OSInfo

dsc/tests/dsc_config_get.tests.ps1

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,19 @@ Describe 'dsc config get tests' {
4141
properties:
4242
output: hello
4343
"@
44-
$null = $config_yaml | dsc config get --format pretty-json | Out-String
44+
$result = $config_yaml | dsc config get --format pretty-json | ConvertFrom-Json
45+
$result.hadErrors | Should -BeFalse
46+
$result.results.Count | Should -Be 1
47+
$result.results[0].Name | Should -Be 'Echo'
48+
$result.results[0].type | Should -BeExactly 'Test/Echo'
49+
$result.results[0].result.actualState.output | Should -Be 'hello'
50+
$result.metadata.'Microsoft.DSC'.version | Should -BeLike '3.*'
51+
$result.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Get'
52+
$result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual'
53+
$result.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty
54+
$result.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty
55+
$result.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty
56+
$result.metadata.'Microsoft.DSC'.securityContext | Should -Not -BeNullOrEmpty
4557
$LASTEXITCODE | Should -Be 0
4658
}
4759
}

dsc/tests/dsc_config_set.tests.ps1

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Describe 'dsc config set tests' {
1717
"@
1818
$out = $config_yaml | dsc config set | ConvertFrom-Json
1919
$LASTEXITCODE | Should -Be 0
20+
$out.hadErrors | Should -BeFalse
21+
$out.results.Count | Should -Be 2
2022
$out.results[0].type | Should -BeExactly 'Test/Exist'
2123
$out.results[0].result.beforeState._exist | Should -BeFalse
2224
$out.results[0].result.afterState.state | Should -BeExactly 'Absent'
@@ -26,5 +28,13 @@ Describe 'dsc config set tests' {
2628
$out.results[1].result.beforeState._exist | Should -BeFalse
2729
$out.results[1].result.afterState.deleteCalled | Should -BeTrue
2830
$out.results[1].result.afterState._exist | Should -BeFalse
31+
$out.metadata.'Microsoft.DSC'.version | Should -BeLike '3.*'
32+
$out.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Set'
33+
$out.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual'
34+
$out.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty
35+
$out.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty
36+
$out.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty
37+
$out.metadata.'Microsoft.DSC'.securityContext | Should -Not -BeNullOrEmpty
38+
2939
}
3040
}

dsc/tests/dsc_export.tests.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Describe 'resource export tests' {
3838
$config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json'
3939
$config_with_process_list.'resources' | Should -Not -BeNullOrEmpty
4040
$config_with_process_list.resources.count | Should -BeGreaterThan 1
41+
$config_with_process_list.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Export'
4142
}
4243

4344
It 'Configuration Export can be piped to configuration Set' -Skip:(!$IsWindows) {

dsc/tests/dsc_group.tests.ps1

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,68 @@ Describe 'Group resource tests' {
55
It 'Nested groups should work for get' {
66
$out = (dsc config get -p $PSScriptRoot/../examples/groups.dsc.yaml -f yaml | Out-String).Trim()
77
$LASTEXITCODE | Should -Be 0
8-
$out | Should -BeExactly @'
8+
$out | Should -BeLike @'
9+
metadata:
10+
Microsoft.DSC:
11+
version: 3*
12+
operation: Get
13+
executionType: Actual
14+
startDatetime: *
15+
endDatetime: *
16+
duration: PT*S
17+
securityContext: *
918
results:
10-
- name: First Group
19+
- metadata:
20+
Microsoft.DSC:
21+
duration: *
22+
name: First Group
1123
type: Microsoft.DSC/Group
1224
result:
13-
- name: First
25+
- metadata:
26+
Microsoft.DSC:
27+
duration: *
28+
name: First
1429
type: Test/Echo
1530
result:
1631
actualState:
1732
output: First
18-
- name: Nested Group
33+
- metadata:
34+
Microsoft.DSC:
35+
duration: *
36+
name: Nested Group
1937
type: Microsoft.DSC/Group
2038
result:
21-
- name: Nested First
39+
- metadata:
40+
Microsoft.DSC:
41+
duration: *
42+
name: Nested First
2243
type: Test/Echo
2344
result:
2445
actualState:
2546
output: Nested First
26-
- name: Nested Second
47+
- metadata:
48+
Microsoft.DSC:
49+
duration: *
50+
name: Nested Second
2751
type: Test/Echo
2852
result:
2953
actualState:
3054
output: Nested Second
31-
- name: Last Group
55+
- metadata:
56+
Microsoft.DSC:
57+
duration: *
58+
name: Last Group
3259
type: Microsoft.DSC/Group
3360
result:
34-
- name: Last
61+
- metadata:
62+
Microsoft.DSC:
63+
duration: *
64+
name: Last
3565
type: Test/Echo
3666
result:
3767
actualState:
3868
output: Last
39-
messages: []
69+
messages: `[`]
4070
hadErrors: false
4171
'@
4272
}

dsc_lib/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
base64 = "0.22"
8-
chrono = "0.4.26"
8+
chrono = { version = "0.4.26" }
99
derive_builder ="0.20"
1010
indicatif = { version = "0.17" }
1111
jsonschema = "0.17"

dsc_lib/src/configure/config_doc.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,45 @@ pub enum SecurityContextKind {
2020
}
2121

2222
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
23+
pub enum Operation {
24+
Get,
25+
Set,
26+
Test,
27+
Export,
28+
}
29+
30+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
31+
pub enum ExecutionKind {
32+
Actual,
33+
WhatIf,
34+
}
35+
36+
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
2337
pub struct MicrosoftDscMetadata {
38+
/// Version of DSC
39+
#[serde(skip_serializing_if = "Option::is_none")]
40+
pub version: Option<String>,
41+
/// The operation being performed
42+
#[serde(skip_serializing_if = "Option::is_none")]
43+
pub operation: Option<Operation>,
44+
/// The type of execution
45+
#[serde(rename = "executionType", skip_serializing_if = "Option::is_none")]
46+
pub execution_type: Option<ExecutionKind>,
47+
/// The start time of the configuration operation
48+
#[serde(rename = "startDatetime", skip_serializing_if = "Option::is_none")]
49+
pub start_datetime: Option<String>,
50+
/// The end time of the configuration operation
51+
#[serde(rename = "endDatetime", skip_serializing_if = "Option::is_none")]
52+
pub end_datetime: Option<String>,
53+
/// The duration of the configuration operation
54+
#[serde(skip_serializing_if = "Option::is_none")]
55+
pub duration: Option<String>,
56+
/// The security context of the configuration operation, can be specified to be required
57+
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
58+
pub security_context: Option<SecurityContextKind>,
59+
/// Identifies if the operation is part of a configuration
2460
#[serde(skip_serializing_if = "Option::is_none")]
2561
pub context: Option<ContextKind>,
26-
#[serde(rename = "requiredSecurityContext", skip_serializing_if = "Option::is_none")]
27-
pub required_security_context: Option<SecurityContextKind>,
2862
}
2963

3064
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]

dsc_lib/src/configure/config_result.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use schemars::JsonSchema;
55
use serde::{Deserialize, Serialize};
66
use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult};
7-
use crate::configure::config_doc;
7+
use crate::configure::config_doc::{Configuration, Metadata};
88

99
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
1010
pub enum MessageLevel {
@@ -26,6 +26,8 @@ pub struct ResourceMessage {
2626
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
2727
#[serde(deny_unknown_fields)]
2828
pub struct ResourceGetResult {
29+
#[serde(skip_serializing_if = "Option::is_none")]
30+
pub metadata: Option<Metadata>,
2931
pub name: String,
3032
#[serde(rename="type")]
3133
pub resource_type: String,
@@ -35,6 +37,7 @@ pub struct ResourceGetResult {
3537
impl From<ResourceTestResult> for ResourceGetResult {
3638
fn from(test_result: ResourceTestResult) -> Self {
3739
Self {
40+
metadata: None,
3841
name: test_result.name,
3942
resource_type: test_result.resource_type,
4043
result: test_result.result.into(),
@@ -45,6 +48,7 @@ impl From<ResourceTestResult> for ResourceGetResult {
4548
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
4649
#[serde(deny_unknown_fields)]
4750
pub struct ConfigurationGetResult {
51+
pub metadata: Option<Metadata>,
4852
pub results: Vec<ResourceGetResult>,
4953
pub messages: Vec<ResourceMessage>,
5054
#[serde(rename = "hadErrors")]
@@ -55,6 +59,7 @@ impl ConfigurationGetResult {
5559
#[must_use]
5660
pub fn new() -> Self {
5761
Self {
62+
metadata: None,
5863
results: Vec::new(),
5964
messages: Vec::new(),
6065
had_errors: false,
@@ -75,6 +80,7 @@ impl From<ConfigurationTestResult> for ConfigurationGetResult {
7580
results.push(result.into());
7681
}
7782
Self {
83+
metadata: None,
7884
results,
7985
messages: test_result.messages,
8086
had_errors: test_result.had_errors,
@@ -85,6 +91,8 @@ impl From<ConfigurationTestResult> for ConfigurationGetResult {
8591
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
8692
#[serde(deny_unknown_fields)]
8793
pub struct ResourceSetResult {
94+
#[serde(skip_serializing_if = "Option::is_none")]
95+
pub metadata: Option<Metadata>,
8896
pub name: String,
8997
#[serde(rename="type")]
9098
pub resource_type: String,
@@ -115,6 +123,7 @@ impl Default for GroupResourceSetResult {
115123
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
116124
#[serde(deny_unknown_fields)]
117125
pub struct ConfigurationSetResult {
126+
pub metadata: Option<Metadata>,
118127
pub results: Vec<ResourceSetResult>,
119128
pub messages: Vec<ResourceMessage>,
120129
#[serde(rename = "hadErrors")]
@@ -125,6 +134,7 @@ impl ConfigurationSetResult {
125134
#[must_use]
126135
pub fn new() -> Self {
127136
Self {
137+
metadata: None,
128138
results: Vec::new(),
129139
messages: Vec::new(),
130140
had_errors: false,
@@ -141,6 +151,8 @@ impl Default for ConfigurationSetResult {
141151
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
142152
#[serde(deny_unknown_fields)]
143153
pub struct ResourceTestResult {
154+
#[serde(skip_serializing_if = "Option::is_none")]
155+
pub metadata: Option<Metadata>,
144156
pub name: String,
145157
#[serde(rename="type")]
146158
pub resource_type: String,
@@ -171,6 +183,7 @@ impl Default for GroupResourceTestResult {
171183
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
172184
#[serde(deny_unknown_fields)]
173185
pub struct ConfigurationTestResult {
186+
pub metadata: Option<Metadata>,
174187
pub results: Vec<ResourceTestResult>,
175188
pub messages: Vec<ResourceMessage>,
176189
#[serde(rename = "hadErrors")]
@@ -181,6 +194,7 @@ impl ConfigurationTestResult {
181194
#[must_use]
182195
pub fn new() -> Self {
183196
Self {
197+
metadata: None,
184198
results: Vec::new(),
185199
messages: Vec::new(),
186200
had_errors: false,
@@ -197,7 +211,8 @@ impl Default for ConfigurationTestResult {
197211
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
198212
#[serde(deny_unknown_fields)]
199213
pub struct ConfigurationExportResult {
200-
pub result: Option<config_doc::Configuration>,
214+
pub metadata: Option<Metadata>,
215+
pub result: Option<Configuration>,
201216
pub messages: Vec<ResourceMessage>,
202217
#[serde(rename = "hadErrors")]
203218
pub had_errors: bool,
@@ -207,6 +222,7 @@ impl ConfigurationExportResult {
207222
#[must_use]
208223
pub fn new() -> Self {
209224
Self {
225+
metadata: None,
210226
result: None,
211227
messages: Vec::new(),
212228
had_errors: false,

dsc_lib/src/configure/context.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
use chrono::{DateTime, Local};
5+
use crate::configure::config_doc::ExecutionKind;
6+
use security_context_lib::{get_security_context, SecurityContext};
47
use serde_json::Value;
58
use std::collections::HashMap;
69

7-
use super::config_doc::DataType;
10+
use super::config_doc::{DataType, SecurityContextKind};
811

912
pub struct Context {
13+
pub execution_type: ExecutionKind,
14+
pub outputs: HashMap<String, Value>, // this is used by the `reference()` function to retrieve output
1015
pub parameters: HashMap<String, (Value, DataType)>,
16+
pub security_context: SecurityContextKind,
1117
_variables: HashMap<String, Value>,
12-
pub outputs: HashMap<String, Value>, // this is used by the `reference()` function to retrieve output
18+
19+
pub start_datetime: DateTime<Local>,
1320
}
1421

1522
impl Context {
1623
#[must_use]
1724
pub fn new() -> Self {
1825
Self {
26+
execution_type: ExecutionKind::Actual,
27+
outputs: HashMap::new(),
1928
parameters: HashMap::new(),
29+
security_context: match get_security_context() {
30+
SecurityContext::Admin => SecurityContextKind::Elevated,
31+
SecurityContext::User => SecurityContextKind::Restricted,
32+
},
2033
_variables: HashMap::new(),
21-
outputs: HashMap::new(),
34+
start_datetime: chrono::Local::now(),
2235
}
2336
}
2437
}

0 commit comments

Comments
 (0)