PowerShell / DSC

This repo is for the DSC v3 project
MIT License
195 stars 24 forks source link

Make output for group and provider resources in `dsc config *` more readable #266

Closed michaeltlombardi closed 2 months ago

michaeltlombardi commented 10 months ago

Summary of the new feature / enhancement

As a user, I want to be able to use group and provider resources in my configuration documents and understand the output for dsc config commands without having to know how DSC works internally or investigate the data to know which keys mean what.

While inspecting the output for group and provider resources, I noticed that the output structure for those resources is very difficult to read.

  1. Define a configuration that uses a group or provider resource.

    Configuration document ```yaml # repro.dsc.config.yaml $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: current user registry type: Microsoft.Windows/Registry properties: keyPath: HKCU\example _exist: true dependsOn: - "[resourceId('DSC/AssertionGroup','my assertions')]" - name: my assertions type: DSC/AssertionGroup properties: $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: os type: Microsoft/OSInfo properties: family: Windows - name: system root type: Microsoft.Windows/Registry properties: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows ```
  2. Get the current state of the configuration.

    dsc --input-file ./assertion.dsc.config.yaml config get
    Actual Output ```yaml results: - name: my assertions type: DSC/AssertionGroup result: actualState: results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false - name: current user registry type: Microsoft.Windows/Registry result: actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false messages: [] hadErrors: false ```
    Proposed Output ```yaml results: - name: my assertions type: DSC/AssertionGroup results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false - name: current user registry type: Microsoft.Windows/Registry result: actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false messages: [] hadErrors: false ```
  3. Test whether the configuration is in the desired state

    dsc --input-file ./assertion.dsc.config.yaml config test
    Actual Output ```yaml results: - name: my assertions type: DSC/AssertionGroup result: desiredState: $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json resources: - name: os type: Microsoft/OSInfo properties: family: Windows - name: system root type: Microsoft.Windows/Registry properties: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false inDesiredState: false differingProperties: - $schema - resources - name: current user registry type: Microsoft.Windows/Registry result: desiredState: keyPath: HKCU\example _exist: true actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false _inDesiredState: false inDesiredState: false differingProperties: - _exist messages: [] hadErrors: false ```
    Proposed Output ```yaml results: - name: my assertions type: DSC/AssertionGroup results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false - name: current user registry type: Microsoft.Windows/Registry result: desiredState: keyPath: HKCU\example _exist: true actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false _inDesiredState: false inDesiredState: false differingProperties: - _exist messages: [] hadErrors: false ```
  4. Enforce the configuration to the desired state

    dsc --input-file ./assertion.dsc.config.yaml config set
    Actual Output ```yaml results: - name: my assertions type: DSC/AssertionGroup result: beforeState: results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false afterState: results: - name: os type: Microsoft/OSInfo result: desiredState: family: Windows actualState: $id: https://developer.microsoft.com/json-schemas/dsc/os_info/20230303/Microsoft.Dsc.OS_Info.schema.json family: Windows version: 10.0.22621 edition: Windows 11 Enterprise bitness: '64' inDesiredState: true differingProperties: [] - name: system root type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: Z:\Windows actualState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: SystemRoot valueData: String: C:\WINDOWS _exist: true _inDesiredState: false inDesiredState: false differingProperties: - valueData messages: [] hadErrors: false changedProperties: [] - name: current user registry type: Microsoft.Windows/Registry result: beforeState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false afterState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example changedProperties: - _exist messages: [] hadErrors: false ```
    Proposed Output ```yaml results: - name: my assertions type: DSC/AssertionGroup results: [] # none because the assertion group doesn't participate in set. messages: [] hadErrors: false - name: current user registry type: Microsoft.Windows/Registry result: beforeState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example _exist: false afterState: $id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json keyPath: HKCU\example changedProperties: - _exist messages: [] hadErrors: false ```

Proposed technical implementation details (optional)

Currently, DSC treats group and provider resources, which definitionally declare an array of resource instances, the same as normal instances.

Options

  1. Instead of defining the result property adhering to the dsc resource * output schema for each group/provider instance, those instances should define the results property adhering to the dsc config * output schemas. This would make it much easier to read and process the results.
  2. Have a transitory step that flattens the deeply nested map of resources and always return the top-level results as a flat array, associating nested instances to their parent with a parentResource or resourceGraphPath property. Using the examples above, the os resource instance would have my assertions as the parentResource or something like my assertions>os for resourceGraphPath.

Option 1 requires DSC, users, and integrating tools to distinguish between group/provider resource instances and normal resource instances in the output, but leaves the data more walkable/comparable to the configuration document definition.

Option 2 ensures that the output is always the same structurally, but may require integrating tools to handle re-grouping instance results.

SteveL-MSFT commented 10 months ago

It looks like the ask is that the provider is just a pass thru and doesn't have it's own wrapper result? As long as there isn't a scenario where a provider should have it's own properties, then that would make sense.

michaeltlombardi commented 10 months ago

As long as there isn't a scenario where a provider should have it's own properties, then that would make sense.

Is there a case where a provider would have properties that are enforceable at the provider level? What would that look like? I think a provider can still have write-only properties to control its behavior, like a hypothetical npm provider that selects the version or cache strategy or whatever. I can't think of a model where I'm enforcing the state of the provider itself in the same instance that I'm using it to declare nested instances.

SteveL-MSFT commented 8 months ago

Based on the discussion, it seems that the ask is to introduce a new GroupGetResult, GroupSetResult, and GroupTestResult schema which contains name, type, messages, haderrors, and indesiredstate and a nested GetResult/SetResult/TestResult.

This means dsc needs to validate the output from a provider resource against this schema and also update use of dsc as a group resource to adhere to this schema.

michaeltlombardi commented 7 months ago

Proposed output from dsc config test:

Full output ```yaml results: - name: PSDSC type: DSC/PowerShellGroup results: - name: OpenSSH service type: PsDesiredStateConfiguration/MSFT_ServiceResource result: desiredState: name: sshd state: running actualState: name: sshd state: stopped inDesiredState: false differingProperties: - state - name: Administrator type: PsDesiredStateConfiguration/MSFT_UserResource result: desiredState: UserName: administrator Ensure: Present actualState: UserName: administrator Ensure: Present inDesiredState: true differingProperties: [] messages: [] hadErrors: false inDesiredState: false - name: current user registry type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: ProductName _exist: true actualState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: ProductName _exist: true inDesiredState: true differingProperties: [] messages: [] hadErrors: false inDesiredState: false ```
Single instance result ```yaml name: current user registry type: Microsoft.Windows/Registry result: desiredState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: ProductName _exist: true actualState: keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion valueName: ProductName _exist: true inDesiredState: true differingProperties: [] ```
Group instance result ```yaml name: PSDSC type: DSC/PowerShellGroup results: - name: OpenSSH service type: PsDesiredStateConfiguration/MSFT_ServiceResource result: desiredState: name: sshd state: running actualState: name: sshd state: stopped inDesiredState: false differingProperties: - state - name: Administrator type: PsDesiredStateConfiguration/MSFT_UserResource result: desiredState: UserName: administrator Ensure: Present actualState: UserName: administrator Ensure: Present inDesiredState: true differingProperties: [] messages: [] hadErrors: false inDesiredState: false ```

In this proposed output, the structure for the output of a group resource is the same as the previous structure for a full configuration. The structure for the output of a singular instance of a resource is the same as before. The output is functionally recursive, which is relatively straightforward to indicate with the JSON schema.

michaeltlombardi commented 7 months ago

When we address this, we'll be making a breaking change to the schema again, so it might be worth co-addressing this with #138, making the schema easier to map to the version(s) that support it.