Intune-Diag-UI.ps1

<#PSScriptInfo
  
.VERSION 2.1
.GUID b00d1997-e4fa-4af1-86f0-49220c606238
.AUTHOR Florian Salzmann
.COMPANYNAME scloud.work
.COPYRIGHT 2025 Florian Salzmann. GPL-3.0 license.
.TAGS PowerShell Intune Diagnostics LogAnalyzer Autopilot
.LICENSEURI https://github.com/FlorianSLZ/scloud/blob/main/LICENSE
.PROJECTURI https://github.com/FlorianSLZ/scloud/tree/main/scripts/Intune-Diag
.ICONURI https://scloud.work/wp-content/uploads/Intune-Diag.png
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
    2024-09-26, 1.0 - Original published version.
    2025-03-14, 1.1 - Minor improvements
    2025-03-14, 1.2 - Changed to English, improved error handling and loader
    2026-02-24, 2.0 - UI overhaul, script selector dropdown
    2026-02-25, 2.1 - Enhanced dark UI, Export Autopilot Logs button, pwsh compatibility
 
#>


<#
.DESCRIPTION
A PowerShell-based tool for analyzing Intune Management Extension and Autopilot logs.
Quickly troubleshoot Intune and Autopilot issues with an intuitive UI and built-in ZIP extraction.
 
Supported diagnostic scripts (downloaded from PowerShell Gallery):
- Get-IntuneManagementExtensionDiagnostics (by Petri Paavola)
- Get-AutopilotDiagnostics (by Michael Niehaus / Microsoft)
- Get-AutopilotDiagnosticsCommunity (by Andrew Taylor, Michael Niehaus & Steven van Beek)
#>


#########################################################################################
# Initial Setup
#########################################################################################
$PackageName = "IntuneDiagnostic-UI"
$ScriptFolder = "C:\ProgramData\$PackageName"

$DiagScripts = @(
    @{
        Name      = "Get-IntuneManagementExtensionDiagnostics"
        Label     = "IME Diagnostics (Petri Paavola)"
        FileName  = "Get-IntuneManagementExtensionDiagnostics.ps1"
        FolderArg = '-LogFilesFolder "{0}"'
        FileArg   = '-LogFilesFolder "{0}"'
        LocalArg  = ''
    },
    @{
        Name      = "Get-IntuneManagementExtensionDiagnosticsV2"
        Label     = "IME Diagnostics V2 - Modern UI (Ayoub Sekoum)"
        FileName  = "Get-IntuneManagementExtensionDiagnosticsV2.ps1"
        FolderArg = '-LogFilesFolder "{0}"'
        FileArg   = '-LogFilesFolder "{0}"'
        LocalArg  = ''
    },
    @{
        Name      = "Get-AutopilotDiagnostics"
        Label     = "Autopilot Diagnostics (Michael Niehaus)"
        FileName  = "Get-AutopilotDiagnostics.ps1"
        FolderArg = '-ZIPFile "{0}"'
        FileArg   = '-ZIPFile "{0}"'
        LocalArg  = ''
    },
    @{
        Name      = "Get-AutopilotDiagnosticsCommunity"
        Label     = "Autopilot Diagnostics Community (Andrew Taylor)"
        FileName  = "Get-AutopilotDiagnosticsCommunity.ps1"
        FolderArg = '-File "{0}"'
        FileArg   = '-File "{0}"'
        LocalArg  = ''
    }
)

# Always use Windows PowerShell 5.1 for diagnostic scripts (they need reg.exe, expand.exe in PATH)
$psExePath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

#########################################################################################
# Execution Policy Check
#########################################################################################
$CurrentPolicy = Get-ExecutionPolicy -Scope Process
if ($CurrentPolicy -ne "Bypass") {
    Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
}

#########################################################################################
# Download Scripts
#########################################################################################
if (!(Test-Path $ScriptFolder)) {
    New-Item -Path $ScriptFolder -Type Directory -Force | Out-Null
}

foreach ($ds in $DiagScripts) {
    $scriptPath = Join-Path $ScriptFolder $ds.FileName
    if (!(Test-Path $scriptPath)) {
        try {
            Save-Script $ds.Name -Path $ScriptFolder -Force -ErrorAction Stop
        }
        catch {
            Write-Host "Warning: Could not download $($ds.Name): $_" -ForegroundColor Yellow
        }
    }
}

#########################################################################################
# UI
#########################################################################################
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Set unique AppUserModelID so the taskbar shows our icon
try {
    Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class TaskbarHelper {
    [DllImport("shell32.dll", SetLastError = true)]
    public static extern void SetCurrentProcessExplicitAppUserModelID(
        [MarshalAs(UnmanagedType.LPWStr)] string AppID);
}
"@

    [TaskbarHelper]::SetCurrentProcessExplicitAppUserModelID("scloud.IntuneDiagnosticTool")
}
catch { }

# ---------- Color Palette (enhanced dark theme) ----------
$colBg = [System.Drawing.Color]::FromArgb(22, 22, 30)
$colSurface = [System.Drawing.Color]::FromArgb(32, 33, 42)
$colSurfaceHi = [System.Drawing.Color]::FromArgb(42, 43, 56)
$colAccent = [System.Drawing.Color]::FromArgb(99, 102, 241)      # indigo
$colAccentHover = [System.Drawing.Color]::FromArgb(118, 120, 255)
$colSuccess = [System.Drawing.Color]::FromArgb(34, 197, 94)
$colWarning = [System.Drawing.Color]::FromArgb(245, 158, 11)
$colError = [System.Drawing.Color]::FromArgb(239, 68, 68)
$colAmber = [System.Drawing.Color]::FromArgb(245, 158, 11)
$colAmberHover = [System.Drawing.Color]::FromArgb(255, 180, 40)
$colTextPrimary = [System.Drawing.Color]::White
$colTextMuted = [System.Drawing.Color]::FromArgb(140, 140, 165)
$colInputBg = [System.Drawing.Color]::FromArgb(38, 38, 55)
$colBorder = [System.Drawing.Color]::FromArgb(55, 55, 75)

# ---------- Fonts ----------
$fontTitle = New-Object System.Drawing.Font("Segoe UI", 15, [System.Drawing.FontStyle]::Bold)
$fontNormal = New-Object System.Drawing.Font("Segoe UI", 9.5)
$fontSmall = New-Object System.Drawing.Font("Segoe UI", 8.5)
$fontBadge = New-Object System.Drawing.Font("Segoe UI", 7.5, [System.Drawing.FontStyle]::Bold)
$fontButton = New-Object System.Drawing.Font("Segoe UI Semibold", 9.5)

# ---------- Form ----------
$form = New-Object System.Windows.Forms.Form
$form.Text = "Intune Diagnostic Tool"
$form.Size = New-Object System.Drawing.Size(560, 520)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedSingle"
$form.MaximizeBox = $false
$form.BackColor = $colBg
$form.ForeColor = $colTextPrimary
$form.Font = $fontNormal

# ---------- Header Panel ----------
$panelHeader = New-Object System.Windows.Forms.Panel
$panelHeader.Dock = "Top"
$panelHeader.Height = 72
$panelHeader.BackColor = $colSurface
$form.Controls.Add($panelHeader)

# Accent separator line at bottom of header
$panelSep = New-Object System.Windows.Forms.Panel
$panelSep.Location = New-Object System.Drawing.Point(0, 70)
$panelSep.Size = New-Object System.Drawing.Size(560, 2)
$panelSep.BackColor = $colAccent
$panelHeader.Controls.Add($panelSep)

# Logo
$logoSize = 44
$logoPadLeft = 16
$logoPadTop = 14
$logoPath = Join-Path $ScriptFolder "IntuneDiag_logo.png"

$picLogo = New-Object System.Windows.Forms.PictureBox
$picLogo.Location = New-Object System.Drawing.Point($logoPadLeft, $logoPadTop)
$picLogo.Size = New-Object System.Drawing.Size($logoSize, $logoSize)
$picLogo.SizeMode = "Zoom"
$picLogo.BackColor = [System.Drawing.Color]::Transparent

try {
    if (Test-Path $logoPath) {
        $picLogo.Image = [System.Drawing.Image]::FromFile($logoPath)
    }
    else {
        $webClient = New-Object System.Net.WebClient
        $webClient.DownloadFile("https://scloud.work/wp-content/uploads/Intune-Diag.png", $logoPath)
        $picLogo.Image = [System.Drawing.Image]::FromFile($logoPath)
    }
    $iconBmp = New-Object System.Drawing.Bitmap($picLogo.Image, 32, 32)
    $hIcon = $iconBmp.GetHicon()
    $form.Icon = [System.Drawing.Icon]::FromHandle($hIcon)
}
catch { }
$panelHeader.Controls.Add($picLogo)

$textLeft = $logoPadLeft + $logoSize + 12

$lblTitle = New-Object System.Windows.Forms.Label
$lblTitle.Text = "Intune Diagnostic Tool"
$lblTitle.Font = $fontTitle
$lblTitle.ForeColor = $colTextPrimary
$lblTitle.AutoSize = $true
$lblTitle.Location = New-Object System.Drawing.Point($textLeft, 12)
$panelHeader.Controls.Add($lblTitle)

$lblSubtitle = New-Object System.Windows.Forms.Label
$lblSubtitle.Text = "Analyze Intune & Autopilot logs with ease"
$lblSubtitle.Font = $fontSmall
$lblSubtitle.ForeColor = $colTextMuted
$lblSubtitle.AutoSize = $true
$lblSubtitle.Location = New-Object System.Drawing.Point(($textLeft + 2), 42)
$panelHeader.Controls.Add($lblSubtitle)

# ---------- Card 1: Script Selector ----------
$card1 = New-Object System.Windows.Forms.Panel
$card1.Location = New-Object System.Drawing.Point(16, 86)
$card1.Size = New-Object System.Drawing.Size(512, 95)
$card1.BackColor = $colSurface
$form.Controls.Add($card1)

$lbl1 = New-Object System.Windows.Forms.Label
$lbl1.Text = " DIAGNOSTIC SCRIPT"
$lbl1.Font = $fontBadge
$lbl1.ForeColor = $colAccent
$lbl1.Location = New-Object System.Drawing.Point(12, 10)
$lbl1.Size = New-Object System.Drawing.Size(300, 16)
$card1.Controls.Add($lbl1)

$comboScript = New-Object System.Windows.Forms.ComboBox
$comboScript.Location = New-Object System.Drawing.Point(12, 30)
$comboScript.Size = New-Object System.Drawing.Size(488, 28)
$comboScript.DropDownStyle = "DropDownList"
$comboScript.FlatStyle = "Flat"
$comboScript.BackColor = $colInputBg
$comboScript.ForeColor = $colTextPrimary
$comboScript.Font = $fontNormal
foreach ($ds in $DiagScripts) {
    $comboScript.Items.Add($ds.Label) | Out-Null
}
$comboScript.SelectedIndex = 0
$card1.Controls.Add($comboScript)

$lblAvail = New-Object System.Windows.Forms.Label
$lblAvail.Font = $fontSmall
$lblAvail.Location = New-Object System.Drawing.Point(12, 66)
$lblAvail.Size = New-Object System.Drawing.Size(488, 18)
$card1.Controls.Add($lblAvail)

function Update-Availability {
    $idx = $comboScript.SelectedIndex
    $scriptPath = Join-Path $ScriptFolder $DiagScripts[$idx].FileName
    if (Test-Path $scriptPath) {
        $lblAvail.Text = [char]0x2713 + " Script ready and cached locally"
        $lblAvail.ForeColor = $colSuccess
    }
    else {
        $lblAvail.Text = [char]0x26A0 + " Script not found - will download on run"
        $lblAvail.ForeColor = $colWarning
    }
}
$comboScript.add_SelectedIndexChanged({ Update-Availability })
Update-Availability

# ---------- Card 2: Log Source ----------
$card2 = New-Object System.Windows.Forms.Panel
$card2.Location = New-Object System.Drawing.Point(16, 193)
$card2.Size = New-Object System.Drawing.Size(512, 95)
$card2.BackColor = $colSurface
$form.Controls.Add($card2)

$lbl2 = New-Object System.Windows.Forms.Label
$lbl2.Text = " LOG SOURCE"
$lbl2.Font = $fontBadge
$lbl2.ForeColor = [System.Drawing.Color]::FromArgb(139, 92, 246)  # violet
$lbl2.Location = New-Object System.Drawing.Point(12, 10)
$lbl2.Size = New-Object System.Drawing.Size(300, 16)
$card2.Controls.Add($lbl2)

$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(12, 30)
$textBox.Size = New-Object System.Drawing.Size(394, 28)
$textBox.AllowDrop = $true
$textBox.BackColor = $colInputBg
$textBox.ForeColor = $colTextPrimary
$textBox.BorderStyle = "FixedSingle"
$textBox.Font = $fontNormal
$card2.Controls.Add($textBox)

# Browse button
$btnBrowse = New-Object System.Windows.Forms.Button
$btnBrowse.Text = "Browse..."
$btnBrowse.Location = New-Object System.Drawing.Point(414, 29)
$btnBrowse.Size = New-Object System.Drawing.Size(86, 28)
$btnBrowse.FlatStyle = "Flat"
$btnBrowse.BackColor = $colSurfaceHi
$btnBrowse.ForeColor = $colTextPrimary
$btnBrowse.Font = $fontSmall
$btnBrowse.FlatAppearance.BorderColor = $colBorder
$btnBrowse.Cursor = [System.Windows.Forms.Cursors]::Hand
$card2.Controls.Add($btnBrowse)

$btnBrowse.add_Click({
        $dlg = New-Object System.Windows.Forms.OpenFileDialog
        $dlg.Title = "Select a ZIP, CAB, or log file"
        $dlg.Filter = "Supported files (*.zip;*.cab)|*.zip;*.cab|All files (*.*)|*.*"
        $dlg.CheckFileExists = $true
        if ($dlg.ShowDialog() -eq "OK") {
            $textBox.Text = $dlg.FileName
        }
    })

$lblHint = New-Object System.Windows.Forms.Label
$lblHint.Text = [char]0x2193 + " Drag & drop a folder, ZIP or CAB file, or use Browse"
$lblHint.Font = $fontSmall
$lblHint.ForeColor = $colTextMuted
$lblHint.Location = New-Object System.Drawing.Point(12, 66)
$lblHint.Size = New-Object System.Drawing.Size(488, 18)
$card2.Controls.Add($lblHint)

# ---------- Drag & Drop ----------
$textBox.add_DragEnter({
        param($sender, $e)
        if ($e.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) {
            $e.Effect = [Windows.Forms.DragDropEffects]::Copy
        }
        else {
            $e.Effect = [Windows.Forms.DragDropEffects]::None
        }
    })

$textBox.add_DragDrop({
        param($sender, $e)
        $items = $e.Data.GetData([Windows.Forms.DataFormats]::FileDrop)
        if ($items.Count -eq 1 -and (Test-Path $items[0])) {
            $textBox.Text = $items[0]
        }
        else {
            [System.Windows.Forms.MessageBox]::Show(
                "Please drop only one valid folder, ZIP, or CAB file.",
                "Invalid Input",
                [System.Windows.Forms.MessageBoxButtons]::OK,
                [System.Windows.Forms.MessageBoxIcon]::Warning
            )
        }
    })

# ---------- Buttons Row 1: Analyze ----------
$btnAnalyzeFolder = New-Object System.Windows.Forms.Button
$btnAnalyzeFolder.Text = [char]0x25B6 + " Analyze Path"
$btnAnalyzeFolder.Location = New-Object System.Drawing.Point(16, 302)
$btnAnalyzeFolder.Size = New-Object System.Drawing.Size(248, 42)
$btnAnalyzeFolder.FlatStyle = "Flat"
$btnAnalyzeFolder.BackColor = $colAccent
$btnAnalyzeFolder.ForeColor = $colTextPrimary
$btnAnalyzeFolder.Font = $fontButton
$btnAnalyzeFolder.FlatAppearance.BorderSize = 0
$btnAnalyzeFolder.Cursor = [System.Windows.Forms.Cursors]::Hand
$form.Controls.Add($btnAnalyzeFolder)

$btnAnalyzeFolder.add_MouseEnter({ $this.BackColor = $colAccentHover })
$btnAnalyzeFolder.add_MouseLeave({ $this.BackColor = $colAccent })

$btnAnalyzePC = New-Object System.Windows.Forms.Button
$btnAnalyzePC.Text = [char]0x2318 + " Analyze This PC"
$btnAnalyzePC.Location = New-Object System.Drawing.Point(280, 302)
$btnAnalyzePC.Size = New-Object System.Drawing.Size(248, 42)
$btnAnalyzePC.FlatStyle = "Flat"
$btnAnalyzePC.BackColor = $colSurface
$btnAnalyzePC.ForeColor = $colTextPrimary
$btnAnalyzePC.Font = $fontButton
$btnAnalyzePC.FlatAppearance.BorderColor = $colBorder
$btnAnalyzePC.Cursor = [System.Windows.Forms.Cursors]::Hand
$form.Controls.Add($btnAnalyzePC)

$btnAnalyzePC.add_MouseEnter({ $this.BackColor = $colSurfaceHi })
$btnAnalyzePC.add_MouseLeave({ $this.BackColor = $colSurface })

# ---------- Button Row 2: Export Logs ----------
$btnExportLogs = New-Object System.Windows.Forms.Button
$btnExportLogs.Text = [char]0x21E9 + " Export Autopilot Logs (.zip)"
$btnExportLogs.Location = New-Object System.Drawing.Point(16, 354)
$btnExportLogs.Size = New-Object System.Drawing.Size(512, 38)
$btnExportLogs.FlatStyle = "Flat"
$btnExportLogs.BackColor = $colSurface
$btnExportLogs.ForeColor = $colAmber
$btnExportLogs.Font = $fontButton
$btnExportLogs.FlatAppearance.BorderColor = $colAmber
$btnExportLogs.Cursor = [System.Windows.Forms.Cursors]::Hand
$form.Controls.Add($btnExportLogs)

$btnExportLogs.add_MouseEnter({ $this.BackColor = $colSurfaceHi })
$btnExportLogs.add_MouseLeave({ $this.BackColor = $colSurface })

# ---------- Status ----------
$lblStatus = New-Object System.Windows.Forms.Label
$lblStatus.Location = New-Object System.Drawing.Point(16, 406)
$lblStatus.Size = New-Object System.Drawing.Size(512, 20)
$lblStatus.Font = $fontSmall
$lblStatus.ForeColor = $colTextMuted
$lblStatus.Text = ""
$form.Controls.Add($lblStatus)

# ---------- Progress Bar ----------
$progressBar = New-Object System.Windows.Forms.ProgressBar
$progressBar.Location = New-Object System.Drawing.Point(16, 428)
$progressBar.Size = New-Object System.Drawing.Size(512, 5)
$progressBar.Style = "Marquee"
$progressBar.MarqueeAnimationSpeed = 30
$progressBar.Visible = $false
$form.Controls.Add($progressBar)

# ---------- Footer ----------
$lblFooter = New-Object System.Windows.Forms.LinkLabel
$lblFooter.Text = "baki | Intune Diagnostic Tool | v2.1"
$lblFooter.Font = $fontSmall
$lblFooter.LinkColor = [System.Drawing.Color]::FromArgb(80, 80, 110)
$lblFooter.ActiveLinkColor = $colAccent
$lblFooter.VisitedLinkColor = [System.Drawing.Color]::FromArgb(80, 80, 110)
$lblFooter.TextAlign = "MiddleCenter"
$lblFooter.Dock = "Bottom"
$lblFooter.Height = 30
$lblFooter.BackColor = [System.Drawing.Color]::FromArgb(16, 16, 22)
$lblFooter.LinkArea = New-Object System.Windows.Forms.LinkArea(0, 4)
$lblFooter.add_LinkClicked({
        Start-Process "https://github.com/Ayoub-Sekoum"
    })
$form.Controls.Add($lblFooter)

#########################################################################################
# Functions
#########################################################################################

function Set-UIBusy {
    param([bool]$Busy, [string]$Message = "")
    $lblStatus.Text = $Message
    $progressBar.Visible = $Busy
    $btnAnalyzeFolder.Enabled = -not $Busy
    $btnAnalyzePC.Enabled = -not $Busy
    $btnExportLogs.Enabled = -not $Busy
    $comboScript.Enabled = -not $Busy
    $btnBrowse.Enabled = -not $Busy
    if ($Busy) {
        $form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor
        $lblStatus.ForeColor = $colAccent
    }
    else {
        $form.Cursor = [System.Windows.Forms.Cursors]::Default
    }
    $form.Refresh()
}

function Get-SelectedScript {
    $idx = $comboScript.SelectedIndex
    return $DiagScripts[$idx]
}

function Ensure-ScriptAvailable {
    param($ScriptDef)
    $path = Join-Path $ScriptFolder $ScriptDef.FileName
    if (!(Test-Path $path)) {
        Set-UIBusy -Busy $true -Message "Downloading $($ScriptDef.Name)..."
        try {
            Save-Script $ScriptDef.Name -Path $ScriptFolder -Force -ErrorAction Stop
        }
        catch {
            [System.Windows.Forms.MessageBox]::Show(
                "Failed to download $($ScriptDef.Name):`n$_",
                "Download Error",
                [System.Windows.Forms.MessageBoxButtons]::OK,
                [System.Windows.Forms.MessageBoxIcon]::Error
            )
            Set-UIBusy -Busy $false -Message "Download failed."
            $lblStatus.ForeColor = $colError
            return $null
        }
    }
    if (Test-Path $path) { return $path } else { return $null }
}

function Start-Analysis {
    param([string]$InputPath)

    $scriptDef = Get-SelectedScript
    $scriptPath = Ensure-ScriptAvailable -ScriptDef $scriptDef
    if (-not $scriptPath) { return }

    Set-UIBusy -Busy $true -Message "Running $($scriptDef.Name)..."

    $logTarget = $InputPath

    # Handle archive extraction for IME Diagnostics (it expects a folder, not a file)
    if ($scriptDef.Name -like "Get-IntuneManagementExtensionDiagnostics*" -and $InputPath -match '\.(zip|cab)$') {
        $extractPath = Join-Path $env:TEMP "IntuneDiag_$(Get-Random)"
        New-Item -Path $extractPath -Type Directory -Force | Out-Null
        try {
            if ($InputPath -match '\.zip$') {
                Expand-Archive -Path $InputPath -DestinationPath $extractPath -Force
            }
            else {
                # CAB files: use expand.exe
                $expandProc = Start-Process -FilePath "expand.exe" `
                    -ArgumentList "`"$InputPath`" -F:* `"$extractPath`"" `
                    -PassThru -Wait -NoNewWindow
                if ($expandProc.ExitCode -ne 0) {
                    throw "expand.exe exited with code $($expandProc.ExitCode)"
                }
            }
            $logTarget = $extractPath
        }
        catch {
            [System.Windows.Forms.MessageBox]::Show(
                "Error extracting archive:`n$_",
                "Extraction Error",
                [System.Windows.Forms.MessageBoxButtons]::OK,
                [System.Windows.Forms.MessageBoxIcon]::Error
            )
            Set-UIBusy -Busy $false -Message "Analysis failed."
            $lblStatus.ForeColor = $colError
            return
        }
    }

    # Build argument string
    $isFolder = (Test-Path $logTarget -PathType Container)
    $isFile = (Test-Path $logTarget -PathType Leaf)

    if ($scriptDef.Name -like "Get-IntuneManagementExtensionDiagnostics*") {
        $argString = $scriptDef.FolderArg -f $logTarget
    }
    elseif ($isFile) {
        $argString = $scriptDef.FileArg -f $logTarget
    }
    else {
        $archiveFile = Get-ChildItem -Path $logTarget -Include *.zip, *.cab -Recurse -File | Select-Object -First 1
        if ($archiveFile) {
            $argString = $scriptDef.FileArg -f $archiveFile.FullName
        }
        else {
            $argString = $scriptDef.FolderArg -f $logTarget
        }
    }

    try {
        $psArgs = "-ExecutionPolicy Bypass -File `"$scriptPath`" $argString"
        Start-Process -FilePath $psExePath -ArgumentList $psArgs -NoNewWindow -Wait
        Set-UIBusy -Busy $false -Message ([char]0x2713 + " Analysis completed successfully.")
        $lblStatus.ForeColor = $colSuccess
    }
    catch {
        Set-UIBusy -Busy $false -Message ([char]0x2717 + " Analysis encountered an error.")
        $lblStatus.ForeColor = $colError
    }

    Update-Availability
}

function Start-LocalAnalysis {
    $scriptDef = Get-SelectedScript
    $scriptPath = Ensure-ScriptAvailable -ScriptDef $scriptDef
    if (-not $scriptPath) { return }

    Set-UIBusy -Busy $true -Message "Running $($scriptDef.Name) on this PC..."

    try {
        $localArg = "$($scriptDef.LocalArg)".Trim()
        $psArgs = "-ExecutionPolicy Bypass -File `"$scriptPath`" $localArg".Trim()
        Start-Process -FilePath $psExePath -ArgumentList $psArgs -NoNewWindow -Wait
        Set-UIBusy -Busy $false -Message ([char]0x2713 + " Local analysis completed successfully.")
        $lblStatus.ForeColor = $colSuccess
    }
    catch {
        Set-UIBusy -Busy $false -Message ([char]0x2717 + " Local analysis encountered an error.")
        $lblStatus.ForeColor = $colError
    }

    Update-Availability
}

function Start-ExportLogs {
    $outFolder = "C:\temp"
    if (!(Test-Path $outFolder)) {
        New-Item -Path $outFolder -Type Directory -Force | Out-Null
    }
    $pcName = $env:COMPUTERNAME
    $cabPath = Join-Path $outFolder "AutopilotLogs-$pcName.cab"
    $extractTo = Join-Path $outFolder "AutopilotLogs-$pcName"

    # Clean previous exports
    if (Test-Path $cabPath) { Remove-Item $cabPath -Force -ErrorAction SilentlyContinue }
    if (Test-Path $extractTo) { Remove-Item $extractTo -Recurse -Force -ErrorAction SilentlyContinue }

    # 1. Export CAB
    Set-UIBusy -Busy $true -Message "Exporting Autopilot logs..."
    try {
        $proc = Start-Process -FilePath "mdmdiagnosticstool.exe" `
            -ArgumentList "-area Autopilot -cab `"$cabPath`"" `
            -PassThru -Wait -NoNewWindow
        if ($proc.ExitCode -ne 0 -or !(Test-Path $cabPath)) {
            Set-UIBusy -Busy $false -Message ([char]0x2717 + " Export failed (exit code $($proc.ExitCode)).")
            $lblStatus.ForeColor = $colError
            return
        }
    }
    catch {
        Set-UIBusy -Busy $false -Message ([char]0x2717 + " Export error: $_")
        $lblStatus.ForeColor = $colError
        return
    }

    # 2. Extract CAB to folder
    New-Item -Path $extractTo -Type Directory -Force | Out-Null
    Set-UIBusy -Busy $true -Message "Extracting CAB..."
    try {
        Start-Process -FilePath "expand.exe" `
            -ArgumentList "`"$cabPath`" -F:* `"$extractTo`"" `
            -Wait -NoNewWindow
    }
    catch {
        Set-UIBusy -Busy $false -Message ([char]0x2717 + " Extraction error: $_")
        $lblStatus.ForeColor = $colError
        return
    }

    # 3. Verify extraction produced files
    $extractedFiles = Get-ChildItem -Path $extractTo -File -ErrorAction SilentlyContinue
    if ($null -eq $extractedFiles -or $extractedFiles.Count -eq 0) {
        Set-UIBusy -Busy $false -Message ([char]0x2717 + " CAB was empty - no files extracted.")
        $lblStatus.ForeColor = $colError
        return
    }

    # 4. Pre-check: is this an Autopilot device?
    Set-UIBusy -Busy $true -Message "Step 3/3: Checking Autopilot data..."

    $autopilotPatterns = @("*DeviceHash*", "*AutopilotDDSZTDFile*", "*CloudAssigned*",
        "*TpmHliInfo*", "*DiagnosticLogCSP*Autopilot*", "*MDMDiagReport*")
    $foundFiles = @()
    foreach ($pat in $autopilotPatterns) {
        $m = $extractedFiles | Where-Object { $_.Name -like $pat }
        if ($m) { $foundFiles += $m.Name }
    }

    $regAutopilot = Test-Path "HKLM:\SOFTWARE\Microsoft\Provisioning\Diagnostics\Autopilot" -ErrorAction SilentlyContinue

    if ($foundFiles.Count -eq 0 -and -not $regAutopilot) {
        $answer = [System.Windows.Forms.MessageBox]::Show(
            "This device does not appear to be Autopilot-enrolled.`n`n" +
            "No Autopilot indicators found in the exported logs.`n" +
            "No Autopilot registry keys detected.`n`n" +
            "Extracted $($extractedFiles.Count) file(s) to:`n$extractTo`n`n" +
            "Do you want to analyze anyway?",
            "Autopilot Not Detected",
            [System.Windows.Forms.MessageBoxButtons]::YesNo,
            [System.Windows.Forms.MessageBoxIcon]::Warning
        )
        if ($answer -eq [System.Windows.Forms.DialogResult]::No) {
            Set-UIBusy -Busy $false -Message ([char]0x26A0 + " Not an Autopilot device. Files saved in $extractTo")
            $lblStatus.ForeColor = $colWarning
            $textBox.Text = $extractTo
            return
        }
    }

    # Ready for analysis - select Autopilot Community (best for exported Autopilot data)
    $comboScript.SelectedIndex = 2
    $textBox.Text = $extractTo

    $statusMsg = [char]0x2713 + " $($extractedFiles.Count) files extracted"
    if ($foundFiles.Count -gt 0) { $statusMsg += " (Autopilot data found)" }
    $statusMsg += " - click Analyze Path"
    Set-UIBusy -Busy $false -Message $statusMsg
    $lblStatus.ForeColor = $colSuccess
}

#########################################################################################
# Button Events
#########################################################################################
$btnAnalyzeFolder.add_Click({
        $path = $textBox.Text.Trim()
        if ([string]::IsNullOrWhiteSpace($path) -or -not (Test-Path $path)) {
            [System.Windows.Forms.MessageBox]::Show(
                "Please enter or drag & drop a valid folder, ZIP, or CAB file first.",
                "No Input",
                [System.Windows.Forms.MessageBoxButtons]::OK,
                [System.Windows.Forms.MessageBoxIcon]::Information
            )
            return
        }
        Start-Analysis -InputPath $path
    })

$btnAnalyzePC.add_Click({
        Start-LocalAnalysis
    })

$btnExportLogs.add_Click({
        Start-ExportLogs
    })

#########################################################################################
# Show Form
#########################################################################################
[System.Windows.Forms.Application]::EnableVisualStyles()
$form.ShowDialog() | Out-Null