Skip to content

Commit ab18d5c

Browse files
author
Steve Lee (POWERSHELL HE/HIM) (from Dev Box)
committed
add metadata to configuration result output
1 parent 61dfb8b commit ab18d5c

File tree

8 files changed

+134
-14
lines changed

8 files changed

+134
-14
lines changed

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 -BeExactly 'Restricted'
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 -BeExactly 'Restricted'
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_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: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize};
66
use serde_json::{Map, Value};
77
use std::collections::HashMap;
88

9+
use super::config_result::ResultMetadata;
10+
911
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
1012
pub enum ContextKind {
1113
Configuration,
@@ -27,6 +29,13 @@ pub struct MicrosoftDscMetadata {
2729
pub required_security_context: Option<SecurityContextKind>,
2830
}
2931

32+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
33+
#[serde(untagged)]
34+
pub enum MicrosoftMetadata {
35+
DscInput(Metadata),
36+
DscResult(ResultMetadata),
37+
}
38+
3039
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
3140
pub struct Metadata {
3241
#[serde(rename = "Microsoft.DSC", skip_serializing_if = "Option::is_none")]
@@ -45,7 +54,7 @@ pub struct Configuration {
4554
pub variables: Option<HashMap<String, Value>>,
4655
pub resources: Vec<Resource>,
4756
#[serde(skip_serializing_if = "Option::is_none")]
48-
pub metadata: Option<Metadata>,
57+
pub metadata: Option<MicrosoftMetadata>,
4958
}
5059

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

dsc_lib/src/configure/config_result.rs

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
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;
8+
9+
use super::config_doc::SecurityContextKind;
810

911
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
1012
pub enum MessageLevel {
@@ -23,6 +25,41 @@ pub struct ResourceMessage {
2325
pub level: MessageLevel,
2426
}
2527

28+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
29+
pub enum Operation {
30+
Get,
31+
Set,
32+
Test,
33+
Export,
34+
}
35+
36+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
37+
pub enum ExecutionKind {
38+
Actual,
39+
WhatIf,
40+
}
41+
42+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
43+
pub struct ResultMetadata {
44+
#[serde(rename = "Microsoft.DSC")]
45+
pub microsoft: MicrosoftDscResultMetadata,
46+
}
47+
48+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
49+
pub struct MicrosoftDscResultMetadata {
50+
pub version: String,
51+
pub operation: Operation,
52+
#[serde(rename = "executionType")]
53+
pub execution_type: ExecutionKind,
54+
#[serde(rename = "startDatetime")]
55+
pub start_datetime: String,
56+
#[serde(rename = "endDatetime")]
57+
pub end_datetime: String,
58+
pub duration: String,
59+
#[serde(rename = "securityContext")]
60+
pub security_context: SecurityContextKind,
61+
}
62+
2663
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
2764
#[serde(deny_unknown_fields)]
2865
pub struct ResourceGetResult {
@@ -45,6 +82,7 @@ impl From<ResourceTestResult> for ResourceGetResult {
4582
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
4683
#[serde(deny_unknown_fields)]
4784
pub struct ConfigurationGetResult {
85+
pub metadata: Option<ResultMetadata>,
4886
pub results: Vec<ResourceGetResult>,
4987
pub messages: Vec<ResourceMessage>,
5088
#[serde(rename = "hadErrors")]
@@ -55,6 +93,7 @@ impl ConfigurationGetResult {
5593
#[must_use]
5694
pub fn new() -> Self {
5795
Self {
96+
metadata: None,
5897
results: Vec::new(),
5998
messages: Vec::new(),
6099
had_errors: false,
@@ -75,6 +114,7 @@ impl From<ConfigurationTestResult> for ConfigurationGetResult {
75114
results.push(result.into());
76115
}
77116
Self {
117+
metadata: None,
78118
results,
79119
messages: test_result.messages,
80120
had_errors: test_result.had_errors,
@@ -115,6 +155,7 @@ impl Default for GroupResourceSetResult {
115155
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
116156
#[serde(deny_unknown_fields)]
117157
pub struct ConfigurationSetResult {
158+
pub metadata: Option<ResultMetadata>,
118159
pub results: Vec<ResourceSetResult>,
119160
pub messages: Vec<ResourceMessage>,
120161
#[serde(rename = "hadErrors")]
@@ -125,6 +166,7 @@ impl ConfigurationSetResult {
125166
#[must_use]
126167
pub fn new() -> Self {
127168
Self {
169+
metadata: None,
128170
results: Vec::new(),
129171
messages: Vec::new(),
130172
had_errors: false,
@@ -171,6 +213,7 @@ impl Default for GroupResourceTestResult {
171213
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
172214
#[serde(deny_unknown_fields)]
173215
pub struct ConfigurationTestResult {
216+
pub metadata: Option<ResultMetadata>,
174217
pub results: Vec<ResourceTestResult>,
175218
pub messages: Vec<ResourceMessage>,
176219
#[serde(rename = "hadErrors")]
@@ -181,6 +224,7 @@ impl ConfigurationTestResult {
181224
#[must_use]
182225
pub fn new() -> Self {
183226
Self {
227+
metadata: None,
184228
results: Vec::new(),
185229
messages: Vec::new(),
186230
had_errors: false,
@@ -197,7 +241,8 @@ impl Default for ConfigurationTestResult {
197241
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
198242
#[serde(deny_unknown_fields)]
199243
pub struct ConfigurationExportResult {
200-
pub result: Option<config_doc::Configuration>,
244+
pub metadata: Option<ResultMetadata>,
245+
pub result: Option<Configuration>,
201246
pub messages: Vec<ResourceMessage>,
202247
#[serde(rename = "hadErrors")]
203248
pub had_errors: bool,
@@ -207,6 +252,7 @@ impl ConfigurationExportResult {
207252
#[must_use]
208253
pub fn new() -> Self {
209254
Self {
255+
metadata: None,
210256
result: None,
211257
messages: Vec::new(),
212258
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::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
}

dsc_lib/src/configure/mod.rs

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

4+
use crate::configure::config_doc::{MicrosoftMetadata, MicrosoftMetadata::DscInput};
45
use crate::configure::parameters::Input;
56
use crate::dscerror::DscError;
67
use crate::dscresources::dscresource::get_diff;
@@ -11,17 +12,16 @@ use crate::DscResource;
1112
use crate::discovery::Discovery;
1213
use crate::parser::Statement;
1314
use self::context::Context;
14-
use self::config_doc::{Configuration, DataType, Metadata, SecurityContextKind};
15+
use self::config_doc::{Configuration, DataType, SecurityContextKind};
1516
use self::depends_on::get_resource_invocation_order;
16-
use self::config_result::{ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ConfigurationExportResult};
17+
use self::config_result::{ConfigurationExportResult, ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ExecutionKind, MicrosoftDscResultMetadata, Operation, ResultMetadata};
1718
use self::contraints::{check_length, check_number_limits, check_allowed_values};
1819
use indicatif::ProgressStyle;
1920
use security_context_lib::{SecurityContext, get_security_context};
2021
use serde_json::{Map, Value};
2122
use std::{collections::HashMap, mem};
2223
use tracing::{debug, info, trace, warn_span, Span};
2324
use tracing_indicatif::span_ext::IndicatifSpanExt;
24-
2525
pub mod context;
2626
pub mod config_doc;
2727
pub mod config_result;
@@ -165,12 +165,12 @@ fn add_metadata(kind: &Kind, mut properties: Option<Map<String, Value>> ) -> Res
165165
Ok(serde_json::to_string(&properties)?)
166166
}
167167

168-
fn check_security_context(metadata: &Option<Metadata>) -> Result<(), DscError> {
168+
fn check_security_context(metadata: &Option<MicrosoftMetadata>) -> Result<(), DscError> {
169169
if metadata.is_none() {
170170
return Ok(());
171171
}
172172

173-
if let Some(metadata) = &metadata {
173+
if let Some(DscInput(metadata)) = &metadata {
174174
if let Some(microsoft_dsc) = &metadata.microsoft {
175175
if let Some(required_security_context) = &microsoft_dsc.required_security_context {
176176
match required_security_context {
@@ -251,6 +251,9 @@ impl Configurator {
251251
result.results.push(resource_result);
252252
}
253253

254+
result.metadata = Some(
255+
self.get_result_metadata(Operation::Get)
256+
);
254257
std::mem::drop(pb_span_enter);
255258
std::mem::drop(pb_span);
256259
Ok(result)
@@ -343,6 +346,9 @@ impl Configurator {
343346
}
344347
}
345348

349+
result.metadata = Some(
350+
self.get_result_metadata(Operation::Set)
351+
);
346352
mem::drop(pb_span_enter);
347353
mem::drop(pb_span);
348354
Ok(result)
@@ -384,6 +390,9 @@ impl Configurator {
384390
result.results.push(resource_result);
385391
}
386392

393+
result.metadata = Some(
394+
self.get_result_metadata(Operation::Test)
395+
);
387396
std::mem::drop(pb_span_enter);
388397
std::mem::drop(pb_span);
389398
Ok(result)
@@ -423,9 +432,14 @@ impl Configurator {
423432
add_resource_export_results_to_configuration(dsc_resource, Some(dsc_resource), &mut conf, input.as_str())?;
424433
}
425434

435+
conf.metadata = Some(
436+
MicrosoftMetadata::DscResult(
437+
self.get_result_metadata(Operation::Export)
438+
)
439+
);
440+
result.result = Some(conf);
426441
std::mem::drop(pb_span_enter);
427442
std::mem::drop(pb_span);
428-
result.result = Some(conf);
429443
Ok(result)
430444
}
431445

@@ -497,6 +511,21 @@ impl Configurator {
497511
Ok(())
498512
}
499513

514+
fn get_result_metadata(&self, operation: Operation) -> ResultMetadata {
515+
let end_datetime = chrono::Local::now();
516+
ResultMetadata {
517+
microsoft: MicrosoftDscResultMetadata {
518+
version: env!("CARGO_PKG_VERSION").to_string(),
519+
operation,
520+
execution_type: self.context.execution_type.clone(),
521+
start_datetime: self.context.start_datetime.to_rfc3339(),
522+
end_datetime: end_datetime.to_rfc3339(),
523+
duration: end_datetime.signed_duration_since(self.context.start_datetime).to_string(),
524+
security_context: self.context.security_context.clone(),
525+
}
526+
}
527+
}
528+
500529
fn validate_parameter_type(name: &str, value: &Value, parameter_type: &DataType) -> Result<(), DscError> {
501530
match parameter_type {
502531
DataType::String | DataType::SecureString => {

0 commit comments

Comments
 (0)