DSC icon indicating copy to clipboard operation
DSC copied to clipboard

`exporter` kind of resource

Open SteveL-MSFT opened this issue 1 year ago • 3 comments
trafficstars

Summary of the new feature / enhancement

There is a scenario where the configuration being exported is dynamic rather than statically defined within a configuration doc. Some examples:

  1. User has manually installed a bunch of applications and configured those apps. They want to export all their apps that have DSCv3 enabled configuration to a single doc. An exporter resource would be responsible for enumerating those apps and associating them to the relevant DSCv3 resource.
  2. User has configured an IDE (like VSCode), installed a bunch of extensions, configured those extensions. They want to export that entire stack of configuration and apply it to another machine.

In both cases, it doesn't make sense to have the user manually craft a configuration and instead, they want to run export at a top-level application (which may mean in the future we may need to have a meta-resource to represent an entire system that recursively exports).

Proposed technical implementation details (optional)

We allow for an exporter kind of resource that only implements export method. There's at least two ways to support this:

  1. expectation is that the exporter returns not configuration, but resource instances that need export called which dsc engine will handle. In this case, dsc would be the one recursively calling export where there could be a nested exporter so the user eventually gets a single doc that represents the total configuration
  2. the exporter resource handles calling export and potentially recursion and returns a complete configuration that can be applied

I think option 1 makes more sense so that the exporter resource doesn't need to call back out to dsc to then call a resource to export or implement the same functionality that dsc already provides

SteveL-MSFT avatar Aug 06 '24 20:08 SteveL-MSFT

For the sake of visibility:

In the case of WinGet as an orchestrator, we have an experimental feature in place for generating configuration from the current state for a single resource or package (and resource). This works with a few DSC v2 resources, but it's more of a building block to work on top of today.

In the future, WinGet would iterate over all installed packages, and it would be aware of Windows Settings.

Dev Home also has plans to support a logical "get my current configuration" and then some kind of editor type of experience to modify the generated configuration.

I think the approach here makes perfect sense in the world of DSC v3 only scenarios for DSC.exe, but in the current hybrid world, we're likely to see multiple different scenarios that need unique treatments.

denelon avatar Aug 06 '24 21:08 denelon

A few things I think important to note about any potential recursive export implementations:

  1. Currently, DSC can only perform the export for resources that are already installed on the machine.

    In the future, with a repository for DSC resource packages, this could be runtime-resolvable. Right now, if you don't already have a resource installed and available, DSC simply can't know that it exists.

    For example, you might have a resource to manage the installation of an application with WinGet, but not that application's configuration resource(s). So the export would be able to ensure that the application gets installed at the right version, but it installs with the default config, not the current config.

  2. I don't think that there's a good way, right now, for resources to indicate (and therefore, for the export function to propogate) the difference between "This instance is configured for property foo to be set to 1 and "This instance implicitly uses the default value 1 for property foo."

    This primarily matters when a resource or application updates. This can lead to a case where both systems seem to have the same configuration before an update, but after updating the systems have different behavior - and from the perspective of the user, they only changed the exact same thing on both systems.

  3. I don't see a good way, without custom implementations for the exporters, to map dependencies between exported instances.

    I don't see how DSC itself (or higher order tools/exporter resources without custom logic) can tell that a package resource instance and a configuration file resource instance are related. With no way to define dependencies, you can end up with a configuration that tries to configure an app that isn't installed, or a web server before the database is defined, etc.

  4. I'm not sure I see a coherent way for the exporter to handle groups.

    Similar to dependencies, there's no way to associate a set of resources together, except through an adapter or group resource - whereas for readability and dependency management I would probably want the application package, configuration, and service resource instances grouped together.

I think recursive export is a good idea, but there's limitations to the operation that I don't see feasible options for that won't require a lot of custom logic or new resource capabilities for.

I also think that we should always be very clear when we describe export-for-reuse operations that the output from that operation is a starting point and should always be inspected and approved by a human before using that output. Especially when we're thinking about recursively exporting the full configuration of a system, but even when exporting the current configuration of a small group of applications.

michaeltlombardi avatar Aug 07 '24 15:08 michaeltlombardi

For the mapping of applications to resources, I think a SWID property in the resource manifest should be sufficient. SWIDs are XML, so I suppose this would be a string that contains XML. However, it does appear that SWID XML doesn't contain content and is only relying on attributes, so this could be converted to JSON.

It would make sense to have a property in the manifest for exporter kinds to declare if it returns and exportable config (which initiates recursion) or not (should be used directly to apply).

SteveL-MSFT avatar Aug 08 '24 00:08 SteveL-MSFT

Thinking about this and the need for it sooner than later, I'm going to have a simplified proposal:

  • we won't define any schema for defining relationships (I do think SWID is probably the right way to go)
  • we won't focus on any recursion that requires dsc engine to participate
  • we allow a resource to emit exported settings for different resources (not itself)

So the primary difference between a resource that supports export and an exporter type resource is that the former exports instances of itself while the latter exports instances for other resources (this assumes the resource itself has built-in knowledge about other resources and we won't define here how that works).

As an example, assume this is the config you want to export:

$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: Windows OS
  type: Microsoft.Windows/System
  properties:
    scope: CurrentUser

In this case, we assume there's a hypothetical resource called Microsoft.Windows/System that has built-in knowledge about different settings exposed that are supported by DSC resources. To be clear, an exporter resource ONLY supports export and doesn't support get, set, or test as that it is expected that the underlying resource that supports the settings allow those operations against the exported configuration.

The resource manifest for this resource would look like:

{
    "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
    "type": "Microsoft.Windows/System",
    "version": "1.0.0",
    "description": "Exports Windows system or user settings",
    "kind": "Exporter",
    "export": {
        "executable": "windows_exporter.exe",
        "args": [
            {
                "jsonInputArg": "--input",
                "mandatory": true
            }
        ]
    }
}

As noted, this resource does not have its own schema so that section doesn't exist in the manifest. We can extend this in the future by adding properties under the export property to support scenarios such as an exporter may simply return a list of resources for dsc to call export against.

So for this simplified example, let's assume that this exporter resource knows about winget and some basic Windows settings (like dark vs light theme). So the output from the exporter resource would look like (the actual output would be JSON):

- name: WinGet packages
  type: Microsoft.Windows/WinGet
  properties:
    packages:
      - id: Microsoft.PowerShell
        version: 7.5.0
      - id: Rustlang.Rustup
        version: 1.27.1
- name: Windows User Settings
  type: Microsoft.Windows/Settings
  properties:
    theme: Dark

dsc engine would only validate that the top level members match a declared resource and not any resource specific schema. If validation succeeds, the engine would wrap it as a valid configuration:

$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: WinGet packages
  type: Microsoft.Windows/WinGet
  properties:
    packages:
      - id: Microsoft.PowerShell
        version: 7.5.0
      - id: Rustlang.Rustup
        version: 1.27.1
- name: Windows User Settings
  type: Microsoft.Windows/Settings
  properties:
    theme: Dark

SteveL-MSFT avatar Feb 21 '25 19:02 SteveL-MSFT

As noted, this resource does not have its own schema so that section doesn't exist in the manifest.

It probably should have a schema to define the inputs to the exporter. For instance, your example has scope: CurrentUser.

JohnMcPMS avatar Feb 24 '25 18:02 JohnMcPMS

As noted, this resource does not have its own schema so that section doesn't exist in the manifest.

It probably should have a schema to define the inputs to the exporter. For instance, your example has scope: CurrentUser.

Yes, schema for input, but not used to validate output.

SteveL-MSFT avatar Feb 25 '25 00:02 SteveL-MSFT