Skip to content

(DSC) Idiomaticize resource schemas - #2040

Open
Mikey Lombardi (He/Him) (michaeltlombardi) wants to merge 3 commits into
PowerShell:masterfrom
michaeltlombardi:dsc/main/idiomaticize-resource-schemas
Open

Mikey Lombardi (He/Him) (michaeltlombardi) wants to merge 3 commits into
PowerShell:masterfrom
michaeltlombardi:dsc/main/idiomaticize-resource-schemas

Conversation

@michaeltlombardi

Copy link
Copy Markdown

PR Summary

This change:

  1. Removes null from the type keyword for optional resource properties that don't explicitly model null as a valid state.
  2. Updates the Microsoft.PowerShell.PSResourceGet/Repository schema to define required at the top-level to indicate that name is always required and simplifies the allOf keyword for the remaining case (when _exist is true then uri is required).
  3. Updates the serialization for the resource types to idiomatically omit optional undefined properties and consolidates standardized trace messaging for the serialization process.
  4. Updates the export functionality to gracefully handle cases where a scope has zero installed PSResources instead of emitting an unhandled (non-terminating) error and returning invalid data that fails schema validation in DSC.

PR Context

Prior to this change, there were a few issues with the DSC resource schemas regarding the idiomatic definitions and semantics of JSON schema:

  1. Optional properties should not define the type keyword with null as a valid data type unless null is an explicitly modeled state for the property.

    In JSON Schema, the absence of a property in an object is semantically distinct from a property explicitly set to null.

    Instead, we control the optionality of properties through the required or dependentRequired keywords.

  2. The schema for Microsoft.PowerShell.PSResourceGet/Repository didn't define required at the top-level. Instead, it used the allOf keyword to indicate whether the name and uri properties are required or just name.

    When a property is always required, we should hoist it to the top-level required keyword array. We can still use the allOf to conditionally extend the schema as needed.

    Parsing a JSON Schema is difficult for consumers, moreso when it requires unrolling schema composition. We should surface as much information to the consumer in the simplest form possible.

PR Checklist

Prior to this change, there were a few issues with the DSC resource
schemas regarding the idiomatic definitions and semantics of JSON
schema:

1. Optional properties should not define the `type` keyword with `null`
   as a valid data type unless `null` is an explicitly modeled state
   for the property.

   In JSON Schema, the absence of a property in an object is
   semantically distinct from a property explicitly set to `null`.

   Instead, we control the optionality of properties through the
   `required` or `dependentRequired` keywords.
1. The schema for `Microsoft.PowerShell.PSResourceGet/Repository`
   didn't define `required` at the top-level. Instead, it used the
   `allOf` keyword to indicate whether the `name` and `uri` properties
   are required or just `name`.

   When a property is always required, we should hoist it to the
   top-level `required` keyword array. We can still use the `allOf` to
   conditionally extend the schema as needed.

   Parsing a JSON Schema is difficult for consumers, moreso when it
   requires unrolling schema composition. We should surface as much
   information to the consumer in the simplest form possible.

To address these issues, this change:

1. Removes `null` from the `type` keyword for optional properties that
   don't explicitly model `null` as a valid state.
1. Updates the `Microsoft.PowerShell.PSResourceGet/Repository` schema
   to define `required` at the top-level to indicate that `name` is
   always required and simplifies the `allOf` keyword for the remaining
   case (when `_exist` is `true` then `uri` is required).
Prior to this change, the resource script serialized the data by just
converting the instances to JSON (stripping `_inDesiredState` for
non-test operations).

This caused the resource to serialize `null` for properties that
weren't defined on the instance, causing a mismatch with the idiomatic
schema.

This change updates the serialization logic to omit undefined optional
fields from the serialized output by:

1. Defining the `ToData` method to convert instances to a data
   representation that omits undefined optional fields while preserving
   property order for predictable serialization.

   The method defines two overloads:

   - `ToData([bool$forTest)`: Converts the instance to a data
      representation, omitting undefined optional fields. If `$forTest`
      is `$false`, the method doesn't insert `_inDesiredState`.
   - `ToData()`: Convenience overload to call `ToData($false)`.
1. Standardizing the serialization trace messaging and extracting into
   reusable methods.
Prior to this change, the `PSResourceList` resource would fail during
`export` operations when a scope had no installed resources because
it would emit an empty/invalid item. The non-terminating error for
no resources in a scope was also erroneously emitted.

This change:

1. Defines the `GetAllPsResourcesInScope` function as a wrapper around
   `Get-PsResource` to retrieve all resources in a given scope,
   returning an empty array if no resources are found and erroring if
   the command fails for any other reason.
1. Updates `PopulatePSResourceListObject` to handle empty resource
   arrays gracefully, emitting an info message instead of invalid data.
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate export and schema-validation issues remain.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR updates DSC schemas and PSResource serialization/export behavior.

Changes:

  • Removes unnecessary nullable types.
  • Simplifies repository schema requirements.
  • Refactors serialization and empty-scope export handling.
File summaries
File Summary and final review comments
src/dsc/repository.dsc.resource.json Refines repository requirements. Moderate (1 vote): Require _exist inside the if schema so absent _exist does not trigger the then branch.
src/dsc/psresourcelist.dsc.resource.json Removes unnecessary nullable property types.
src/dsc/psresourceget.ps1 Updates serialization and export behavior. Critical (3 votes): Single-resource scopes are incorrectly treated as empty at lines 913 and 927. Moderate (3 votes): Empty repositoryName omits a required property. Moderate (3 votes): Catch-all exception handling hides failures as empty results. Nit (1 vote): Add tests for empty scopes and unexpected query failures.
Review details

Suppressed comments (3)

src/dsc/psresourceget.ps1:927

  • When a scope contains exactly one resource, .Count is 1, so this branch treats the scope as empty and omits that resource from the export. Test for a non-empty collection instead.
    if ($currentUserPSResources.count -gt 1) {

src/dsc/psresourceget.ps1:863

  • The new empty-scope and error-handling branches are not exercised by the DSC tests: the existing export test installs testmodule99 and only asserts a non-empty exported list. Add coverage for a scope with no installed resources (and for an unexpected query failure) so the new fallback cannot regress into invalid or silently incomplete export output.
    try {
        Get-PSResource -Scope $Scope -ErrorAction Stop
    } catch [Microsoft.PowerShell.PSResourceGet.UtilClasses.ResourceNotFoundException] {
        # Everything is fine, there's just no installed resources
        @()
    } catch {
        Write-Trace -level error -message "Failed to get PSResources for '$Scope' scope: $_"
        @()
    }

src/dsc/repository.dsc.resource.json:83

  • Because if only has properties, it also evaluates true when _exist is absent (properties does not require the property). The then branch therefore requires uri for inputs such as { "name": "repo" }, even though the condition is supposed to apply only when _exist is true. Add required: ["_exist"] inside the if schema.
                                "const": true
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/dsc/psresourceget.ps1
$_.Repository,
$_.PreRelease ? $true : $false
)
if ($allUsersPSResources.count -gt 1) {
Comment thread src/dsc/psresourceget.ps1
Comment on lines +207 to +209
if (-not [string]::IsNullOrEmpty($this.repositoryName)) {
$data['repositoryName'] = $this.repositoryName
}
Comment thread src/dsc/psresourceget.ps1
Comment on lines +860 to +863
} catch {
Write-Trace -level error -message "Failed to get PSResources for '$Scope' scope: $_"
@()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants