nordlo.windows.gporeport 0.1.0

Creates read-only ADMX/GPO cross-reference reports for a single Active Directory domain as a self-contained SQLite snapshot.

No packages depend on nordlo.windows.gporeport.

First release. Everything below is the initial feature set rather than a change from a prior version; subsequent sections will describe deltas.

Added

  • Unified Settings tab in the HTML report, replacing the three separate settings tabs (All settings / Settings by GPO / Other areas): a grain toggle (Values | Settings) plus a checkbox "Include settings not configured in any GPO", backed by three .sql files (all-settings.sql, reused unchanged, plus new settings-per-gpo.sql and settings-all-known.sql). The checked state additionally lists ADMX policies and Security Options catalog entries nothing configures anywhere in the scan, with the catalog-boundary caveat stated in settings-all-known.sql's header: only Administrative Templates and Security Options have a full catalog to check absence against.
  • ADMX files hub tab, replacing the admx-to-gpo / gpo-to-admx tabs: one row per template (admx-files.sql), expanding in place to show referencing GPOs, configured policies and <using> dependencies in both directions.
  • Findings tab grouping the five finding views (Conflicts, Unreferenced ADMX, Unlinked GPOs, Orphaned settings, Links with no effect) as chips under one landing statement that findings are supporting evidence, never a removal list or recommendation. Each chip keeps its own query, prose, badge and row behaviour unchanged.
  • Overview stat cards are now clickable, navigating to the tab (and, where one exists, Findings chip) each number is about.
  • Report renamed from "ADMX Reference Report" to "GPO Report" everywhere it is user-visible: the masthead <h1>, the browser tab title (both the initial <title> Export-NordloGpoReportToHtml writes and the one renderMasthead sets once the snapshot has loaded), and the -Title parameter's default. No module function, file or CLI surface renamed — display text only.
  • Masthead layout: the domain/scan-date/DC subtext now sits inline with the title (baseline-aligned, wrapping onto its own line only on a narrow viewport) instead of stacked beneath it, so the header claims less vertical room.
  • New bundled reports for the CLI: settings-per-gpo, settings-all-known, admx-files (see Get-NordloGpoReportView -Name). all-settings, gpo-settings, other-settings, admx-to-gpo and gpo-to-admx remain available under their existing names for scripts and old bookmarks (#all-settings etc. redirect into the new tabs in the HTML report).

Changed

  • HTML report tab bar reduced from eleven tabs to six: Overview | GPOs | Settings | ADMX files | Findings | SQL. The three settings tabs folded into Settings' two grains; admx-to-gpo / gpo-to-admx folded into ADMX files; the five finding tabs folded into Findings. Every folded view's query is unchanged and still runs identically via Get-NordloGpoReportView.
  • Full ADMX catalog in every snapshot. admx_policies, admx_policy_elements and admx_enum_items previously described only policies some GPO configures (a size trade-off: ~600 configured policies vs. a reference store's 7,786). They now describe every policy defined by every ADMX in the Central Store, unconditionally — Invoke-NordloGpoReportScan no longer passes ConfiguredPolicyId to Resolve-AdmxDisplayName, which still accepts the parameter for callers that want a restricted catalogue. A row in admx_policies no longer implies the policy is configured anywhere — join against gpo_settings to find out. Existing consumers (all-settings.sql, conflicting-settings.sql, gpo-settings.sql, the JS detail-modal queries in 35-detail.js/ 30-views.js) all join from the configured side (gpo_settings/gpo_setting_values outward via LEFT JOIN), so none of them read row-existence in admx_policies as "configured" and none needed changes. No test asserted admx_policies row counts equal configured counts. Existing snapshots need a fresh scan to pick up the full catalogue.

Added

  • Invoke-NordloGpoReportScan — read-only scan of a single Active Directory domain producing a self-contained SQLite snapshot of which ADMX files in the Central Store are referenced by which GPOs. Never modifies AD, SYSVOL, or any GPO.

  • Get-NordloGpoReportView — runs one of six bundled SQL reports (unreferenced-admx, admx-to-gpo, gpo-to-admx, unlinked-gpos, orphaned-settings, dead-links) against a snapshot, or arbitrary SQL via -Query.

  • Attribution from registry.pol matched against an ADMX registry-target index, with exact key|valueName matching and a key-only fallback restricted to <list>-defined keys. Unmatched values are recorded as orphans rather than guessed at.

  • Setting counts at ADMX-policy level (setting_count) alongside raw value counts (value_count), so numbers agree with GPMC. Delete/disable markers count as references and are flagged via disabled_policy_count / has_delete_markers.

  • Central Store hygiene findings: inter-ADMX <using> dependency graph, per-culture ADML coverage gaps, and registry-target collisions between templates (all claimants retained).

  • GPO context from AD directly — links with order/enabled/enforced, per-hive enabled state, WMI filter definitions (raw WQL, never evaluated), and object counts under each distinct link target.

  • AD-vs-SYSVOL consistency check comparing versionNumber against GPT.INI.

  • Unreadable GPOs are recorded with is_accessible = 0 and mark the whole scan's visibility as incomplete, so an unreferenced-ADMX result cannot be misread as complete.

  • tests/Parsing.Tests.ps1 — ADMX/PReg parsing fixtures covering all four observed ADMX root namespaces, the <using>-vs-<target> attribution trap, marker handling, collision detection, and the guarded key-only fallback.

  • Export-NordloGpoReportToHtml — renders a snapshot into one self-contained HTML file containing the whole SQLite database, a SQLite engine compiled to WebAssembly (vendored sql.js, MIT), and a browser interface over all six reports. Opens from file:// with zero network requests and needs no PowerShell, PSSQLite or RSAT to read. Sortable and filterable grids, CSV export of the filtered view, an ad-hoc SQL console, and a button that hands back the embedded .sqlite.

  • -Html switch on Invoke-NordloGpoReportScan, which renders the report it just wrote and returns both paths.

  • Each HTML tab's explanatory text is extracted from the leading comment block of the corresponding bundled .sql file, and the browser executes that same SQL, so the caveats and the query cannot drift from what Get-NordloGpoReportView runs.

  • .gitattributes, marking *.wasm and *.sqlite binary.

  • Configured settings detail. The scan now stores every configured registry value (gpo_setting_values, decoded per registry type) and one row per configured policy (gpo_settings) with a derived Enabled/Disabled state and a state_basis column stating, in words, how the verdict was reached — some verdicts are inferences and the basis makes each row checkable. Display names, category paths, element labels and explain text are resolved from the ADML files into admx_policies / admx_policy_elements / admx_enum_items, restricted to configured policies to keep the snapshot small.

  • Three new reports in both Get-NordloGpoReportView and the HTML: all-settings (search every configured value), gpo-settings (what does this GPO actually do, with state and reasoning), and conflicting-settings (the same value set by several GPOs, values side by side, explicitly not RSOP).

  • HTML report: rows in the settings views open a detail modal showing every registry value the policy wrote, the state with its basis, where else the policy is configured, and the template's own explain text.

  • GPOs tab with rows that expand in place to show where the GPO is linked (with subtree object counts, NULL rendered as "unknown" rather than 0) and every setting it configures; those settings rows open the policy modal. dead-links and unlinked-gpos rows drill through to it.

  • Editor-to-registry mapping in the policy modal: each editor field with the registry value it writes, its allowed range or enum options, and the default the editor pre-fills — plus the policy's own Enabled/Disabled sentinel values, marked when the ADMX declared neither and the schema default was assumed. Written enum values are annotated with their meaning (2 (High blocking level)). Editor defaults come from the ADML presentation and are stored in admx_policy_elements.default_value; admx_policies gains enabled_value, disabled_value and state_values_implied.

  • Conflicts modal listing every GPO writing a contested registry value side by side, with the value each writes, its state, hive status and active link count.

  • The GPOs tab filter searches inside each GPO — every registry key, value name and written value, every setting name, category and template, and every link path — so "print" or "lsass" finds the GPOs that touch those things, not just ones whose name matches. Built from one grouped query, cached for the session (~480 KB, sub-millisecond filtering for a 158-GPO domain), so it is unconditional rather than hidden behind a toggle. The row count says when matches came from content not visible in the row.

  • Everything outside Administrative Templates is now collected, always — security settings, Preferences, scripts, Windows Firewall, AppLocker, wireless/wired policies, certificate services. 4,522 settings across the reference domain. Read from Get-GPOReport -ReportType Xml, which is unusable for ADMX attribution (no namespace or policy id) but is the best source for every other area and resolves display names that exist nowhere in SYSVOL. New tables gpo_other_settings, gpo_other_setting_props, gpo_security_filtering, gpo_policy_areas; new other-settings report; new "Other areas" tab with a detail modal.

  • GroupPolicy (RSAT/GPMC) is now required. It is checked by importing it rather than by Get-Module -ListAvailable, which does not list it under PowerShell 7 even when installed.

  • A GPO configuring only non-ADMX areas no longer reads as empty: gpos carries other_areas and other_setting_count, the GPOs tab tints a zero ADMX count when other areas exist, and dead-links splits the old no settings configured into no settings at all (9 GPOs here) and no ADMX settings (has other policy areas) (34).

  • Item-level targeting is recorded per item as has_filters and surfaced prominently — recorded, never evaluated.

  • Security filtering (who a GPO applies to) captured from the same report.

  • Search matches are highlighted wherever they appear, including inside an expanded GPO's settings, links and other-areas lists.

  • Typed parsing for the Security extension (ConvertFrom-GpoSecuritySetting.ps1): SecurityOptions, Account, UserRightsAssignment, RestrictedGroups, SystemServices, Audit and EventLog records each decode to exactly one settings row, replacing the generic XML walker for this extension. Every DisplayFields checkbox state (e.g. Kerberos encryption types allowed) is now preserved as its own true/false property instead of being dropped.

  • gpo_other_settings.recognized — 1 when a row came from a shape-aware handler (today, Security only), 0 when it came from the generic walker. Flagged in the "Other areas" grid, the GPO expansion table and the setting detail modal (a caveat banner, worded as a statement about this tool's coverage rather than about the GPO); aggregated as an Overview card only when the count is non-zero.

  • gpo_other_settings.source_xml — the setting's own report XML fragment, verbatim, stored for every other-areas row (typed and generic alike) and shown in a collapsed "Source XML" disclosure in the detail modal, always present regardless of recognized.

  • Unrecognised security element shapes (a future Windows security area, or a SecurityOptions Display variant this tool does not decode) fall back to the generic walker and raise a scan warning rather than mis-rendering silently.

  • Fixture and regression tests in tests/OtherSettings.Tests.ps1 for every typed Security shape, the two real-domain SecurityOptions fixtures this normalizer was built against (one collapses 3 rows to 1, the other 9 rows to 3), and the unrecognised-shape fallback.

Changed

  • The page no longer caps at 1600px and grid cells have room (mono 44ch, text 56ch); modals widen to 1400px and no longer truncate inside. Widening the browser now actually shows more data.
  • Report prose is collapsed behind an "About this report" summary. The scan-visibility banner stays outside it — a caveat that gates interpretation must not be collapsible.
  • Console result cells truncate with a tooltip like the main grids, so one MULTI_SZ row cannot blow out the layout.
  • The "Source XML" disclosure in the other-setting detail modal is now pretty-printed and syntax-coloured in the browser (indentation, .xml-tag/.xml-attr/.xml-val/.xml-text/ .xml-comment), instead of rendering OuterXml as one long unindented line. The stored source_xml value itself is unchanged — still the byte-faithful "as reported" artifact — only its rendering was reformatted.

Fixed

  • SecurityOptions records (e.g. "Network security: Configure encryption types allowed for Kerberos") fragmented into up to three settings rows — the real setting plus two phantom rows named KeyName and SettingNumber from the record's own leaf elements — inflating other-areas counts roughly threefold for any GPO configuring security settings. Fixed by the typed Security normalizer above.

Notes

  • gpo_other_settings gained two columns (recognized, source_xml). Snapshot databases from a scan run before this change do not have them; Get-NordloGpoReportView and the HTML report both assume the current schema, so old .sqlite files must be regenerated by a fresh Invoke-NordloGpoReportScan run rather than reused. This is consistent with the snapshot-per-file model — the database is a point-in-time export, not a store this tool migrates in place.

  • ADML streaming reader skipped every other element (ReadElementContentAsString already advances, and the loop advanced again) — 300 of 615 configured policies silently lost their display names. A regression test pins all-consecutive-strings parsing.

  • Policies whose element shares the policy's own valueName (TPM lockout, Defender enum policies) were reported Undetermined; the key index now merges roles instead of dropping the second registration.

  • Policies writing the same registry value from both enabledList and disabledList (fDenyTSConnections) are now decided by comparing the written data against each list item's declared value — presence-based reasoning got them backwards half the time.

  • Two state-derivation errors, found by cross-checking every derived state against GPMC's own reported state (agreement went from 99.6% to 99.93%, 1441 of 1442):

    • A policy that expresses Disabled by deleting its element values (**del.DCSettingIndex, no policy value of its own) was read as Enabled, because delete markers were only checked on the policy's own value and then counted as "element values present". A marker is evidence of the opposite of presence.
    • An implied enabled/disabled sentinel (the schema's 1/0 default, used when the ADMX declares neither) outranked real element evidence, so an enum choice of 0 read as Disabled. Declared sentinels still win; implied ones are now consulted only after element evidence.
  • A stray U+0001 control character inside a string literal in 20-grid.js. Harmless at runtime and invisible in every editor and diff, which is exactly why a test now rejects control characters in any bundled asset.

  • The GPOs tab's "not actually empty" flag never fired: the columns it reads to decide were looked up before the grid switched to the new view's columns, so on the normal path (arriving from Overview) the lookup always ran against an empty column list.

  • Dead-links tooltip text was keyed to no settings configured, a string the SQL never emits (it emits no settings at all); the tooltip for that reason, and for no ADMX settings (has other policy areas), never appeared. Both keys are fixed, and a test now checks every non-conclusive reason the SQL can produce has a matching tooltip key.

  • The SQL console's prose named "the eleven tables" by hand; the schema has since grown to twenty and the sentence had not been updated. The table list is now rendered at boot from sqlite_master.

  • highlightMatch matched the search term against already-HTML-escaped text, so a needle like amp, lt or quot could land inside an entity produced by escaping a raw &, < or " in the value, corrupting its display. Matching now runs on the raw text before escaping.

  • The ADMX setting detail modal did not show security filtering ("This GPO applies to"), unlike the other-areas modal — a policy configured in a GPO nobody is filtered to looked identical to one that actually applies. Both modals now share the same section.

  • The conflict-detail policy lookup filtered on registry key and value name but not hive, so a Computer-side and User-side conflict sharing those names could name the wrong policy in the modal.

  • CSV export wrote raw column names as headers and left every value un-formatted (booleans as 1/0, timestamps as ISO strings); it now exports the same labels and yes/no/formatted-date values the grid shows, while still writing numeric columns raw rather than with locale thousands separators.

Added

  • dead-links reason applies to no one: a GPO whose security filtering grants Apply to no principal, checked only for GPOs the scan could actually read. Group nesting and membership are not evaluated.
  • dead-links reason empty subtree for configured hive: a GPO that configures only one hive (via ADMX or any other policy area) whose subtree has zero objects right now, even when the other hive's count is nonzero or unknown. Not conclusive, same caveat as empty subtree.
  • conflicting-settings gains live_writers: how many of the writing GPOs currently have at least one enabled link and the relevant hive switched on. Not precedence — it separates writers that could currently apply anywhere from ones that could not.
  • all-settings includes state_basis alongside state.
  • WMI filter presence (wmi_filter_name) is now flagged wherever it appears — recorded, never evaluated, same treatment as item-level targeting.
  • unlinked-gpos' header notes that an old modified_time on an unlinked, configured GPO is the strongest staleness signal this data can offer.
  • Every grid row is now clickable: views with none of the richer row behaviours (orphaned-settings, admx-to-gpo, gpo-to-admx, unreferenced-admx) fall through to a generic "every column, full value" modal, so a long mono value is reachable on touch and keyboard, not only via a hover tooltip.
  • The detail modal traps Tab/Shift+Tab within its panel while open.
  • The tab strip is a real keyboard tablist: roving tabindex, and ArrowLeft/ArrowRight/Home/End move focus and select.
  • URL hash deep links: #<viewId> on tab selection, #gpos/<guid> when a GPO row is expanded from the GPOs tab. Restored on load and on browser back/forward; filter text and sort order are not serialised, and an unrecognised hash falls back to Overview silently.

Changed

  • Header cells in the detail modal's tables and a GPO row's inline expansion tables are now plain text instead of <button disabled> — those tables never sorted, and the disabled button put an inert, unreachable-by-purpose control in the tab order and screen reader flow for no benefit.

Notes

  • ADMX XML parsing is namespace-agnostic (local-name()): 36 of 257 templates in a real store use a non-canonical root namespace or none at all, and a namespace-bound parser silently drops them.
  • The HTML writer streams the document through a StreamWriter, base64-encoding payloads in chunks of 196 608 bytes (a multiple of three, so padding only ever lands at the end). The obvious alternative — ReadAllBytes, ToBase64String, substitute into a template — peaks at roughly 8-12x the database size in large-object-heap strings. A round-trip test pins the chunk arithmetic.
  • sql.js is initialised with wasmBinary, never locateFile: the latter resolves a URL and goes through fetch/XHR, which a file:// page is not permitted to do, so it cannot work standalone.
  • All collections are List[psobject], never List[object]: @($list) throws "Argument types do not match" from PowerShell's own enumerable binder for that element type, on both 5.1 and 7. Two tests enforce this.

Added

  • Typed parsing for nine Group Policy Preferences areas (ConvertFrom-GpoPreferenceSetting.ps1): Drive Maps, Shortcuts, Folders, Files, Ini Files, Windows Registry, Environment and Local Users and Groups get full treatment (headline summary plus every attribute as a property); Scheduled Tasks gets a deliberately headline-only extraction (command line, runAs/RunLevel, task variant, Enabled, and trigger TYPE names only) — modelling the full Task schema (repetition, boundaries, idle settings, the 15+ Settings booleans) is out of scope this round by explicit agreement; the detail modal for a Scheduled Tasks row carries a Note prop pointing at Source XML and the Group Policy editor as the authoritative view for everything past the headline.
  • gpo_other_settings.targeting_text — item-level targeting (<Filters>) spelled out in words, e.g. member of CROSS\Domain Users OR member of CROSS\it-res-ou-Cross external users manager. Built by walking FilterGroup ("member of X"), generic Filter* types ("Type: attr=value, ..."), and FilterCollection (recursed and parenthesized), chaining clauses with each filter's own bool attribute and honouring not="1". Shown under the existing "item has item-level targeting" banner in the detail modal. Still recorded, never evaluated — this makes the recorded condition readable, not resolved.
  • gpo_other_settings.item_disabled — 1 when a GPP item's own disabled attribute is 1. Previously disabled sat on the generic walker's noise-attribute skip list with nothing reading it, so a disabled preference item rendered identically to an active one. Surfaced as a Disabled prop, a (item disabled) summary suffix, a warn-flagged grid column, and a new banner in the detail modal. disabled stays on the noise list for prop emission — the column is the intended signal now, not a bare attribute dump.
  • tools/Update-SecurityOptionCatalog.ps1, a maintainer-run script that regenerates source/files/security-option-catalog.json from HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SeCEdit\Reg Values, resolving @wsecedit.dll/@scecli.dll display strings via LoadLibraryExW+LoadStringW. 101 Security Options on the generating build, with value types, display types, units and full choice/flag label lists (the metadata the security-settings editor itself renders from). Deliberately not part of the scan — the data is OS-shipped and stable, so the catalog is a bundled artifact refreshed by a maintainer after Windows feature updates; output is sorted so regenerations diff cleanly. Explain text is not included: an exhaustive dump of both DLLs' string tables found no mechanical mapping from display-name resource IDs to explain-text paragraphs, and shipping a fuzzy match was ruled out. Validated by tests/SecurityCatalog.Tests.ps1.
  • Real-domain fixtures in tests/OtherSettings.Tests.ps1 (trimmed of GPO-level SecurityDescriptor boilerplate, byte-faithful ExtensionData) for Scheduled Tasks (one ImmediateTaskV2 running ConfigMgrClientHealth.ps1), a mixed Preferences GPO (Folders/Ini Files/Shortcuts sharing one item-level-targeting filter chain), and GPP Registry alongside NRPT and the literal Administrative-Templates Registry extension (the regression fixture for the skip below). Plus synthetic fixtures for Drive Maps, Files, Environment, the Local Users and Groups <User> variant, and a non-immediate TaskV2 with CalendarTrigger/LogonTrigger to prove trigger-type listing and the legacy/modern variant distinction.
  • The bundled Security Options catalog now travels INTO the snapshot at scan time instead of staying a build-time-only asset: Get-SecurityOptionCatalog.ps1 reads security-option-catalog.json (the same $PSScriptRoot/files\ resolution Get-GpoReportHtmlAsset.ps1 uses) and its rows are written to two new tables, security_option_catalog and security_option_choices, plus three new scan_info provenance columns (catalog_generated_on, catalog_os_build, catalog_ui_culture). A snapshot now renders its security options identically forever, independent of which module version later opens it. A missing or unparsable catalog records a scan warning and leaves both tables empty rather than failing the scan.
  • Security option detail modal gained an "Editor metadata" section (securityOptionMetadataSection in 35-detail.js) for Security-area rows with a KeyName prop: the catalog's display name (when it differs from what is already shown), type plus units, the full choice list for choice-type options with the configured value marked, and a decoded flag table for flags-type options (one row per flag, set/not-set against the configured DWORD). When the report XML's own decoded value and the catalog's choice label disagree — possible when the domain was scanned against a different OS generation than the catalog's build — both are shown side by side rather than one being silently preferred. No catalog row (including when the catalog was missing at scan time) renders the modal exactly as before: no empty section, no fabricated placeholder.
  • Overview facts gained a "Security option catalog" line (securityOptionCatalogFact in 10-database.js), e.g. "101 options, build 26100 (en-US)" — omitted entirely when the snapshot carries no catalog rows.
  • tests/CatalogIntegration.Tests.ps1Get-SecurityOptionCatalog against the real bundled JSON (row count, the Kerberos SupportedEncryptionTypes six-choice ordering, the NoLMHash null-display-name case) and against a missing/unparsable file (warning recorded, empty tables, no throw); the new schema (both tables plus the scan_info provenance columns); and Write-GpoReportData actually populating both tables from a loaded catalog.

Changed

  • GPP summaries now lead with the operative fact instead of "first three attributes in document order": a Shortcut's summary previously showed targetType/shortcutKey/iconIndex while omitting both paths; it now reads -> \\cts03\cross at %APPDATA%\...\target. Folders, Ini Files, Files, Drive Maps, Windows Registry, Environment and Local Users and Groups summaries were rewritten the same way.

Notes

  • The previously-recorded risk that the ADMX-only Registry skip in ConvertFrom-GpoReportXml might also be swallowing GPP Registry items has been verified against real report XML and does not apply: Administrative Templates reports as <Name>Registry</Name> in the .../Settings/Registry namespace, while GPP Registry preferences report as <Name>Windows Registry</Name> in .../Settings/Windows/Registry — different names, so only the former is skipped. The code comment recording this was updated in the prior round; this round adds a regression fixture (gpo-gpp-registry.xml) pinning both extensions coexisting in one GPO.
  • gpo_other_settings gained two more columns (targeting_text, item_disabled), on top of recognized/source_xml from the previous round. As before, snapshot databases from an earlier scan do not have them; regenerate with a fresh Invoke-NordloGpoReportScan run rather than reusing an old .sqlite file.
  • Snapshots need to be regenerated to pick up security_option_catalog / security_option_choices and the scan_info catalog columns; older .sqlite files simply render security options without the editor-metadata section — the same graceful-fallback path a catalog-load failure takes, not an error.
  • The catalog's own display_type (boolean/number/string/choice/stringlist/flags) and value_type (REG_DWORD/REG_SZ/...) are already readable strings in security-option-catalog.json, not the numeric 0–5 / enum codes an earlier planning pass assumed — security_option_catalog.display_type and .value_type store them as-is; display_type_name only re-spaces stringlist to string list for display.

Fixed

  • A SecurityOptions record with no <Display> block (hand-imported baseline templates write options the reporting machine's editor has no metadata for) threw one non-terminating "InvokeMethodOnNull" error per record during real scans. The local-name lookup helpers are now null-tolerant, and a Display-less record is treated as a typed row (its KeyName and setting value are fully understood; only display decoration is absent) rather than falling to the generic walker with an unrecognised-shape banner. Regression-tested against a Display-less fixture.
  • Restricted Groups reported "0 member(s)" for every group. Real Get-GPOReport output puts and directly under the RestrictedGroups record; the handler was matching a wrapper invented by an early synthetic fixture. Members and member-of entries are now read from the real shape (either Memberof capitalisation), an unresolvable principal falls back to its SID, and the fixture was corrected to the verified real-world form.
  • The grid filter could not be typed into: the hash-routing loop guard was timing-based (setTimeout(0)), and the browser does not guarantee that timer fires after the queued hashchange task. When the view's query was slow enough the guard was already clear when hashchange arrived, and the handler re-ran the entire view load, wiping the filter input mid-typing. The guard now compares the hash VALUE this code itself wrote, which cannot lose a race.
  • Searching for a principal that a Restricted Groups setting names as a member returned nothing. The GPOs tab's deep-search index covered non-ADMX settings only through area/item/summary, and those summaries deliberately truncate -- a restricted group's reads "3 member(s)" while the member names live only in gpo_other_setting_props. The index now folds in every property of every non-ADMX setting (plus targeting_text), and the Settings-grain and Other-areas grids gained an equivalent per-setting property index, so a search for a member name, a task command line or a mapped drive path finds the row that carries it. The row-count line now names which hidden content matched rather than always claiming "inside each GPO's settings and links".
  • Publishing a tag failed on a clean CI runner: Publish-PSResource runs Test-ModuleManifest, which resolves every RequiredModules entry against PSModulePath and calls the entry invalid when it cannot find it. PSSQLite is the module's runtime dependency but was never installed by build.ps1, so the publish job failed while every other task passed -- on a developer machine PSSQLite is already present and the publish path is never exercised. It is now installed with the other prerequisites.
  • Publishing then failed a second check: Publish-PSResource also refuses to publish a module whose RequiredModules are absent from the TARGET repository, and PSSQLite is not mirrored to the feed this module publishes to. The publish call now passes -SkipDependenciesCheck. The manifest still declares the dependency; PSSQLite is installed from the PowerShell Gallery, which the README now states explicitly.
  • PSSQLite is now mirrored to the publishing feed, so Publish-PSResource's dependency check passes on its own merits and the -SkipDependenciesCheck workaround has been removed. Installing needs no special switches: PSResourceGet resolves RequiredModules against the feed being installed from, and finds it there. Verified under Windows PowerShell 5.1, which is where the missing dependency failed before.

Any 0.0

Version Downloads Last updated
0.2.0-rc.1 3 08/18/2026
0.1.0 3 07/31/2026
0.1.0-rc.3 18 07/31/2026