A modular, configuration-driven **PowerShell 7** identity-classification service for **Microsoft Entra ID**.
The engine enumerates Entra user accounts, evaluates each one against an ordered, JSON-defined rule set, deterministically assigns **exactly one persona**, and updates a single approved persona attribute — and only when the calculated value differs from the current value.
| `0` | Successful run; no fatal processing errors |
| `1` | Configuration validation failure |
| `2` | Authentication / authorization failure |
| `3` | User enumeration failure |
| `4` | Fatal required data-provider failure |
| `5` | Reconciliation failure |
| `6` | Unexpected fatal engine error |
A per-user `EvaluationError` does not necessarily terminate the run, but the final status must report the number of affected accounts and may apply a configurable warning/failure threshold.
### 2. `Edit-PersonaEngineConfig.ps1`
Configuration validation, interactive editing, synthetic rule testing, and pipeline enforcement.
| Parameter | Notes |
| --- | --- |
| `-ConfigPath <string>` | Required |
| `-ValidateOnly` | Validate without entering the editor |
Graph acquisition -> normalized identity record -> pure rule engine
-> persona decision result -> comparison -> optional persistence adapter
-> console and structured audit output
```
The **pure rule engine must not depend** on Graph authentication, Azure Automation, or console rendering. It has to be testable offline with synthetic data.
- **Authentication** — Azure Automation uses a **managed identity**. Local development uses an approved interactive or read-only application identity. **No client secret in source control.**
- **Least privilege** — the execution identity gets only what the enabled rules require: in-scope user properties, configured group membership, configured role data, and the existing persona value.
- **Write permissions** — the production identity is granted the minimum permission needed to update the configured target attribute. Whether Entra can enforce write scope at the **individual attribute level must be verified, never assumed** (see OTD-003).
- **Compensating controls**, if the Graph permission proves broader than the single attribute: the persistence module accepts only the approved target attribute; that attribute must appear in `approvedWritableAttributes`; validation rejects all others; a dedicated function builds a request body containing only that attribute; unit and integration tests inspect the request body; code owners and branch policies gate persistence changes; directory audit logs are monitored for unexpected property writes.
- **Data handling** — UPN and Account Object ID are approved for logs. Never log access tokens, authorization headers, secrets, or full Graph responses. Detailed condition values are diagnostic-only, behind `-Debug`.
- **Kill switch** — disable the Automation schedule, run with `-WhatIf`, revoke production write permission, or disable write deployment stages.
---
## Sanitization requirements
**Every** artifact in this repository — docs, examples, tests, configuration samples — must be free of organization names, real domains, tenant or subscription IDs, automation account names, real UPNs or Object IDs, real group/role identifiers, environment-specific attribute names, Log Analytics details, and any secret, token, certificate, or credential.
Synthetic test data must be obviously fictional and must never reproduce real employee records. Tenant-specific configuration belongs in a protected repository or configuration store, not here.
---
## Open technical decisions
Research items to be resolved in `research.md` or an ADR. **OTD-001 through OTD-005 must be closed before persistence implementation.**
| ID | Decision |
| --- | --- |
| OTD-001 | Select the exact Entra persona attribute mechanism — data type, Graph read/update method, discoverability, Conditional Access compatibility |
| OTD-002 | Confirm exact least-privilege Microsoft Graph permissions |
| OTD-003 | Confirm whether write authorization can be restricted to the individual target attribute |
| OTD-004 | Select the Graph access approach — SDK cmdlets, direct REST, or a controlled combination |
| OTD-005 | Select a JSON Schema validation approach compatible with PS7 locally and in Azure Automation |
| OTD-006 | Select structured-log destination and transport |
| OTD-007 | Define retry policy — retryable status codes, max attempts, backoff, jitter, logging |
| OTD-008 | Define full versus incremental processing roadmap |
| OTD-009 | Define production schedule and concurrency lock |
3. Build a requirements traceability list using FR/NFR identifiers.
4. Close OTD-001 through OTD-005 before any persistence work.
5. Create `persona-engine.schema.json` and a placeholder-only `persona-engine.example.json`.
6. Define normalized PowerShell object contracts.
7.**Build the pure rule engine first**, with offline Pester tests, before any Graph integration.
8. Implement configuration validation and non-interactive pipeline mode.
9. Implement Graph read adapters, then console and structured logging.
10. Implement the persistence adapter **last**, with `ShouldProcess` and tests proving zero writes under `-WhatIf`.
Requirements: **PowerShell 7**. Offline unit testing must not require tenant connectivity.
---
## Delivery
- Hosted in **Azure DevOps Git** — feature branches, pull requests, protected release branch, code owners on persistence, security configuration, and production rules.
- **Validation pipeline** — repository hygiene checks, PowerShell static analysis, JSON Schema validation, semantic/safety configuration validation, Pester unit tests, Pester safety tests, test result publication, artifact packaging.
- **Release pipeline** — validate approved branch/tag, repeat validation and tests, package, deploy to Azure Automation, import modules, publish runbook, **keep the schedule disabled**, execute `-WhatIf` validation, approval gate, then enable enforcement.
- Changes to the target attribute, `approvedWritableAttributes`, rule priority, rule enablement, rule conditions, persona outputs, authentication permissions, persistence functions, logging destination, or `WhatIf`/`ShouldProcess` behaviour **all require review**.
---
## Definition of done (v1)
Version 1 is complete when both PowerShell scripts are implemented; the JSON Schema exists and is documented; validation covers syntax, schema, semantic, and safety layers; ordered first-match evaluation and nested `All`/`Any` work within the configured depth; the initial property and membership operators are tested; null behaviour matches the approved decision; required group-lookup failures produce `EvaluationError` and preserve the existing persona; `Unclassified` users are reported distinctly; each user result displays immediately; interim and final summaries work — including interval `0` — and reconciliation checks pass; logs include UPN and Account Object ID; **`-WhatIf` produces zero Graph writes**; only changed valid values are written in enforcement mode; the write payload contains only the approved target attribute; local offline Pester, read-only production-tenant, Azure Automation PowerShell 7, and Azure DevOps pipeline runs all pass; security review confirms permissions and compensating controls; operational documentation, kill switch, and rollback procedure are complete; and `-WhatIf` impact evidence is reviewed before enforcement is enabled.