Skip to content

(GH-1755) Add new DSC resource to manage SecurityPolicy - #1759

Open
Gijs Reijn (Gijsreyn) wants to merge 3 commits into
PowerShell:mainfrom
Gijsreyn:gh-1755/main/add-securitypolicy-dsc-resource
Open

Gijs Reijn (Gijsreyn) wants to merge 3 commits into
PowerShell:mainfrom
Gijsreyn:gh-1755/main/add-securitypolicy-dsc-resource

Conversation

@Gijsreyn

Copy link
Copy Markdown
Collaborator

PR Summary

This change:

  • Adds the Microsoft.Windows/UserRightsAssignment resource. One instance per user right, keyed by the Windows constant (SeServiceLogonRight), with an identity list of accounts and a write-only setAction of replace (default), add or remove. Supports get, set, test, export, and --what-if.
  • Adds the windows_security_policy crate and manifests file that will also host the later AccountPolicy, SecurityOption and SecurityTemplate types.
  • Enables the Win32_Security_Authentication_Identity feature of the workspace windows dependency.
  • Adds reference docs, overview and changelog entries, package file entries, Pester tests and Rust unit tests.
  • Addresses the SecurityPolicyDsc part of Hardening with dsc 3 ? #1755, starting with its UserRightsAssignment resource.

PR Context

SecurityPolicyDsc manages user rights by exporting and re-importing secedit templates, so it depends on temp files, INF parsing and localized output. This resource calls the LSA account-rights APIs (LsaEnumerateAccountsWithUserRight, LsaAddAccountRights, LsaRemoveAccountRights) directly and compares accounts by SID, so Administrators, BUILTIN\Administrators and S-1-5-32-544 refer to the same holder. Holders are reported as DOMAIN\Name, or as the SID string when the account no longer exists.

Remarks on the design

  • SecurityPolicyDsc keyed an instance on the right plus Ensure and Force to express add, remove and exact-list semantics. Here identity is the complete list by default and setAction covers the other two cases, which matches how hardening baselines state these settings. An empty list with replace means No One.
  • The resource implements its own test. DSC's synthetic test compares arrays by exact string and skips write-only properties, so it can't judge add or remove or match an account written in a different form. The native test compares SID sets and reports _inDesiredState; DSC runs it before set because implementsPretest is false.
  • All operations require elevation, including get and export, because the LSA refuses to enumerate account rights from a non-elevated token.
  • Grants are applied before revokes so a logon right never has zero holders part-way through a replace. LsaRemoveAccountRights is always called for the single right, never with the flag that strips every right from an account.

Copilot AI balanced review requested due to automatic review settings October 16, 2026 11:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Input-validation gaps can cause unintended security-policy changes, and test correctness issues remain unresolved.

5 open findings
What changed in this PR

Adds Microsoft.Windows/UserRightsAssignment as the first native Windows security-policy resource addressing #1755.

Changes:

  • Implements SID-based get, set, test, export, and what-if operations using Windows LSA APIs.
  • Integrates the resource into workspace builds and Windows packaging.
  • Adds tests, reference documentation, examples, and review-guidance coverage.
File Description
resources/​windows_security_policy/​windows_security_policy.dsc.manifests.json Defines operations and resource schema.
resources/​windows_security_policy/​tests/​windows_security_policy_user_right_whatif.tests.ps1 Tests preview behavior.
resources/​windows_security_policy/​tests/​windows_security_policy_user_right_test.tests.ps1 Tests desired-state comparisons.
resources/​windows_security_policy/​tests/​windows_security_policy_user_right_set.tests.ps1 Tests policy changes and validation.
resources/​windows_security_policy/​tests/​windows_security_policy_user_right_get.tests.ps1 Tests reads and elevation requirements.
resources/​windows_security_policy/​tests/​windows_security_policy_user_right_export.tests.ps1 Tests exported assignments.
resources/​windows_security_policy/​src/​user_rights.rs Implements resource operations.
resources/​windows_security_policy/​src/​types.rs Defines state and error types.
resources/​windows_security_policy/​src/​sid.rs Resolves and normalizes identities.
resources/​windows_security_policy/​src/​rights.rs Lists supported user rights.
resources/​windows_security_policy/​src/​main.rs Handles CLI dispatch and JSON output.
resources/​windows_security_policy/​src/​lsa.rs Wraps LSA account-rights APIs.
resources/​windows_security_policy/​locales/​en-us.toml Provides localized messages.
resources/​windows_security_policy/​Cargo.toml Defines crate dependencies.
resources/​windows_security_policy/​build.rs Tracks translation changes.
resources/​windows_security_policy/​.project.data.json Registers Windows build artifacts.
docs/​reference/​resources/​overview.md Lists the new resource.
docs/​reference/​resources/​Microsoft/​Windows/​UserRightsAssignment/​index.md Documents the resource contract.
docs/​reference/​resources/​Microsoft/​Windows/​UserRightsAssignment/​examples/​userrightsassignment.config.dsc.yaml Demonstrates assignment modes.
docs/​reference/​resources/​Microsoft/​Windows/​UserRightsAssignment/​examples/​get-user-rights-assignment.md Demonstrates read, test, and export commands.
docs/​reference/​resources/​Microsoft/​Windows/​UserRightsAssignment/​examples/​configure-user-rights-assignment.md Demonstrates configuration and previews.
data.build.json Includes Windows package files.
CHANGELOG.md Announces the resource.
Cargo.toml Registers the crate and Windows API feature.
Cargo.lock Records crate dependencies.
.github/​skills/​code-review/​windows.instructions.md Extends Windows guidance scope.
.github/​skills/​code-review/​security.instructions.md Extends security guidance scope.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread resources/windows_security_policy/src/main.rs Outdated
Comment thread resources/windows_security_policy/src/sid.rs

/// Every user right, ordered by display name. This is also the order `export` reports them in.
pub const USER_RIGHTS: [UserRight; 45] = [
right("SeTrustedCredManAccessPrivilege", "Access Credential Manager as a trusted caller"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The display names should be in the loc file

);
if what_if {
return Ok(UserRightsAssignment {
right: input.right.clone(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It appears that the what_if doesn't take into account adding or removing, just replacing

@SteveL-MSFT

Copy link
Copy Markdown
Member

Mikey Lombardi (He/Him) (@michaeltlombardi) would be good to have you look at the schema design

@SteveL-MSFT

Copy link
Copy Markdown
Member

Gijs Reijn (@Gijsreyn) you can have copilot address the PR feedback and fix the merge conflict

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Elevated Windows policy mutations need human validation, and unresolved manifest contract issues remain.

1 open issue
1 previously missed issue

In code that hasn't changed since last review

Medium severity Declare right as the DSC instance key

resources/​windows_security_policy/​windows_security_policy.dsc.manifests.json:85

right is documented as the instance key, but the schema never declares x-dsc-key: true. DSC identifies key properties through that keyword (docs/concepts/resources/properties.md:77–79), not through required, so schema consumers currently see no instance key. Add the key annotation to match the resource's per-right identity contract.

5 resolved since last review

Review effort: Balanced

Comment on lines +157 to +178
"_inDesiredState": {
"type": "boolean",
"title": "In desired state",
"description": "Whether the current holders of the right satisfy 'identity' and 'setAction'. Returned by the test operation only.",
"readOnly": true
},
"_metadata": {
"type": "object",
"title": "Metadata",
"description": "Metadata returned by what-if operations describing the changes that would be applied.",
"readOnly": true,
"properties": {
"whatIf": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Agreed, prefer to follow the convention and reference the canonical definition while bundling it into the $defs.

The bundling is only required until the schema handling in the engine is updated to register resources.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Added some recommendations and considerations for the resource schema

},
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We really should prefer to define schemas in the latest published dialect:

Suggested change
"$schema": "http://json-schema.org/draft-07/schema#",
"$schema": "https://json-schema.org/draft/2020-12/schema",

"type": "string",
"title": "User right",
"description": "The user right or privilege constant, for example 'SeServiceLogonRight' (Log on as a service).",
"enum": [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit: Not required for this PR, but always worth considering defining markdownEnumDescriptions to help users quickly understand what these values mean. It would also be helpful to define markdownDescription and point users to where they can look up more information.

Comment on lines +139 to +142
"items": {
"type": "string",
"minLength": 1
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Worth considering whether we can more explicitly verify the inputs here, maybe with a oneOf keyword to distinguish between the different forms.

For example, we could define an enum for the well-known names, patterns for <DOMAIN>\<NAME>. <bare name>, <sid>, and <secedit>.

Based on the description we may want to raise a messaage (info?) when the input isn't in the DOMAIN\NAME form since it's not the canonical representation for accounts that exist and can be resolved.

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.

4 participants