From ab18d5c14362a8d667ffa3ced5e46dc27fd8dfbd Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 09:44:21 -0700 Subject: [PATCH 1/8] add metadata to configuration result output --- dsc/tests/dsc_config_get.tests.ps1 | 14 +++++++- dsc/tests/dsc_config_set.tests.ps1 | 10 ++++++ dsc/tests/dsc_export.tests.ps1 | 1 + dsc_lib/Cargo.toml | 2 +- dsc_lib/src/configure/config_doc.rs | 11 +++++- dsc_lib/src/configure/config_result.rs | 50 ++++++++++++++++++++++++-- dsc_lib/src/configure/context.rs | 19 ++++++++-- dsc_lib/src/configure/mod.rs | 41 +++++++++++++++++---- 8 files changed, 134 insertions(+), 14 deletions(-) diff --git a/dsc/tests/dsc_config_get.tests.ps1 b/dsc/tests/dsc_config_get.tests.ps1 index a83aedc0..4136ad7a 100644 --- a/dsc/tests/dsc_config_get.tests.ps1 +++ b/dsc/tests/dsc_config_get.tests.ps1 @@ -41,7 +41,19 @@ Describe 'dsc config get tests' { properties: output: hello "@ - $null = $config_yaml | dsc config get --format pretty-json | Out-String + $result = $config_yaml | dsc config get --format pretty-json | ConvertFrom-Json + $result.hadErrors | Should -BeFalse + $result.results.Count | Should -Be 1 + $result.results[0].Name | Should -Be 'Echo' + $result.results[0].type | Should -BeExactly 'Test/Echo' + $result.results[0].result.actualState.output | Should -Be 'hello' + $result.metadata.'Microsoft.DSC'.version | Should -BeLike '3.*' + $result.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Get' + $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual' + $result.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty + $result.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty + $result.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty + $result.metadata.'Microsoft.DSC'.securityContext | Should -BeExactly 'Restricted' $LASTEXITCODE | Should -Be 0 } } diff --git a/dsc/tests/dsc_config_set.tests.ps1 b/dsc/tests/dsc_config_set.tests.ps1 index e322e052..461d73d7 100644 --- a/dsc/tests/dsc_config_set.tests.ps1 +++ b/dsc/tests/dsc_config_set.tests.ps1 @@ -17,6 +17,8 @@ Describe 'dsc config set tests' { "@ $out = $config_yaml | dsc config set | ConvertFrom-Json $LASTEXITCODE | Should -Be 0 + $out.hadErrors | Should -BeFalse + $out.results.Count | Should -Be 2 $out.results[0].type | Should -BeExactly 'Test/Exist' $out.results[0].result.beforeState._exist | Should -BeFalse $out.results[0].result.afterState.state | Should -BeExactly 'Absent' @@ -26,5 +28,13 @@ Describe 'dsc config set tests' { $out.results[1].result.beforeState._exist | Should -BeFalse $out.results[1].result.afterState.deleteCalled | Should -BeTrue $out.results[1].result.afterState._exist | Should -BeFalse + $out.metadata.'Microsoft.DSC'.version | Should -BeLike '3.*' + $out.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Set' + $out.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual' + $out.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty + $out.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty + $out.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty + $out.metadata.'Microsoft.DSC'.securityContext | Should -BeExactly 'Restricted' + } } diff --git a/dsc/tests/dsc_export.tests.ps1 b/dsc/tests/dsc_export.tests.ps1 index 62501207..b837b11b 100644 --- a/dsc/tests/dsc_export.tests.ps1 +++ b/dsc/tests/dsc_export.tests.ps1 @@ -38,6 +38,7 @@ Describe 'resource export tests' { $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json' $config_with_process_list.'resources' | Should -Not -BeNullOrEmpty $config_with_process_list.resources.count | Should -BeGreaterThan 1 + $config_with_process_list.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'Export' } It 'Configuration Export can be piped to configuration Set' -Skip:(!$IsWindows) { diff --git a/dsc_lib/Cargo.toml b/dsc_lib/Cargo.toml index ed37b49a..cd2c4a1e 100644 --- a/dsc_lib/Cargo.toml +++ b/dsc_lib/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] base64 = "0.22" -chrono = "0.4.26" +chrono = { version = "0.4.26" } derive_builder ="0.20" indicatif = { version = "0.17" } jsonschema = "0.17" diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index a3d96f2e..e57c40a7 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use std::collections::HashMap; +use super::config_result::ResultMetadata; + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum ContextKind { Configuration, @@ -27,6 +29,13 @@ pub struct MicrosoftDscMetadata { pub required_security_context: Option, } +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(untagged)] +pub enum MicrosoftMetadata { + DscInput(Metadata), + DscResult(ResultMetadata), +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub struct Metadata { #[serde(rename = "Microsoft.DSC", skip_serializing_if = "Option::is_none")] @@ -45,7 +54,7 @@ pub struct Configuration { pub variables: Option>, pub resources: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, + pub metadata: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/dsc_lib/src/configure/config_result.rs b/dsc_lib/src/configure/config_result.rs index 493a102c..c6e9b641 100644 --- a/dsc_lib/src/configure/config_result.rs +++ b/dsc_lib/src/configure/config_result.rs @@ -4,7 +4,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult}; -use crate::configure::config_doc; +use crate::configure::config_doc::Configuration; + +use super::config_doc::SecurityContextKind; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum MessageLevel { @@ -23,6 +25,41 @@ pub struct ResourceMessage { pub level: MessageLevel, } +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub enum Operation { + Get, + Set, + Test, + Export, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub enum ExecutionKind { + Actual, + WhatIf, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub struct ResultMetadata { + #[serde(rename = "Microsoft.DSC")] + pub microsoft: MicrosoftDscResultMetadata, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub struct MicrosoftDscResultMetadata { + pub version: String, + pub operation: Operation, + #[serde(rename = "executionType")] + pub execution_type: ExecutionKind, + #[serde(rename = "startDatetime")] + pub start_datetime: String, + #[serde(rename = "endDatetime")] + pub end_datetime: String, + pub duration: String, + #[serde(rename = "securityContext")] + pub security_context: SecurityContextKind, +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceGetResult { @@ -45,6 +82,7 @@ impl From for ResourceGetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationGetResult { + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -55,6 +93,7 @@ impl ConfigurationGetResult { #[must_use] pub fn new() -> Self { Self { + metadata: None, results: Vec::new(), messages: Vec::new(), had_errors: false, @@ -75,6 +114,7 @@ impl From for ConfigurationGetResult { results.push(result.into()); } Self { + metadata: None, results, messages: test_result.messages, had_errors: test_result.had_errors, @@ -115,6 +155,7 @@ impl Default for GroupResourceSetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationSetResult { + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -125,6 +166,7 @@ impl ConfigurationSetResult { #[must_use] pub fn new() -> Self { Self { + metadata: None, results: Vec::new(), messages: Vec::new(), had_errors: false, @@ -171,6 +213,7 @@ impl Default for GroupResourceTestResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationTestResult { + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -181,6 +224,7 @@ impl ConfigurationTestResult { #[must_use] pub fn new() -> Self { Self { + metadata: None, results: Vec::new(), messages: Vec::new(), had_errors: false, @@ -197,7 +241,8 @@ impl Default for ConfigurationTestResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationExportResult { - pub result: Option, + pub metadata: Option, + pub result: Option, pub messages: Vec, #[serde(rename = "hadErrors")] pub had_errors: bool, @@ -207,6 +252,7 @@ impl ConfigurationExportResult { #[must_use] pub fn new() -> Self { Self { + metadata: None, result: None, messages: Vec::new(), had_errors: false, diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index 9198c99a..c4793af2 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -1,24 +1,37 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use chrono::{DateTime, Local}; +use crate::configure::ExecutionKind; +use security_context_lib::{get_security_context, SecurityContext}; use serde_json::Value; use std::collections::HashMap; -use super::config_doc::DataType; +use super::config_doc::{DataType, SecurityContextKind}; pub struct Context { + pub execution_type: ExecutionKind, + pub outputs: HashMap, // this is used by the `reference()` function to retrieve output pub parameters: HashMap, + pub security_context: SecurityContextKind, _variables: HashMap, - pub outputs: HashMap, // this is used by the `reference()` function to retrieve output + + pub start_datetime: DateTime, } impl Context { #[must_use] pub fn new() -> Self { Self { + execution_type: ExecutionKind::Actual, + outputs: HashMap::new(), parameters: HashMap::new(), + security_context: match get_security_context() { + SecurityContext::Admin => SecurityContextKind::Elevated, + SecurityContext::User => SecurityContextKind::Restricted, + }, _variables: HashMap::new(), - outputs: HashMap::new(), + start_datetime: chrono::Local::now(), } } } diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index c8b19cea..422dbee1 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +use crate::configure::config_doc::{MicrosoftMetadata, MicrosoftMetadata::DscInput}; use crate::configure::parameters::Input; use crate::dscerror::DscError; use crate::dscresources::dscresource::get_diff; @@ -11,9 +12,9 @@ use crate::DscResource; use crate::discovery::Discovery; use crate::parser::Statement; use self::context::Context; -use self::config_doc::{Configuration, DataType, Metadata, SecurityContextKind}; +use self::config_doc::{Configuration, DataType, SecurityContextKind}; use self::depends_on::get_resource_invocation_order; -use self::config_result::{ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ConfigurationExportResult}; +use self::config_result::{ConfigurationExportResult, ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ExecutionKind, MicrosoftDscResultMetadata, Operation, ResultMetadata}; use self::contraints::{check_length, check_number_limits, check_allowed_values}; use indicatif::ProgressStyle; use security_context_lib::{SecurityContext, get_security_context}; @@ -21,7 +22,6 @@ use serde_json::{Map, Value}; use std::{collections::HashMap, mem}; use tracing::{debug, info, trace, warn_span, Span}; use tracing_indicatif::span_ext::IndicatifSpanExt; - pub mod context; pub mod config_doc; pub mod config_result; @@ -165,12 +165,12 @@ fn add_metadata(kind: &Kind, mut properties: Option> ) -> Res Ok(serde_json::to_string(&properties)?) } -fn check_security_context(metadata: &Option) -> Result<(), DscError> { +fn check_security_context(metadata: &Option) -> Result<(), DscError> { if metadata.is_none() { return Ok(()); } - if let Some(metadata) = &metadata { + if let Some(DscInput(metadata)) = &metadata { if let Some(microsoft_dsc) = &metadata.microsoft { if let Some(required_security_context) = µsoft_dsc.required_security_context { match required_security_context { @@ -251,6 +251,9 @@ impl Configurator { result.results.push(resource_result); } + result.metadata = Some( + self.get_result_metadata(Operation::Get) + ); std::mem::drop(pb_span_enter); std::mem::drop(pb_span); Ok(result) @@ -343,6 +346,9 @@ impl Configurator { } } + result.metadata = Some( + self.get_result_metadata(Operation::Set) + ); mem::drop(pb_span_enter); mem::drop(pb_span); Ok(result) @@ -384,6 +390,9 @@ impl Configurator { result.results.push(resource_result); } + result.metadata = Some( + self.get_result_metadata(Operation::Test) + ); std::mem::drop(pb_span_enter); std::mem::drop(pb_span); Ok(result) @@ -423,9 +432,14 @@ impl Configurator { add_resource_export_results_to_configuration(dsc_resource, Some(dsc_resource), &mut conf, input.as_str())?; } + conf.metadata = Some( + MicrosoftMetadata::DscResult( + self.get_result_metadata(Operation::Export) + ) + ); + result.result = Some(conf); std::mem::drop(pb_span_enter); std::mem::drop(pb_span); - result.result = Some(conf); Ok(result) } @@ -497,6 +511,21 @@ impl Configurator { Ok(()) } + fn get_result_metadata(&self, operation: Operation) -> ResultMetadata { + let end_datetime = chrono::Local::now(); + ResultMetadata { + microsoft: MicrosoftDscResultMetadata { + version: env!("CARGO_PKG_VERSION").to_string(), + operation, + execution_type: self.context.execution_type.clone(), + start_datetime: self.context.start_datetime.to_rfc3339(), + end_datetime: end_datetime.to_rfc3339(), + duration: end_datetime.signed_duration_since(self.context.start_datetime).to_string(), + security_context: self.context.security_context.clone(), + } + } + } + fn validate_parameter_type(name: &str, value: &Value, parameter_type: &DataType) -> Result<(), DscError> { match parameter_type { DataType::String | DataType::SecureString => { From e98fb1604c1eb064765351c7088d0cd7989972a3 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 09:58:14 -0700 Subject: [PATCH 2/8] fix test --- dsc/tests/dsc_group.tests.ps1 | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dsc/tests/dsc_group.tests.ps1 b/dsc/tests/dsc_group.tests.ps1 index a64b0c11..5a8d1cc6 100644 --- a/dsc/tests/dsc_group.tests.ps1 +++ b/dsc/tests/dsc_group.tests.ps1 @@ -5,7 +5,16 @@ Describe 'Group resource tests' { It 'Nested groups should work for get' { $out = (dsc config get -p $PSScriptRoot/../examples/groups.dsc.yaml -f yaml | Out-String).Trim() $LASTEXITCODE | Should -Be 0 - $out | Should -BeExactly @' + $out | Should -BeLike @' +metadata: + Microsoft.DSC: + version: 3* + operation: Get + executionType: Actual + startDatetime: * + endDatetime: * + duration: PT*S + securityContext: Restricted results: - name: First Group type: Microsoft.DSC/Group @@ -36,7 +45,7 @@ results: result: actualState: output: Last -messages: [] +messages: `[`] hadErrors: false '@ } From 32de3261ef05b0138271214173999ad2dae30bd8 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 10:21:10 -0700 Subject: [PATCH 3/8] fix tests which are running elevated in CI --- dsc/tests/dsc_config_get.tests.ps1 | 2 +- dsc/tests/dsc_config_set.tests.ps1 | 2 +- dsc/tests/dsc_group.tests.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dsc/tests/dsc_config_get.tests.ps1 b/dsc/tests/dsc_config_get.tests.ps1 index 4136ad7a..381e4cf7 100644 --- a/dsc/tests/dsc_config_get.tests.ps1 +++ b/dsc/tests/dsc_config_get.tests.ps1 @@ -53,7 +53,7 @@ Describe 'dsc config get tests' { $result.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty $result.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty $result.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty - $result.metadata.'Microsoft.DSC'.securityContext | Should -BeExactly 'Restricted' + $result.metadata.'Microsoft.DSC'.securityContext | Should -Not -BeNullOrEmpty $LASTEXITCODE | Should -Be 0 } } diff --git a/dsc/tests/dsc_config_set.tests.ps1 b/dsc/tests/dsc_config_set.tests.ps1 index 461d73d7..b740a09f 100644 --- a/dsc/tests/dsc_config_set.tests.ps1 +++ b/dsc/tests/dsc_config_set.tests.ps1 @@ -34,7 +34,7 @@ Describe 'dsc config set tests' { $out.metadata.'Microsoft.DSC'.startDatetime | Should -Not -BeNullOrEmpty $out.metadata.'Microsoft.DSC'.endDatetime | Should -Not -BeNullOrEmpty $out.metadata.'Microsoft.DSC'.duration | Should -Not -BeNullOrEmpty - $out.metadata.'Microsoft.DSC'.securityContext | Should -BeExactly 'Restricted' + $out.metadata.'Microsoft.DSC'.securityContext | Should -Not -BeNullOrEmpty } } diff --git a/dsc/tests/dsc_group.tests.ps1 b/dsc/tests/dsc_group.tests.ps1 index 5a8d1cc6..bbe038fd 100644 --- a/dsc/tests/dsc_group.tests.ps1 +++ b/dsc/tests/dsc_group.tests.ps1 @@ -14,7 +14,7 @@ metadata: startDatetime: * endDatetime: * duration: PT*S - securityContext: Restricted + securityContext: * results: - name: First Group type: Microsoft.DSC/Group From f7108d0762cba419aba03349a2a779752bdb0097 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 12:49:16 -0700 Subject: [PATCH 4/8] combine metadata to single struct --- dsc_lib/src/configure/config_doc.rs | 49 +++++++++++++++++++------- dsc_lib/src/configure/config_result.rs | 47 +++--------------------- dsc_lib/src/configure/context.rs | 2 +- dsc_lib/src/configure/mod.rs | 43 +++++++++++----------- 4 files changed, 64 insertions(+), 77 deletions(-) diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index e57c40a7..e0a0389d 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -6,8 +6,6 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use std::collections::HashMap; -use super::config_result::ResultMetadata; - #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum ContextKind { Configuration, @@ -22,18 +20,45 @@ pub enum SecurityContextKind { } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub struct MicrosoftDscMetadata { - #[serde(skip_serializing_if = "Option::is_none")] - pub context: Option, - #[serde(rename = "requiredSecurityContext", skip_serializing_if = "Option::is_none")] - pub required_security_context: Option, +pub enum Operation { + Get, + Set, + Test, + Export, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub enum ExecutionKind { + Actual, + WhatIf, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -#[serde(untagged)] -pub enum MicrosoftMetadata { - DscInput(Metadata), - DscResult(ResultMetadata), +pub struct MicrosoftDscMetadata { + /// Version of DSC + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, + /// The operation being performed + #[serde(skip_serializing_if = "Option::is_none")] + pub operation: Option, + /// The type of execution + #[serde(rename = "executionType", skip_serializing_if = "Option::is_none")] + pub execution_type: Option, + /// The start time of the configuration operation + #[serde(rename = "startDatetime", skip_serializing_if = "Option::is_none")] + pub start_datetime: Option, + /// The end time of the configuration operation + #[serde(rename = "endDatetime", skip_serializing_if = "Option::is_none")] + pub end_datetime: Option, + /// The duration of the configuration operation + #[serde(skip_serializing_if = "Option::is_none")] + pub duration: Option, + /// The security context of the configuration operation, can be specified to be required + #[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")] + pub security_context: Option, + /// Identifies if the operation is part of a configuration + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] @@ -54,7 +79,7 @@ pub struct Configuration { pub variables: Option>, pub resources: Vec, #[serde(skip_serializing_if = "Option::is_none")] - pub metadata: Option, + pub metadata: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/dsc_lib/src/configure/config_result.rs b/dsc_lib/src/configure/config_result.rs index c6e9b641..d7d036a4 100644 --- a/dsc_lib/src/configure/config_result.rs +++ b/dsc_lib/src/configure/config_result.rs @@ -4,9 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult}; -use crate::configure::config_doc::Configuration; - -use super::config_doc::SecurityContextKind; +use crate::configure::config_doc::{Configuration, Metadata}; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum MessageLevel { @@ -25,41 +23,6 @@ pub struct ResourceMessage { pub level: MessageLevel, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub enum Operation { - Get, - Set, - Test, - Export, -} - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub enum ExecutionKind { - Actual, - WhatIf, -} - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub struct ResultMetadata { - #[serde(rename = "Microsoft.DSC")] - pub microsoft: MicrosoftDscResultMetadata, -} - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub struct MicrosoftDscResultMetadata { - pub version: String, - pub operation: Operation, - #[serde(rename = "executionType")] - pub execution_type: ExecutionKind, - #[serde(rename = "startDatetime")] - pub start_datetime: String, - #[serde(rename = "endDatetime")] - pub end_datetime: String, - pub duration: String, - #[serde(rename = "securityContext")] - pub security_context: SecurityContextKind, -} - #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceGetResult { @@ -82,7 +45,7 @@ impl From for ResourceGetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationGetResult { - pub metadata: Option, + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -155,7 +118,7 @@ impl Default for GroupResourceSetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationSetResult { - pub metadata: Option, + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -213,7 +176,7 @@ impl Default for GroupResourceTestResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationTestResult { - pub metadata: Option, + pub metadata: Option, pub results: Vec, pub messages: Vec, #[serde(rename = "hadErrors")] @@ -241,7 +204,7 @@ impl Default for ConfigurationTestResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ConfigurationExportResult { - pub metadata: Option, + pub metadata: Option, pub result: Option, pub messages: Vec, #[serde(rename = "hadErrors")] diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index c4793af2..93823ebe 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use chrono::{DateTime, Local}; -use crate::configure::ExecutionKind; +use crate::configure::config_doc::ExecutionKind; use security_context_lib::{get_security_context, SecurityContext}; use serde_json::Value; use std::collections::HashMap; diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 422dbee1..5ac6a096 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::configure::config_doc::{MicrosoftMetadata, MicrosoftMetadata::DscInput}; +use crate::configure::config_doc::Metadata; use crate::configure::parameters::Input; use crate::dscerror::DscError; use crate::dscresources::dscresource::get_diff; @@ -12,9 +12,9 @@ use crate::DscResource; use crate::discovery::Discovery; use crate::parser::Statement; use self::context::Context; -use self::config_doc::{Configuration, DataType, SecurityContextKind}; +use self::config_doc::{Configuration, DataType, MicrosoftDscMetadata, Operation, SecurityContextKind}; use self::depends_on::get_resource_invocation_order; -use self::config_result::{ConfigurationExportResult, ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult, ExecutionKind, MicrosoftDscResultMetadata, Operation, ResultMetadata}; +use self::config_result::{ConfigurationExportResult, ConfigurationGetResult, ConfigurationSetResult, ConfigurationTestResult}; use self::contraints::{check_length, check_number_limits, check_allowed_values}; use indicatif::ProgressStyle; use security_context_lib::{SecurityContext, get_security_context}; @@ -165,14 +165,14 @@ fn add_metadata(kind: &Kind, mut properties: Option> ) -> Res Ok(serde_json::to_string(&properties)?) } -fn check_security_context(metadata: &Option) -> Result<(), DscError> { +fn check_security_context(metadata: &Option) -> Result<(), DscError> { if metadata.is_none() { return Ok(()); } - if let Some(DscInput(metadata)) = &metadata { + if let Some(metadata) = &metadata { if let Some(microsoft_dsc) = &metadata.microsoft { - if let Some(required_security_context) = µsoft_dsc.required_security_context { + if let Some(required_security_context) = µsoft_dsc.security_context { match required_security_context { SecurityContextKind::Current => { // no check needed @@ -432,11 +432,7 @@ impl Configurator { add_resource_export_results_to_configuration(dsc_resource, Some(dsc_resource), &mut conf, input.as_str())?; } - conf.metadata = Some( - MicrosoftMetadata::DscResult( - self.get_result_metadata(Operation::Export) - ) - ); + conf.metadata = Some(self.get_result_metadata(Operation::Export)); result.result = Some(conf); std::mem::drop(pb_span_enter); std::mem::drop(pb_span); @@ -511,18 +507,21 @@ impl Configurator { Ok(()) } - fn get_result_metadata(&self, operation: Operation) -> ResultMetadata { + fn get_result_metadata(&self, operation: Operation) -> Metadata { let end_datetime = chrono::Local::now(); - ResultMetadata { - microsoft: MicrosoftDscResultMetadata { - version: env!("CARGO_PKG_VERSION").to_string(), - operation, - execution_type: self.context.execution_type.clone(), - start_datetime: self.context.start_datetime.to_rfc3339(), - end_datetime: end_datetime.to_rfc3339(), - duration: end_datetime.signed_duration_since(self.context.start_datetime).to_string(), - security_context: self.context.security_context.clone(), - } + Metadata { + microsoft: Some( + MicrosoftDscMetadata { + context: None, + version: Some(env!("CARGO_PKG_VERSION").to_string()), + operation: Some(operation), + execution_type: Some(self.context.execution_type.clone()), + start_datetime: Some(self.context.start_datetime.to_rfc3339()), + end_datetime: Some(end_datetime.to_rfc3339()), + duration: Some(end_datetime.signed_duration_since(self.context.start_datetime).to_string()), + security_context: Some(self.context.security_context.clone()), + } + ) } } From 199ddb222c3c8255de7926e93f38bb43e67705c2 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 13:01:43 -0700 Subject: [PATCH 5/8] fix yaml for test --- dsc/examples/require_admin.yaml | 2 +- dsc/examples/require_nonadmin.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc/examples/require_admin.yaml b/dsc/examples/require_admin.yaml index 1ad061e4..90afa5b8 100644 --- a/dsc/examples/require_admin.yaml +++ b/dsc/examples/require_admin.yaml @@ -4,7 +4,7 @@ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: Microsoft.DSC: - requiredSecurityContext: Elevated + securityContext: Elevated resources: - name: os type: Microsoft/OSInfo diff --git a/dsc/examples/require_nonadmin.yaml b/dsc/examples/require_nonadmin.yaml index 9f273905..cf3f0e05 100644 --- a/dsc/examples/require_nonadmin.yaml +++ b/dsc/examples/require_nonadmin.yaml @@ -3,7 +3,7 @@ $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json metadata: Microsoft.DSC: - requiredSecurityContext: Restricted + securityContext: Restricted resources: - name: os type: Microsoft/OSInfo From 73e66feb873eb4ca76f11fadad63c6fd02ad23ab Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 16:26:55 -0700 Subject: [PATCH 6/8] add metadata to resource result --- dsc/tests/dsc_group.tests.ps1 | 42 +++++++++++++++++---- dsc_lib/src/configure/config_doc.rs | 15 ++++++++ dsc_lib/src/configure/config_result.rs | 7 ++++ dsc_lib/src/configure/mod.rs | 52 ++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 7 deletions(-) diff --git a/dsc/tests/dsc_group.tests.ps1 b/dsc/tests/dsc_group.tests.ps1 index bbe038fd..03468a52 100644 --- a/dsc/tests/dsc_group.tests.ps1 +++ b/dsc/tests/dsc_group.tests.ps1 @@ -16,31 +16,59 @@ metadata: duration: PT*S securityContext: * results: -- name: First Group +- metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: First Group type: Microsoft.DSC/Group result: - - name: First + - metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: First type: Test/Echo result: actualState: output: First - - name: Nested Group + - metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: Nested Group type: Microsoft.DSC/Group result: - - name: Nested First + - metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: Nested First type: Test/Echo result: actualState: output: Nested First - - name: Nested Second + - metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: Nested Second type: Test/Echo result: actualState: output: Nested Second -- name: Last Group +- metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: Last Group type: Microsoft.DSC/Group result: - - name: Last + - metadata: + Microsoft.DSC: + version: 0.1.0 + duration: * + name: Last type: Test/Echo result: actualState: diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index e0a0389d..00d9ad47 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -61,6 +61,21 @@ pub struct MicrosoftDscMetadata { pub context: Option, } +impl Default for MicrosoftDscMetadata { + fn default() -> Self { + Self { + version: None, + operation: None, + execution_type: None, + start_datetime: None, + end_datetime: None, + duration: None, + security_context: None, + context: None, + } + } +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub struct Metadata { #[serde(rename = "Microsoft.DSC", skip_serializing_if = "Option::is_none")] diff --git a/dsc_lib/src/configure/config_result.rs b/dsc_lib/src/configure/config_result.rs index d7d036a4..7a622666 100644 --- a/dsc_lib/src/configure/config_result.rs +++ b/dsc_lib/src/configure/config_result.rs @@ -26,6 +26,8 @@ pub struct ResourceMessage { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceGetResult { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, pub name: String, #[serde(rename="type")] pub resource_type: String, @@ -35,6 +37,7 @@ pub struct ResourceGetResult { impl From for ResourceGetResult { fn from(test_result: ResourceTestResult) -> Self { Self { + metadata: None, name: test_result.name, resource_type: test_result.resource_type, result: test_result.result.into(), @@ -88,6 +91,8 @@ impl From for ConfigurationGetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceSetResult { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, pub name: String, #[serde(rename="type")] pub resource_type: String, @@ -146,6 +151,8 @@ impl Default for ConfigurationSetResult { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceTestResult { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, pub name: String, #[serde(rename="type")] pub resource_type: String, diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 5ac6a096..44e6779a 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -241,9 +241,22 @@ impl Configurator { debug!("resource_type {}", &resource.resource_type); let filter = add_metadata(&dsc_resource.kind, properties)?; trace!("filter: {filter}"); + let start_datetime = chrono::Local::now(); let get_result = dsc_resource.get(&filter)?; + let end_datetime = chrono::Local::now(); self.context.outputs.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&get_result)?); let resource_result = config_result::ResourceGetResult { + metadata: Some( + Metadata { + microsoft: Some( + MicrosoftDscMetadata { + version: Some(dsc_resource.version.clone()), + duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), + ..Default::default() + } + ) + } + ), name: resource.name.clone(), resource_type: resource.resource_type.clone(), result: get_result, @@ -303,9 +316,22 @@ impl Configurator { if exist || dsc_resource.capabilities.contains(&Capability::SetHandlesExist) { debug!("Resource handles _exist or _exist is true"); + let start_datetime = chrono::Local::now(); let set_result = dsc_resource.set(&desired, skip_test)?; + let end_datetime = chrono::Local::now(); self.context.outputs.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&set_result)?); let resource_result = config_result::ResourceSetResult { + metadata: Some( + Metadata { + microsoft: Some( + MicrosoftDscMetadata { + version: Some(dsc_resource.version.clone()), + duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), + ..Default::default() + } + ) + } + ), name: resource.name.clone(), resource_type: resource.resource_type.clone(), result: set_result, @@ -314,7 +340,9 @@ impl Configurator { } else if dsc_resource.capabilities.contains(&Capability::Delete) { debug!("Resource implements delete and _exist is false"); let before_result = dsc_resource.get(&desired)?; + let start_datetime = chrono::Local::now(); dsc_resource.delete(&desired)?; + let end_datetime = chrono::Local::now(); let after_result = dsc_resource.get(&desired)?; // convert get result to set result let set_result = match before_result { @@ -336,6 +364,17 @@ impl Configurator { }; self.context.outputs.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&set_result)?); let resource_result = config_result::ResourceSetResult { + metadata: Some( + Metadata { + microsoft: Some( + MicrosoftDscMetadata { + version: Some(dsc_resource.version.clone()), + duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), + ..Default::default() + } + ) + } + ), name: resource.name.clone(), resource_type: resource.resource_type.clone(), result: SetResult::Resource(set_result), @@ -380,9 +419,22 @@ impl Configurator { debug!("resource_type {}", &resource.resource_type); let expected = add_metadata(&dsc_resource.kind, properties)?; trace!("expected: {expected}"); + let start_datetime = chrono::Local::now(); let test_result = dsc_resource.test(&expected)?; + let end_datetime = chrono::Local::now(); self.context.outputs.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&test_result)?); let resource_result = config_result::ResourceTestResult { + metadata: Some( + Metadata { + microsoft: Some( + MicrosoftDscMetadata { + version: Some(dsc_resource.version.clone()), + duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), + ..Default::default() + } + ) + } + ), name: resource.name.clone(), resource_type: resource.resource_type.clone(), result: test_result, From 4844a3bbb937ed6ebb3f56dc4f7a49f334227094 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sat, 13 Apr 2024 16:33:50 -0700 Subject: [PATCH 7/8] fix clippy --- dsc_lib/src/configure/config_doc.rs | 17 +---------------- dsc_lib/src/configure/mod.rs | 1 + 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index 00d9ad47..d48f3562 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -33,7 +33,7 @@ pub enum ExecutionKind { WhatIf, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub struct MicrosoftDscMetadata { /// Version of DSC #[serde(skip_serializing_if = "Option::is_none")] @@ -61,21 +61,6 @@ pub struct MicrosoftDscMetadata { pub context: Option, } -impl Default for MicrosoftDscMetadata { - fn default() -> Self { - Self { - version: None, - operation: None, - execution_type: None, - start_datetime: None, - end_datetime: None, - duration: None, - security_context: None, - context: None, - } - } -} - #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub struct Metadata { #[serde(rename = "Microsoft.DSC", skip_serializing_if = "Option::is_none")] diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 44e6779a..c584ecc1 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -282,6 +282,7 @@ impl Configurator { /// # Errors /// /// This function will return an error if the underlying resource fails. + #[allow(clippy::too_many_lines)] pub fn invoke_set(&mut self, skip_test: bool, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result { let config = self.validate_config()?; let mut result = ConfigurationSetResult::new(); From ed435ffe8dfe0a57192d746d43bc27461c62dfa1 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Sun, 14 Apr 2024 08:48:31 -0700 Subject: [PATCH 8/8] remove version from resource metadata --- dsc/tests/dsc_group.tests.ps1 | 7 ------- dsc_lib/src/configure/mod.rs | 4 ---- 2 files changed, 11 deletions(-) diff --git a/dsc/tests/dsc_group.tests.ps1 b/dsc/tests/dsc_group.tests.ps1 index 03468a52..90c89780 100644 --- a/dsc/tests/dsc_group.tests.ps1 +++ b/dsc/tests/dsc_group.tests.ps1 @@ -18,14 +18,12 @@ metadata: results: - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: First Group type: Microsoft.DSC/Group result: - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: First type: Test/Echo @@ -34,14 +32,12 @@ results: output: First - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: Nested Group type: Microsoft.DSC/Group result: - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: Nested First type: Test/Echo @@ -50,7 +46,6 @@ results: output: Nested First - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: Nested Second type: Test/Echo @@ -59,14 +54,12 @@ results: output: Nested Second - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: Last Group type: Microsoft.DSC/Group result: - metadata: Microsoft.DSC: - version: 0.1.0 duration: * name: Last type: Test/Echo diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index c584ecc1..c2132faf 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -250,7 +250,6 @@ impl Configurator { Metadata { microsoft: Some( MicrosoftDscMetadata { - version: Some(dsc_resource.version.clone()), duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), ..Default::default() } @@ -326,7 +325,6 @@ impl Configurator { Metadata { microsoft: Some( MicrosoftDscMetadata { - version: Some(dsc_resource.version.clone()), duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), ..Default::default() } @@ -369,7 +367,6 @@ impl Configurator { Metadata { microsoft: Some( MicrosoftDscMetadata { - version: Some(dsc_resource.version.clone()), duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), ..Default::default() } @@ -429,7 +426,6 @@ impl Configurator { Metadata { microsoft: Some( MicrosoftDscMetadata { - version: Some(dsc_resource.version.clone()), duration: Some(end_datetime.signed_duration_since(start_datetime).to_string()), ..Default::default() }