Skip to content

v0.3 (script-only): fix all actionable open issues, final PowerShell release#63

Merged
ugurkocde merged 16 commits into
mainfrom
feature/v0.3-script
Jul 7, 2026
Merged

v0.3 (script-only): fix all actionable open issues, final PowerShell release#63
ugurkocde merged 16 commits into
mainfrom
feature/v0.3-script

Conversation

@ugurkocde

@ugurkocde ugurkocde commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

v0.3 stays a single PowerShell Gallery script (published with Publish-Script, exactly like 0.2.x). This branch replaces the module-migration approach from #61: converting the PSGallery package from script to module would have required freeing the package name via PSGallery support and retraining users, for a distribution that the v0.4 WinUI app replaces anyway. v0.3 is the final PowerShell release.

The branch starts from the last pre-migration commit of feature/v0.3 (so every v0.3 bug fix and feature is already in script form) and backports the functional content of the module-only commits, plus new fixes found during triage.

Issues fixed in this PR / on this branch

Addressed but left open for reporter confirmation: #46 (dashboard $count batch rework with full-fetch fallback plus rebuilt device-name search should resolve both symptoms; needs a live-tenant check).

Deferred to v0.4 (WinUI app): #60, #14, #62 (AD/ConfigMgr part), #3.

Verification

  • [System.Management.Automation.Language.Parser]::ParseFile clean on the final script; all 11 XAML blocks validated as well-formed XML
  • Settings round-trip tested in isolation (default off → enable persists → disable persists)
  • Playbook bundle extraction tested: 12 files extracted, byte-identical modulo line endings, all parse clean
  • 22-point grep sweep confirming each fix mechanism exists in the final script (each issue's mechanism verified individually)
  • WPF UI cannot run on this Linux dev box, so no live-tenant smoke test; recommend one manual pass on Windows before release

Release notes

  • .VERSION bumped to 0.3.0, changelog dated 7/7/2026 — merging this PR ships the 0.3.0 release
  • README refreshed for 0.3: final-script-release note (v0.4 WinUI plan), Required Roles table, device code login, Defender opt-in, saved auth config, group tags, playbook bundling
  • This is the final PowerShell script release; v0.4 is the WinUI app rework
  • tasks/todo.md rewritten with the script-only strategy and full issue triage
  • v0.3 module migration and issue fixes #61 (module migration PR) closed in favor of this PR

🤖 Generated with Claude Code

ugurkocde and others added 15 commits March 13, 2026 19:39
- Fix Autopilot property typo: lastContactDateTime -> lastContactedDateTime
- Fix BitLocker key retrieval array bug: access first element explicitly
- Clear stale parsedDevices on bulk import cancel
- Fix status string corruption from $deviceSuccess++ inside PSCustomObject
- Fix permission scopes: Device.ReadWrite.All, add BitlockerKey.Read.All
- Fix export functions to use actual DeviceObject class properties
- Load playbooks from local Playbooks/ directory instead of remote GitHub URLs
- Generate playbook result columns dynamically from actual output schema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Invoke-GraphRequestWithRetry wrapper for 429 throttling (reads
  Retry-After header), 5xx transient errors (exponential backoff), and
  network-level failures
- Add Invoke-GraphBatchRequest helper that auto-chunks up to 20
  sub-requests per POST /$batch with sub-request retry logic
- Update Get-GraphPagedResults to use retry wrapper and accept optional
  Headers parameter
- Migrate all v1.0 endpoints to beta with $select across main script
  and Playbooks 1-5 to reduce API payload size
- Rewrite dashboard statistics to use $count batch (1 API call replaces
  3 full-collection fetches + client-side counting), with automatic
  fallback to full-fetch for tenants without $count support
- Batch Entra+Intune search queries for device name lookups and
  Intune+Autopilot queries for serial number lookups
- Hoist Autopilot full-collection fetch out of per-search-term loop
- Batch per-device offboarding operations (Entra+Intune+Autopilot)
  into single $batch call per device
- Add bulk Autopilot deletion via deleteDevices endpoint using serial
  numbers when 2+ devices selected, with individual deletion fallback
  and per-device status parsing from API response
…m filtering, grid filtering, HTML reports

- Add saved authentication config (#48): Save/auto-load cert and secret
  configs to %LocalAppData%/DeviceOffboardingManager. Client secret is
  never persisted for security.
- Add co-management awareness: ManagementAgent property on DeviceObject,
  amber warning banner in confirmation dialog for co-managed devices.
- Add platform filtering on dashboard (#40): ComboBox to filter all
  dashboard statistics by OS platform.
- Add grid filtering and shift-click range selection (#33): Filter
  TextBoxes above DataGrid for live column filtering, shift-click on
  checkboxes to toggle ranges.
- Add HTML offboarding report generation: Export-OffboardingReport
  function with professional styled HTML, per-device service status,
  summary stats. Export buttons in summary and dashboard dialogs.
- Update homepage to show Defender for Endpoint as supported (no longer
  "Soon").

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…polation

- Add fallback from EntraDeviceObjectId to intuneDevice.azureADDeviceId for
  LAPS lookup (mirrors BitLocker fallback pattern)
- Fix PowerShell string interpolation in LAPS URI using $() syntax
- Add diagnostic logging for LAPS device ID resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ybooks, confirmation dialog

- Standardize CornerRadius to 6, fix dashboard subtitle contrast
- Fix DataGrid column widths, add tooltips and hover states
- Restructure sidebar: connect/tenant at top, nav center, utils bottom
- Add ConnectionStatusDot indicator (red/green)
- Redesign home page: slim preview banner, Get Started card, quick nav buttons
- Add dashboard page header with refresh button
- Fix hardcoded stale device progress bars with dynamic calculation
- Add device management page header, search placeholder, result count
- Fix dropdown labels (Device Name, Serial Number)
- Add filter toggle, clear search, conditional offboard panel with selection count
- Categorize playbook cards into Device Compliance, Inventory, Health sections
- Make playbook cards responsive (MinWidth/MaxWidth)
- Move confirmation warning to top, add type-to-confirm safeguard

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… sidebar, and page-by-page polish

Toast notifications:
- Inline toast overlay replaces ~33 blocking MessageBox popups in main window
- Slide-in/out animation with generation counter to prevent stale callbacks
- Auto-dismiss with configurable duration, manual dismiss via X button or Escape
- Color-coded: green (success), red (error), blue (info)

Keyboard shortcuts:
- Ctrl+K: navigate to Device Offboarding and focus search
- Ctrl+B: toggle sidebar collapse/expand
- F5: refresh dashboard (when on Dashboard page)
- Escape: contextual dismiss (toast > filter row > clear search)
- Enter: search (in search box), connect (auth dialog), confirm (OS picker)
- IsCancel on all modal Close/Cancel buttons for universal Escape-to-close

Collapsible sidebar:
- Toggle button with collapse to 48px rail
- Connection status dot remains visible when collapsed
- Synced at all connection state change points

Search UX:
- Search button shows "Searching..." with wait cursor during operation
- Dynamic placeholder text updates when search mode dropdown changes
- Empty state message shown before first search
- Clear button returns focus to search input
- DataGrid visibility toggled with empty state

Home page:
- Specific subtitle about offboarding workflow
- Ellipse dot bullets replacing ASCII dashes
- Feature list rewritten (offboarding, CSV import, stale reporting, key backup)
- Full Microsoft service names
- Card backgrounds unified to 2 tones
- Get Started card updates dynamically on connection (green button, new text)
- Quick nav tooltips show "(connect first)" when disabled, Ctrl+K hint
- Auto-navigate to Dashboard when already connected on launch

Dashboard page:
- ScrollViewer wrapper for small windows
- "Last refreshed" timestamp in header
- All counts show "--" before data loads (not "0")
- Specific subtitles: "Managed by Intune", "Registered in Autopilot", etc.
- Stale cards: "Not synced in 30/90/180+ days"
- Tooltips on all clickable stat cards
- Refresh button loading state with F5 shortcut
- "EntraID" corrected to "Entra ID"

Device Management page:
- Search/Clear button tooltips with keyboard hints
- Filter row columns aligned to DataGrid widths
- Filter boxes have tooltips
- "OS" column header expanded to "Operating System"
- "View" groups link gets tooltip
- "Offboard device(s)" simplified to "Offboard Devices"
- Empty state TextBlock before first search

Playbooks page:
- Specific subtitle about compliance, inventory, security audits
- "Device Compliance" renamed to "Enrollment Discrepancies"
- Hover effect on playbook cards
- Back/Export buttons get cursor and tooltips
- Results header includes device count
- Results DataGrid column widths proportional

Auth dialog:
- Window title matches header text
- Concise subtitle without "API"
- "Interactive Login (Browser)" instead of "(Admin User)"
- IsDefault on Connect, IsCancel on Cancel
- Cursor and tooltips on Save/Import buttons
- "Save Config" renamed to "Save Locally"
- Auto-selects previously used auth method

Bulk Import dialog:
- "File Template" instead of "CSV Template"
- "Save Template" instead of "Download Template"
- Instructive placeholder text
- IsCancel on Cancel button
- Tooltips on all buttons
- Full file path as tooltip on file path TextBox

Confirmation dialog:
- Warning text covers both delete and disable actions
- Dynamic subtitle with device count
- IsCancel on Cancel, Cursor on Confirm
- Type-to-confirm TextBox turns green on match
- Copy Key button gets tooltip

Offboarding Summary dialog:
- Dynamic timestamp subtitle
- Clean status labels without ASCII art prefixes
- "Defender" instead of "MDE" column label
- IsCancel + Cursor on Close, tooltip on Export Report

All remaining modals:
- IsCancel="True" on every Close/Cancel button (10 total)
- Cursor="Hand" on every interactive button (42 total)
- MenuButtonStyle gets Cursor="Hand"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…o script

The v0.3 line returns to the single-script layout; this ports the
functional content of the module-only commits back into
DeviceOffboardingManager.ps1:

- Add ConvertTo-ODataStringValue and escape search text and
  device-derived names/serials in all Graph $filter interpolations
  (from 2d47b30). The module-era serial-search regression does not
  apply here: the script always used serialNumber eq and
  deviceName-only contains filters.
- Document why Intune managedDevices filters must use serialNumber eq
  and must not combine contains(deviceName) with contains(serialNumber)
  (from 2979360).
- Show a loading toast + dispatcher pump in all 8 dashboard stat-card
  handlers before the synchronous fetch (from 2979360).
- Remove dead locals $bulkAutopilotIds and $playbookName (from 2979360).
- Mark 0.3 changelog as Unreleased.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backports the functional content of module commits fe01ab9 and bdf71f4
into the script:

- New settings.json in the config directory with
  DefenderIntegrationEnabled (default false), managed via
  Get/Save-DeviceOffboardingSettings and
  Get/Set-DefenderIntegrationEnabled.
- Defender only appears as an offboarding target when the setting is
  enabled; offboarding and summary checks are gated the same way.
- Prerequisites dialog gains a Defender integration toggle and an
  optional MSAL.PS module check with install button.
- Get-MdeAccessToken now tries both Defender API resource endpoints
  and supports app-only token acquisition for client-secret and
  certificate auth via $script:CurrentAuthDetails (captured in
  Connect-ToGraph, disposed on disconnect/auth failure), with
  delegated silent/interactive fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backports the device-code flow from module commit 2d47b30 into the
script: a DeviceCodeAuth radio button in the authentication dialog,
a DeviceCode branch in Connect-ToGraph using Connect-MgGraph
-UseDeviceCode, and input-panel collapse handling. Avoids browser
localhost redirect / WAM failures with interactive authentication.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Publish-Script only delivers DeviceOffboardingManager.ps1, so PSGallery
installs had no Playbooks/ directory and every playbook failed with
'Playbook file not found'. The script now embeds all 10 playbooks plus
PlaybookHelpers.ps1 as here-strings; Get-PlaybookRoot prefers the
repo-local Playbooks/ directory and otherwise extracts the bundled
copies to the per-user config directory.

Also:
- Replace 3-argument Join-Path calls at playbook call sites, which
  fail on Windows PowerShell 5.1.
- Guard recovery-key clipboard copy against transient clipboard
  failures so the exception cannot escape the routed event handler
  and destabilize the offboarding dialogs (Issue #38).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backported from module commit 2d47b30: a 'Set Group Tag' button next to
the export buttons, enabled with device selection; Show-GroupTagDialog
for entering or clearing a tag; Set-AutopilotGroupTagForDevices posts
updateDeviceProperties per device, resolving missing Autopilot
identities by exact serial match only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backported from module commit 2d47b30: lists imported corporate device
identifiers (importedDeviceIdentities) with enrollment state and last
contact staleness. Wired into the Playbooks page and included in the
embedded playbook bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Parses the main script and all playbooks, validates every inline XAML
block as well-formed XML, and checks that the embedded playbook bundle
is in sync with the Playbooks/ directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
#52, #53)

- Get-Graph403Message maps per-service 403 Forbidden responses during
  offboarding to the likely missing directory/Intune role instead of a
  raw HTTP error (MAA detection unchanged)
- README: final-script-release note (v0.4 WinUI plan), Required Roles
  table, device code login, Defender opt-in section, saved auth config,
  group tags, playbook bundling, updated search/usage docs
- Bump .VERSION to 0.3.0, date changelog 7/7/2026

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ugurkocde ugurkocde merged commit bf030d6 into main Jul 7, 2026
1 check passed
@ugurkocde ugurkocde deleted the feature/v0.3-script branch July 7, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment