nordlo.partille.cblifecycle 0.3.0-rc.13

Handles creation, updates and deletion of computer objects representing chromebooks in Active Directory

No packages depend on nordlo.partille.cblifecycle.

Fixed

  • Invoke-PKChromebookLifecycle SPN add on existing accounts no longer fails with Cannot validate argument on parameter 'ServicePrincipalNames'. Values in the argument collection should be of Type: 'System.String'. Set-ADComputer -ServicePrincipalNames @{ Add = ... } validates each Add value as a single String, not a string array (the singular in the error message is literal). Now loops one SPN per call rather than passing an array. Replaces the failed List[string].ToArray() attempt in rc.12.
  • Invoke-PKChromebookLifecycle no longer throws on "unknown root field" when a -DescriptionTemplate, -InfoTemplate or -AttributeMap key references a Google field that happens to be unset on every fetched device. Google's Directory API omits null fields from the response, so a legitimate mapping (e.g. annotatedLocation = 'extensionAttribute3') hard-failed the run whenever no device had that field populated. The strict check is replaced by the existing always-empty soft warning (event 1003), which catches both genuine typos and sparse-field cases without blocking the run. Operators reading the rolling log will see one warning per always-empty path.

Added

  • Get-GoogleChromeDevice -Limit <int> parameter -- caps the number of devices fetched. Pagination terminates as soon as the cap is reached, and the last page's maxResults is scaled down to the remaining count so the API does not return more rows than needed. Default 0 = no limit (existing behaviour). Intended for test / dry-run iterations against large domains.
  • Get-GoogleChromeDevice -OrgUnitPath <string> parameter -- restricts the fetch to a specific OU and its sub-OUs via Google's server-side orgUnitPath filter (URL-encoded for OU names with spaces or non-ASCII characters). Combines with -Limit for quick samples of a specific subtree (e.g. -OrgUnitPath '/_Chromeenheter_elever' -Limit 5). Default '' = no filtering.
  • Invoke-PKChromebookLifecycle -InfoTemplate parameter. Writes a templated value to the AD info attribute (the "Notes" field on a computer object). Same path/transform syntax as -DescriptionTemplate; multi-line via PowerShell here-strings. When non-empty the attribute is fully managed -- manual edits in AD UC are overwritten on the next run; when empty (default) the attribute is not fetched, diffed or written. Run-ChromebookProvisioning.ps1 -InfoTemplate forwards to the lifecycle. Setting 'info' as a value in -AttributeMap while -InfoTemplate is also set throws at startup (conflict).
  • Invoke-PKChromebookLifecycle -AttributeMap keys now accept the full path syntax used by -DescriptionTemplate, e.g. 'recentUsers[0].email' or 'autoUpdateExpiration:date'. Existing flat keys (annotatedUser, orgUnitPath, ...) remain unchanged. AttributeMap keys are validated the same way as templates: unknown root segments throw at startup, and paths that resolve to empty on every device log event 1003 (WARNING).
  • Template transform suffix :date -- formats a millisecond Unix-epoch field (e.g. autoUpdateExpiration) as YYYY-MM-DD (UTC). Works in -DescriptionTemplate, -InfoTemplate, and -AttributeMap keys. Unknown transform names throw at startup with the list of known transforms.
  • Invoke-PKChromebookLifecycle -DescriptionTemplate parameter. Builds the AD Description from an operator-supplied template with PowerShell-style path syntax: {name}, {name.sub}, {name[0]}, {name[0].sub}. Derived names {school} (last segment of orgUnitPath), {class} (annotatedLocation), and {student} (annotatedUser) take precedence over same-named raw fields. Default '{school} - {class} - {student}' preserves the historical hardcoded format. Empty result falls back to 'Chromebook - RADIUS/NPS'. Validation throws at startup on unknown root segments (typo guard against the loaded JSON's top-level keys) and logs event 1003 (WARNING) when a path resolves to empty on every device. Run-ChromebookProvisioning.ps1 -DescriptionTemplate forwards to the lifecycle.
  • examples/Run-ChromebookProvisioning.ps1 orchestration wrapper that runs Invoke-PKChromebookPreprovision and Invoke-PKChromebookLifecycle in series, intended for a 30-minute Task Scheduler cadence. State-file alerting fires mail only on OK ↔ ERROR transitions (no duplicate alerts when an error persists across many ticks), a named mutex plus the Task Scheduler "no concurrent instances" setting prevent overlap, and a retention pass prunes processed/failed artifacts older than -RetentionDays while the rolling log is rotated when it crosses -LogRotateSizeMB. Aggregates both functions' structured results into a single exit code.

Changed

  • Google → lifecycle data exchange switched from CSV to JSON. Get-GoogleChromeDevice -OutputCsvPath is removed in favour of -OutputJsonPath, which writes the full Chrome-device tree via ConvertTo-Json -Depth 100. Nested fields like recentUsers and lastKnownNetwork are preserved (CSV flattens them to unusable strings). Invoke-PKChromebookLifecycle -CsvPath renamed to -DataPath; reads JSON via ConvertFrom-Json. Wrapper default file changed from chromebooks-limited.csv to chromebooks-limited.json. Breaking change for external callers of -OutputCsvPath -- switch to -OutputJsonPath. No alias.
  • examples/Run-ChromebookProvisioning.ps1 now performs the Google Workspace device fetch as its first step via Get-GoogleChromeDevice, writing the result to -DataPath. New parameters: -ServiceAccountJsonPath, -ImpersonateEmail, -CustomerId, -GoogleFields. If the fetch fails or credentials are missing, the lifecycle pass is skipped (preprovision still runs because it does not depend on Google data) and the run ends with ERROR, which feeds into the existing OK↔ERROR mail transition -- no separate FETCH_FAILED mail kind. Under -WhatIf the fetch is skipped (it writes a file) but the rest of the wrapper runs dry as before. The summary text now starts with a Google fetch block and ERROR DETAILS: distinguishes GF: (Google fetch), PP: (preprovision) and LC: (lifecycle) so the operator can tell at a glance which step failed.
  • Wrapper defaults now anchor every path under C:\CB-LifeCycle\ (logs\, googledevices_in\, preprovision\, credentials\, plus state/lock files at the root), and the writable parent directories are auto-created at startup. README adds a "Standard-mapplayout" subsection plus a copy-pasteable mkdir recipe.
  • Wrapper detects abnormal termination of the previous run (Task Scheduler force-kill, crash, BSOD, power loss) by checking for a leftover lock file at startup and fires a dedicated ABNORMAL_END mail with the previous run's PID, start time, user and host. Independent of the OK/ERROR state machine so each kill event surfaces exactly once.
  • README section "Schemaläggning" covering the Task Scheduler recipe (including the 25-minute "stop the task if it runs longer than" setting that bounds hung runs), service-account permissions, the OK↔ERROR alerting table plus the ABNORMAL_END notification, single-instance lock semantics, and retention/rotation tuning knobs.
  • New exported function Invoke-PKChromebookPreprovision for pre-creating AD computer accounts from a watched folder of serial-number lists (*.txt, *.lst, *.csv with serial/serialNumber column). Designed for school-start onboarding so devices can obtain a NPS/RADIUS certificate immediately after MDM enrollment, without waiting for the Google Admin Console export. Source files are moved to processed\ (or failed\) and accompanied by a per-file .log (one status line per serial) and -summary.txt (totals and error excerpts). Returns a structured result object with FilesProcessed, FilesFailed, SerialsRead, SerialsCreated, SerialsSkipped, GroupAddErrors, Errors, ErrorMessages, AdCountBefore/After.
  • -DescriptionMarkerPrefix parameter (default PREPROVISIONED) on both Invoke-PKChromebookPreprovision and Invoke-PKChromebookLifecycle. Pre-provisioned accounts get Description = "<prefix> <date> [<source-file>]". The lifecycle deletion pass excludes any account whose Description starts with the prefix, and logs a single summary line (event 1204) listing how many were protected.
  • "Claim" event on Invoke-PKChromebookLifecycle: when an existing account whose Description starts with -DescriptionMarkerPrefix is matched against an ACTIVE CSV row, event 1142 is logged. The lifecycle's existing Description-diff path then overwrites the marker with the normal School - Class - Student value, self-clearing the protection.
  • Event IDs 1150 / 1151 / 1152 / 1154 / 1160 / 1161 - preprovision created / create error / skipped (already in AD) / group-add / file OK / file failed.
  • Return-object fields on Invoke-PKChromebookLifecycle: Claimed, PreprovisionedProtected.
  • README section "Pre-provisionering" covering folder layout, txt/csv input formats, protection semantics and combined scheduled-task usage.
  • SPN registration on Invoke-PKChromebookLifecycle. Every account is ensured to carry host/<serial> and host/<serial>.<DnsDomain> on servicePrincipalName. SPNs are written atomically at creation via New-ADComputer -OtherAttributes and ensured on existing accounts via Set-ADComputer -Add (add-only: other SPNs already on the object are left alone). Adds SpnsAdded to the return object and SPN additions to the run summary.
  • Event IDs 1140 / 1141 - SPN added to AD account / error adding SPN.
  • -MatchAttribute parameter on Invoke-PKChromebookLifecycle (default extensionAttribute1) - AD attribute holding the Chromebook serial number, used as the identity key for matching and for TameMyCerts certificate-to-account resolution. Validated at startup against the AD schema (must exist and be single-valued) to catch typos.
  • -SamAccountPrefix parameter (default CB-) - prefix for the generated SAM name.
  • -Server parameter - pins all AD operations to a single DC (defaults to the PDC emulator) to avoid replication-delay races between consecutive reads/writes.
  • -OrgUnitGroupMap parameter - hashtable of regex pattern -> single AD group. Devices whose orgUnitPath matches a pattern are added to the associated group; memberships are reconciled (a device is removed from a rule's group once its orgUnitPath no longer matches). Reconciliation is bounded to groups listed in the map; -MemberOf groups are still add-only. Conflicts between -MemberOf and -OrgUnitGroupMap throw at startup. Invalid regex or missing groups also throw at startup.
  • Disable/re-enable lifecycle driven by CSV status column. Non-ACTIVE rows (e.g. DEPROVISIONED, DISABLED) disable the matching AD account if it exists; missing AD accounts for non-ACTIVE devices are not created. Accounts whose CSV status returns to ACTIVE are re-enabled. Inactive serials are excluded from the deletion pass (still present in CSV).
  • Return object fields: Disabled, Reenabled, InactiveCount.
  • Event ID 1102 - legacy account detected (no value in -MatchAttribute), create skipped to avoid duplicates.
  • Event IDs 1122 / 1123 - account removed from AD group (OU-rule reconcile) / error removing from group.
  • Event IDs 1130 / 1131 - AD account disabled (CSV status not ACTIVE) / error disabling.
  • Event IDs 1132 / 1133 - AD account re-enabled (CSV status returned to ACTIVE) / error re-enabling.
  • README section on registering the Windows Event Log source manually (for scheduled-task service accounts without local admin).

Changed

  • Invoke-PKChromebookLifecycle now matches AD accounts via -MatchAttribute instead of by Name/sAMAccountName. New accounts get a randomly generated sAMAccountName of the form CB-<11 hex chars> (14 characters), decoupled from the Chromebook serial number — which is now stored in the attribute named by -MatchAttribute. The 15-character NetBIOS limit applies only to the generated SAM name; serial numbers of any length are supported.
  • DNSHostName is built from <serialNumber>.<DnsDomain> (was <samName>.<DnsDomain>) so Google MDM WiFi profiles authenticate against the correct hostname at NPS/RADIUS. Cert-to-AD matching is unaffected — it still resolves via -MatchAttribute.
  • CSV gating column changed from provisionStatus=PROVISIONED to status=ACTIVE to match the actual Google Admin Console export schema.
  • Account creation is now atomic: MatchAttribute and all -AttributeMap values are written via New-ADComputer -OtherAttributes, eliminating the previous create-then-set race that could leave orphan accounts.
  • Renamed Get-GoogleChromeDevices to Get-GoogleChromeDevice (singular noun per PowerShell conventions). File renamed to match. [OutputType] declared.
  • Upgraded module scaffold (build pipeline, VS Code tasks, PSScriptAnalyzer settings, generic tests) to the latest templates.
  • Introduced GitVersion.yml for automated versioning.
  • Event Log writes use [System.Diagnostics.EventLog]::WriteEntry(...) instead of Write-EventLog (which was removed from Microsoft.PowerShell.Management in PowerShell 7). Removed the now-unused EventLogName parameter from Write-CbLog.

Fixed

  • Invoke-PKChromebookPreprovision no longer writes per-file .log and -summary.txt artifacts under -WhatIf. Those files imply concrete results (created / skipped serials, totals) and were misleading on dry runs. The rolling Write-CbLog audit trail is still written under -WhatIf per the entry below.
  • Log file writes (Out-File in Write-CbLog and the run summary) no longer suppressed by -WhatIf — the audit trail is always written, even in dry runs.
  • Import-Module ActiveDirectory no longer emits implicit-remoting proxy-copy What if: noise under -WhatIf. $WhatIfPreference is temporarily cleared around the import (and restored immediately after) so the temporary remoteIpMoProxy_* file copies don't inherit it. Wrapped in try/catch to surface a clear error if RSAT is missing.

Migration

  • TameMyCerts policy must be repointed to <DirectoryServicesAttribute>extensionAttribute1</DirectoryServicesAttribute> (or the attribute chosen via -MatchAttribute). Coordinated outside this module.
  • Existing accounts from prior versions are handled by delete-and-recreate, and require two runs because deletion happens after create/update inside a single invocation:
    • Run 1 with -EnableDeletion (add -ForceDelete if over the safety threshold): legacy accounts trigger event 1102 and are skipped for creation, then removed by the deletion pass.
    • Run 2: legacy accounts are gone, new CB-<hex> accounts are created.
  • Tip: dry-run with -WhatIf -EnableDeletion first to count legacy accounts before deciding on -ForceDelete.
  • Accounts already on the new CB-<hex> scheme but with <samName>.<DnsDomain> DNSHostName will be migrated to <serial>.<DnsDomain> in-place on the next run (event 1110 with a DNSHostName: ... -> ... change). Expect a one-shot batch of "updated" accounts.

Known gaps

  • No Pester tests covering the new naming behaviour (legacy detection, conflict guard, SAM prefix validation, DNSHostName regen). To follow up in a separate change.

This package has no dependencies.

Version Downloads Last updated
0.5.0-rc.1 2 07/01/2026
0.4.0-rc.3 2 07/01/2026
0.4.0-rc.2 1 07/01/2026
0.4.0-rc.1 2 07/01/2026
0.3.0 1 06/25/2026
0.3.0-rc.21 1 07/01/2026
0.3.0-rc.20 1 06/23/2026
0.3.0-rc.19 1 06/23/2026
0.3.0-rc.18 2 06/22/2026
0.3.0-rc.17 2 06/17/2026
0.3.0-rc.16 8 06/17/2026
0.3.0-rc.15 2 06/17/2026
0.3.0-rc.14 1 06/17/2026
0.3.0-rc.13 2 06/17/2026
0.3.0-rc.12 2 06/17/2026
0.3.0-rc.11 3 06/17/2026
0.3.0-rc.10 2 06/17/2026
0.3.0-rc.9 1 06/16/2026
0.3.0-rc.8 1 06/16/2026
0.3.0-rc.7 2 06/12/2026
0.3.0-rc.6 8 06/09/2026