Skip to content

AD and Entra ID Providers: Generate policy-compliant passwords with controlled output#180

Merged
blindzero merged 12 commits intomainfrom
copilot/generate-policy-aware-password
Feb 11, 2026
Merged

AD and Entra ID Providers: Generate policy-compliant passwords with controlled output#180
blindzero merged 12 commits intomainfrom
copilot/generate-policy-aware-password

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 11, 2026

AD user creation fails in domains with password policies when no password is provided. Onboarding workflows need initial passwords but unconditional plaintext output risks leaking secrets into logs/artifacts.

Changes

Password Generation (New-IdleADPassword - AD Provider)

  • Queries Get-ADDefaultDomainPasswordPolicy for domain requirements
  • Falls back to configurable rules (min length 24, complexity enabled) on policy read failure
  • Enforces fallback as minimum baseline even when domain allows weaker passwords
  • Generates passwords satisfying length + complexity (upper/lower/digit/special)
  • Validation: Empty special character set falls back to safe default with verbose logging

Password Generation (Entra ID Provider)

  • Generates GUID-based passwords (compliant with Entra ID requirements)
  • Returns generated password with controlled output (same pattern as AD provider)
  • Supports ForceChangePasswordNextSignIn attribute (default: $true)

Adapter Integration

  • AD: Auto-generates when Enabled = $true and no AccountPassword/AccountPasswordAsPlainText provided
  • Entra ID: Generates GUID password and returns it for administrator access
  • Supports ResetOnFirstLogin (AD) / ForceChangePasswordNextSignIn (Entra ID) attributes (defaults $true, overridable to $false)
  • Attaches generation metadata to result object

Controlled Output (Both Providers)

  • Default: GeneratedAccountPasswordProtected (DPAPI-scoped ProtectedString) + metadata
  • Opt-in: AllowPlainTextPasswordOutput = $true adds GeneratedAccountPasswordPlainText
  • Redaction rules updated for new password fields (redacts in logging/eventing paths, does not prevent direct access when explicitly requested)
  • Metadata includes PasswordGenerated, PasswordGenerationPolicyUsed (AD) or PasswordGenerationMethod (Entra ID)

Configuration (AD Provider)
Provider accepts fallback parameters: PasswordGenerationFallbackMinLength, PasswordGenerationRequire{Upper,Lower,Digit,Special}, PasswordGenerationSpecialCharSet

Documentation & Examples

  • Added comprehensive password generation section to docs/reference/providers/provider-ad.md:
    • Automatic password generation with provider configuration
    • Password output control with ProtectedString reveal path
    • Reset on first login behavior
    • Security warnings (DPAPI scope, plaintext handling)
  • Added comprehensive password generation section to docs/reference/providers/provider-entraID.md:
    • Automatic GUID-based password generation explanation
    • Password output control with ProtectedString reveal path
    • Control password reset requirement (ForceChangePasswordNextSignIn)
    • Security warnings (DPAPI scope, plaintext handling)
    • Code examples for all scenarios
  • Integrated password generation examples directly into ad-joiner-complete.psd1 with inline comments showing:
    • How automatic password generation triggers
    • Optional attributes (AllowPlainTextPasswordOutput, ResetOnFirstLogin)
    • Result properties containing password information
    • How to reveal password from ProtectedString
  • Simplified provider README (removed Key Features section per review feedback)

Example (works for both AD and Entra ID providers)

# Default: secure ProtectedString output
$result = $provider.CreateIdentity('jdoe@contoso.com', @{
    SamAccountName = 'jdoe'  # AD
    # or UserPrincipalName = 'jdoe@contoso.com'  # Entra ID
    Enabled = $true
})
$protectedPwd = $result.GeneratedAccountPasswordProtected

# Reveal when needed (DPAPI-scoped)
$secure = ConvertTo-SecureString -String $protectedPwd
$plain = [pscredential]::new('x', $secure).GetNetworkCredential().Password

# Opt-in plaintext
$result = $provider.CreateIdentity('jsmith@contoso.com', @{
    SamAccountName = 'jsmith'
    Enabled = $true
    AllowPlainTextPasswordOutput = $true
})
$plain = $result.GeneratedAccountPasswordPlainText

Testing

  • Added 10 password generation integration tests for AD provider covering generation triggers, controlled output modes, and reveal path. All 96 AD provider tests passing.
  • Added 19 unit tests for New-IdleADPassword function in tests/Providers/New-IdleADPassword.Tests.ps1 covering:
    • Fallback configuration scenarios (min length, character requirements, special char set handling)
    • Domain policy integration (policy read, min length enforcement, complexity requirements)
    • Password output formats (PlainText, SecureString, ProtectedString with round-trip validation)
    • Edge cases (AD minimum length, random variation, empty special char set validation)
  • Added 8 password generation tests for Entra ID provider covering generation, controlled output, password change requirements, and reveal path. All 52 Entra ID provider tests passing.
  • Added 4 redaction tests in tests/Core/Copy-IdleRedactedObject.Tests.ps1 to ensure GeneratedAccountPasswordPlainText and GeneratedAccountPasswordProtected are properly redacted in nested structures. All 12 redaction tests passing.
Original prompt

This section details on the original issue you should resolve

<issue_title>AD Provider: Generate initial policy-aware password and support controlled output</issue_title>
<issue_description>## Problem Statement

For AD joiner workflows, IdLE must be able to create and enable accounts in domains with password policies (length/complexity/history). If no password is supplied, user creation/enabling may fail with domain policy errors.

Additionally, onboarding often requires a human-readable initial password. Returning plaintext unconditionally is dangerous because results/events may be persisted, exported, or logged.

Goal:

  1. Generate policy-compliant initial passwords when none is supplied.
  2. Allow controlled, explicit plaintext output only when requested.
  3. Provide a safe “reveal” method when plaintext output is not requested.

Proposed Solution

1) Policy-aware password generation (with fallback)

When creating an AD user and no password input is provided (see Issue #1), and the account is requested to be enabled:

  1. Try to read domain password policy using Get-ADDefaultDomainPasswordPolicy.
  2. Generate a password that satisfies:
    • minimum length (policy or fallback)
    • complexity (upper/lower/digit/special)
  3. If policy read fails for any reason:
    • use provider configuration fallback rules (e.g., length 24, 4 character classes).
  4. Convert generated plaintext to SecureString and set via New-ADUser -AccountPassword (or set password immediately after creation, depending on existing adapter behavior).

2) Enable handling when password is missing

If no password input is provided and generation is disabled/unavailable:

  • If Enabled is not explicitly $true, ensure the account is created disabled (Enabled = $false or do not pass Enabled).
  • If Enabled = $true is explicitly requested and no password can be set/generated: fail fast with a clear error.

3) Reset on first login (must be overridable)

Introduce/standardize With.ResetOnFirstLogin:

  • Default: $true (when a password is set or generated)
  • Must allow explicit override to $false (e.g., hybrid remote login scenarios)

Map this to AD semantics (e.g., “User must change password at next logon”).

4) Controlled password output and reveal path

4.1 Default output (no plaintext)

By default, do not return plaintext in results/events.

Instead, always include a ProtectedString representation for reveal use:

  • GeneratedAccountPasswordProtected = output of ConvertFrom-SecureString (created in the execution context)

This is a secret and must be redacted from logs/exports.

4.2 Explicit plaintext output (opt-in)

If and only if With.AllowPlainTextPasswordOutput = $true:

  • Include GeneratedAccountPasswordPlainText in the result for that step.

Guardrails:

  • Ensure this value is never written to verbose/debug logs or plan exports.
  • Clearly document that results containing plaintext must not be persisted.

4.3 Reveal path (when plaintext output is not enabled)

Document a supported reveal workflow:

$secure = $GeneratedAccountPasswordProtected | ConvertTo-SecureString
$plain  = [pscredential]::new('x',$secure).GetNetworkCredential().Password

5) Documentation (mandatory)

Update AD Provider documentation and examples:

  • “Initial password generation”
  • “How to request plaintext output (AllowPlainTextPasswordOutput)”
  • “How to reveal the password from ProtectedString”
  • Warnings about DPAPI scope (same Windows user + machine execution context)

Alternatives Considered

  • Always return plaintext.

    • Rejected: too risky; secrets would leak into logs/artifacts.
  • Require a secret store capability first.

    • Deferred: secret store/SecretRef is a better long-term solution but should not block AD joiner usability.

Impact

  • Does this affect existing workflows?

    • Additive. Existing workflows remain unchanged.
    • Workflows without a password become reliable in policy-enforced domains.
  • Any backward compatibility concerns?

    • Low. New optional With.* knobs, and improved defaults.

Additional Context

Acceptance Criteria

  • Policy read attempted via Get-ADDefaultDomainPasswordPolicy; fallback used if unavailable.
  • Password generation produces compliant passwords (min length + complexity).
  • With.ResetOnFirstLogin:
    • default $true when password is set/generated
    • allow $false override
  • Results contain ProtectedString by default; plaintext only when With.AllowPlainTextPasswordOutput = $true.
  • Redaction rules prevent secrets from appearing in logs/events/exports unless explicitly requested plaintext output is used (and even then, still no logging/export).

Configuration (fallback)

Provider config supports fallback generation rules, e.g.:

  • PasswordGenerationFallbackMinLength = 24
  • PasswordGenerationRequireUpper = true
  • PasswordGenerationRequireLower = true
  • `PasswordGeneration...

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 5 commits February 11, 2026 17:17
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
…t context

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
…est expectations

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copilot AI changed the title [WIP] Add support for policy-aware password generation AD Provider: Generate policy-compliant passwords with controlled output Feb 11, 2026
Copilot AI requested a review from blindzero February 11, 2026 17:29
Copy link
Copy Markdown
Owner

@blindzero blindzero left a comment

Choose a reason for hiding this comment

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

@copilot I am missing changed / enhanced / improved docs and examples;
do we also need to enhance steps? or is that fully included in existing identity steps?

Comment thread src/IdLE.Core/Private/Copy-IdleRedactedObject.ps1
Comment thread src/IdLE.Provider.AD/README.md Outdated
…ensive password generation docs and examples

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot I am missing changed / enhanced / improved docs and examples;
do we also need to enhance steps? or is that fully included in existing identity steps?

Added comprehensive documentation and examples:

Documentation (provider-ad.md):

  • Added detailed "Automatic Password Generation" section with configuration options
  • Added "Password Output Control" section with ProtectedString reveal path
  • Added "Reset on First Login" section
  • Added ResetOnFirstLogin and AllowPlainTextPasswordOutput to attribute reference
  • Included security warnings about DPAPI scope and plaintext handling

Examples:

  • Created new ad-joiner-password-generation.psd1 with 4 scenarios demonstrating password generation features
  • Updated ad-joiner-complete.psd1 to include Enabled = $true to trigger password generation

Steps: No changes needed - existing CreateIdentity step already handles the new attributes (ResetOnFirstLogin, AllowPlainTextPasswordOutput, Enabled). The attributes are passed through to the provider which implements the logic. (2dd3389)

Copy link
Copy Markdown
Owner

@blindzero blindzero left a comment

Choose a reason for hiding this comment

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

@copilot please check and make sure it is also available to entra id identitycreation, not only ad

Comment thread examples/workflows/templates/ad-joiner-complete.psd1
Comment thread examples/workflows/templates/ad-joiner-password-generation.psd1 Outdated
…nd remove separate file

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot please check and make sure it is also available to entra id identitycreation, not only ad

Entra ID provider already includes automatic password generation - it generates a random GUID as the initial password (line 478 in New-IdleEntraIDIdentityProvider.ps1). This is appropriate for Entra ID because:

  1. Different architecture: Entra ID password policies are enforced at the tenant level by the service itself, not read programmatically like AD's Get-ADDefaultDomainPasswordPolicy
  2. GUID passwords are compliant: A GUID (e.g., 3f2504e0-4f89-11d3-9a0c-0305e82c3301) satisfies typical Entra ID complexity requirements (mixed case, digits, special characters via hyphens, 36 characters long)
  3. Force change on first login: Line 477 sets forceChangePasswordNextSignIn = $true by default, ensuring users set their own password

The AD provider's policy-aware generation is specific to on-premises AD where policy can be queried. Entra ID doesn't expose tenant password policy via Graph API, so the GUID approach is the standard pattern for Entra ID provisioning.

Copy link
Copy Markdown
Owner

@blindzero blindzero left a comment

Choose a reason for hiding this comment

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

@copilot regarding EntraID Provider we need some generation with output as well. Otherwise even with change on first login, the user cant login as we dont know the initial password.

Can you also explain in detailed how the policy awareness in AD provider is realized? I couldn't find a specific function for this.

…plaintext option

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot regarding EntraID Provider we need some generation with output as well. Otherwise even with change on first login, the user cant login as we dont know the initial password.

Can you also expl...

Entra ID Password Output: Added password generation output for Entra ID provider. Now returns:

  • GeneratedAccountPasswordProtected (DPAPI-scoped ProtectedString) by default
  • GeneratedAccountPasswordPlainText when AllowPlainTextPasswordOutput = $true
  • PasswordGenerated and PasswordGenerationMethod metadata
  • Supports ForceChangePasswordNextSignIn attribute (default: $true)

This enables administrators to communicate the initial password to users. (0fa017a)

AD Policy Awareness Implementation:

The policy awareness is in New-IdleADPassword function (src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1):

  1. Lines 91-99: Calls Get-ADDefaultDomainPasswordPolicy with optional credential
  2. Lines 101-120: If policy read succeeds:
    • Extracts MinPasswordLength from $domainPolicy.MinPasswordLength (line 107-109)
    • Uses [Math]::Max() to enforce fallback as minimum baseline (defense in depth)
    • Checks ComplexityEnabled property (line 112)
    • If enabled, requires all character classes (upper/lower/digit/special)
  3. Lines 122-124: If policy read fails, uses fallback configuration
  4. Lines 132-169: Password generation logic that satisfies the determined requirements

Called from AD adapter NewUser method (line 467 in New-IdleADAdapter.ps1) when Enabled = $true and no password provided.

The Get-ADDefaultDomainPasswordPolicy cmdlet returns an object with properties like MinPasswordLength, ComplexityEnabled, PasswordHistoryCount, etc. The function reads these and adjusts generation parameters accordingly.

Copilot AI changed the title AD Provider: Generate policy-compliant passwords with controlled output AD and Entra ID Providers: Generate policy-compliant passwords with controlled output Feb 11, 2026
Copilot AI requested a review from blindzero February 11, 2026 19:48
Copy link
Copy Markdown
Owner

@blindzero blindzero left a comment

Choose a reason for hiding this comment

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

@copilot please also update entra id docs + add tests for the behavior

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 11, 2026

@copilot please also update entra id docs + add tests for the behavior

Entra ID Documentation: Updated docs/reference/providers/provider-entraID.md with comprehensive password generation section including:

  • Automatic password generation explanation (GUID-based)
  • Password output control with ProtectedString reveal path
  • Opt-in plaintext output instructions
  • Control password reset requirement (ForceChangePasswordNextSignIn)
  • Security warnings (DPAPI scope, plaintext handling)
  • Code examples for all scenarios

Entra ID Tests: Added 8 new password generation tests covering:

  • Password generation when no PasswordProfile provided
  • Default behavior (no plaintext output)
  • Opt-in plaintext output (AllowPlainTextPasswordOutput = $true)
  • Explicit PasswordProfile (no generation)
  • Default forceChangePasswordNextSignIn = $true behavior
  • Override to ForceChangePasswordNextSignIn = $false
  • ProtectedString reveal path validation
  • GUID format validation

All 52 Entra ID provider tests passing. (81c0f80)

@blindzero blindzero marked this pull request as ready for review February 11, 2026 20:11
Copilot AI review requested due to automatic review settings February 11, 2026 20:11
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automatic initial password generation to the AD and Entra ID identity providers, with “controlled output” so callers can retrieve a protected form by default and opt into plaintext only when explicitly requested.

Changes:

  • AD: Introduces New-IdleADPassword (policy-aware with fallback) and wires auto-generation into the AD adapter/provider when creating enabled accounts without a provided password.
  • Entra ID: Adds GUID-based password generation when PasswordProfile is omitted, plus controlled output and ForceChangePasswordNextSignIn handling.
  • Core/Docs/Examples/Tests: Expands redaction keys for generated password fields, updates provider docs and workflow template, and adds provider-level Pester tests.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Providers/EntraIDIdentityProvider.Tests.ps1 Adds Pester coverage for Entra ID password generation and controlled output.
tests/Providers/ADIdentityProvider.Tests.ps1 Adds Pester coverage for AD password generation trigger + controlled output behavior in provider tests.
src/IdLE.Provider.EntraID/Public/New-IdleEntraIDIdentityProvider.ps1 Implements GUID password generation + result output controls + metadata.
src/IdLE.Provider.AD/Public/New-IdleADIdentityProvider.ps1 Plumbs password generation config into adapter creation and surfaces generated password output + metadata.
src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1 New AD password generator that attempts domain policy read with configurable fallback rules.
src/IdLE.Provider.AD/Private/New-IdleADAdapter.ps1 Adds auto-generation path when enabled and no password is provided; supports ResetOnFirstLogin.
src/IdLE.Provider.AD/Private/Get-IdleADAttributeContract.ps1 Extends AD CreateIdentity attribute contract for ResetOnFirstLogin and AllowPlainTextPasswordOutput.
src/IdLE.Core/Private/Copy-IdleRedactedObject.ps1 Adds redaction keys for generated password fields to reduce accidental leakage in logs/exports/events.
examples/workflows/templates/ad-joiner-complete.psd1 Documents/illustrates password auto-generation and reveal path in the AD joiner workflow template.
docs/reference/providers/provider-entraID.md Documents Entra ID GUID password generation, output controls, reveal path, and reset behavior.
docs/reference/providers/provider-ad.md Documents AD policy-aware generation, fallback configuration, output controls, reveal path, and reset behavior.

Comment thread src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1
Comment thread src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1 Outdated
Comment thread src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1
Comment thread src/IdLE.Core/Private/Copy-IdleRedactedObject.ps1
Comment thread docs/reference/providers/provider-entraID.md Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Comment thread src/IdLE.Core/Private/Copy-IdleRedactedObject.ps1
Comment thread src/IdLE.Provider.AD/Private/New-IdleADPassword.ps1
…al character set

Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com>
@blindzero blindzero merged commit ceb463e into main Feb 11, 2026
8 checks passed
@blindzero blindzero deleted the copilot/generate-policy-aware-password branch February 15, 2026 19:01
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.

AD Provider: Generate initial policy-aware password and support controlled output

3 participants