Intune-Diag.ps1
|
<#PSScriptInfo
.VERSION 2.0 .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 Errorhandling and loader, Added button for local analysis 2026-02-24, 2.0: UI overhaul, added script selector dropdown for multiple diagnostic tools (IME Diagnostics, Autopilot Diagnostics, Autopilot Diagnostics Community) #> <# .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" # Script definitions: Name, display label, script file name, and how to pass folder/file args $DiagScripts = @( @{ Name = "Get-IntuneManagementExtensionDiagnostics" Label = "IME Diagnostics (Petri Paavola)" FileName = "Get-IntuneManagementExtensionDiagnostics.ps1" FolderArg = '-LogFilesFolder "{0}"' # accepts folder path FileArg = '-LogFilesFolder "{0}"' # ZIP extracted to folder LocalArg = '' # no args = local }, @{ Name = "Get-AutopilotDiagnostics" Label = "Autopilot Diagnostics (Michael Niehaus)" FileName = "Get-AutopilotDiagnostics.ps1" FolderArg = '-ZIPFile "{0}"' # accepts ZIP/CAB file FileArg = '-ZIPFile "{0}"' # pass ZIP directly LocalArg = '' # no args = local }, @{ Name = "Get-AutopilotDiagnosticsCommunity" Label = "Autopilot Diagnostics Community (Andrew Taylor)" FileName = "Get-AutopilotDiagnosticsCommunity.ps1" FolderArg = '-File "{0}"' # accepts ZIP/CAB file FileArg = '-File "{0}"' # pass ZIP directly LocalArg = '' # no args = local } ) ######################################################################################### # 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 instead of the PowerShell 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 { # Silently ignore on older systems } # ---------- Color Palette ---------- $colBg = [System.Drawing.Color]::FromArgb(30, 30, 30) $colSurface = [System.Drawing.Color]::FromArgb(45, 45, 48) $colAccent = [System.Drawing.Color]::FromArgb(0, 120, 212) # Microsoft blue $colAccentHover = [System.Drawing.Color]::FromArgb(16, 138, 230) $colSuccess = [System.Drawing.Color]::FromArgb(16, 185, 129) $colWarning = [System.Drawing.Color]::FromArgb(245, 158, 11) $colError = [System.Drawing.Color]::FromArgb(239, 68, 68) $colTextPrimary = [System.Drawing.Color]::White $colTextMuted = [System.Drawing.Color]::FromArgb(160, 160, 160) $colInputBg = [System.Drawing.Color]::FromArgb(60, 60, 64) $colBorder = [System.Drawing.Color]::FromArgb(70, 70, 74) # ---------- Fonts ---------- $fontTitle = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold) $fontNormal = New-Object System.Drawing.Font("Segoe UI", 9.5) $fontSmall = New-Object System.Drawing.Font("Segoe UI", 8.5) $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(520, 410) $form.StartPosition = "CenterScreen" $form.FormBorderStyle = "FixedSingle" $form.MaximizeBox = $false $form.BackColor = $colBg $form.ForeColor = $colTextPrimary $form.Font = $fontNormal # ---------- Title Bar Area ---------- $panelHeader = New-Object System.Windows.Forms.Panel $panelHeader.Dock = "Top" $panelHeader.Height = 64 $panelHeader.BackColor = $colSurface $form.Controls.Add($panelHeader) # Logo from web (cached locally) $logoSize = 44 $logoPadLeft = 14 $logoPadTop = 10 $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 { # Use cached logo if available, otherwise download 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) } # Set as taskbar / title bar icon $iconBmp = New-Object System.Drawing.Bitmap($picLogo.Image, 32, 32) $hIcon = $iconBmp.GetHicon() $form.Icon = [System.Drawing.Icon]::FromHandle($hIcon) } catch { # Silently ignore – logo is decorative only } $panelHeader.Controls.Add($picLogo) $textLeft = $logoPadLeft + $logoSize + 10 $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, 10) $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), 38) $panelHeader.Controls.Add($lblSubtitle) # ---------- Script Selector ---------- $lblScript = New-Object System.Windows.Forms.Label $lblScript.Text = "Diagnostic Script" $lblScript.Font = $fontSmall $lblScript.ForeColor = $colTextMuted $lblScript.Location = New-Object System.Drawing.Point(20, 78) $lblScript.Size = New-Object System.Drawing.Size(460, 18) $form.Controls.Add($lblScript) $comboScript = New-Object System.Windows.Forms.ComboBox $comboScript.Location = New-Object System.Drawing.Point(20, 98) $comboScript.Size = New-Object System.Drawing.Size(460, 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 $form.Controls.Add($comboScript) # ---------- Availability Indicator ---------- $lblAvail = New-Object System.Windows.Forms.Label $lblAvail.Font = $fontSmall $lblAvail.Location = New-Object System.Drawing.Point(20, 130) $lblAvail.Size = New-Object System.Drawing.Size(460, 18) $form.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" $lblAvail.ForeColor = $colSuccess } else { $lblAvail.Text = [char]0x2717 + " Script not found - will attempt download on run" $lblAvail.ForeColor = $colWarning } } $comboScript.add_SelectedIndexChanged({ Update-Availability }) Update-Availability # ---------- Path Input ---------- $lblPath = New-Object System.Windows.Forms.Label $lblPath.Text = "Log Folder or ZIP/CAB File (drag & drop supported)" $lblPath.Font = $fontSmall $lblPath.ForeColor = $colTextMuted $lblPath.Location = New-Object System.Drawing.Point(20, 160) $lblPath.Size = New-Object System.Drawing.Size(460, 18) $form.Controls.Add($lblPath) $textBox = New-Object System.Windows.Forms.TextBox $textBox.Location = New-Object System.Drawing.Point(20, 180) $textBox.Size = New-Object System.Drawing.Size(380, 28) $textBox.AllowDrop = $true $textBox.BackColor = $colInputBg $textBox.ForeColor = $colTextPrimary $textBox.BorderStyle = "FixedSingle" $textBox.Font = $fontNormal $form.Controls.Add($textBox) # Browse button $btnBrowse = New-Object System.Windows.Forms.Button $btnBrowse.Text = "..." $btnBrowse.Location = New-Object System.Drawing.Point(405, 179) $btnBrowse.Size = New-Object System.Drawing.Size(75, 28) $btnBrowse.FlatStyle = "Flat" $btnBrowse.BackColor = $colSurface $btnBrowse.ForeColor = $colTextPrimary $btnBrowse.Font = $fontButton $btnBrowse.FlatAppearance.BorderColor = $colBorder $btnBrowse.Cursor = [System.Windows.Forms.Cursors]::Hand $form.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 } }) # ---------- 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 ---------- $btnAnalyzeFolder = New-Object System.Windows.Forms.Button $btnAnalyzeFolder.Text = "Analyze Path" $btnAnalyzeFolder.Location = New-Object System.Drawing.Point(20, 225) $btnAnalyzeFolder.Size = New-Object System.Drawing.Size(220, 40) $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) # Hover effect $btnAnalyzeFolder.add_MouseEnter({ $this.BackColor = $colAccentHover }) $btnAnalyzeFolder.add_MouseLeave({ $this.BackColor = $colAccent }) $btnAnalyzePC = New-Object System.Windows.Forms.Button $btnAnalyzePC.Text = "Analyze This PC" $btnAnalyzePC.Location = New-Object System.Drawing.Point(260, 225) $btnAnalyzePC.Size = New-Object System.Drawing.Size(220, 40) $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 = $colBorder }) $btnAnalyzePC.add_MouseLeave({ $this.BackColor = $colSurface }) # ---------- Status ---------- $lblStatus = New-Object System.Windows.Forms.Label $lblStatus.Location = New-Object System.Drawing.Point(20, 280) $lblStatus.Size = New-Object System.Drawing.Size(460, 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(20, 302) $progressBar.Size = New-Object System.Drawing.Size(460, 6) $progressBar.Style = "Marquee" $progressBar.MarqueeAnimationSpeed = 30 $progressBar.Visible = $false $form.Controls.Add($progressBar) # ---------- Footer ---------- $lblFooter = New-Object System.Windows.Forms.LinkLabel $lblFooter.Text = "scloud | v2.0" $lblFooter.Font = $fontSmall $lblFooter.LinkColor = [System.Drawing.Color]::FromArgb(100, 100, 100) $lblFooter.ActiveLinkColor = $colAccent $lblFooter.VisitedLinkColor = [System.Drawing.Color]::FromArgb(100, 100, 100) $lblFooter.DisabledLinkColor = [System.Drawing.Color]::FromArgb(80, 80, 80) $lblFooter.TextAlign = "MiddleCenter" $lblFooter.Dock = "Bottom" $lblFooter.Height = 30 $lblFooter.LinkArea = New-Object System.Windows.Forms.LinkArea(0, 6) # only "scloud" is clickable $lblFooter.add_LinkClicked({ Start-Process "https://scloud.work/Intune-Diag" }) $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 $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 ZIP extraction for IME Diagnostics (it expects a folder, not a ZIP) if ($scriptDef.Name -eq "Get-IntuneManagementExtensionDiagnostics" -and $InputPath -match '\.(zip)$') { $extractPath = Join-Path $env:TEMP "IntuneDiag_$(Get-Random)" try { Expand-Archive -Path $InputPath -DestinationPath $extractPath -Force $logTarget = $extractPath } catch { [System.Windows.Forms.MessageBox]::Show( "Error extracting ZIP file:`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 -eq "Get-IntuneManagementExtensionDiagnostics") { # IME Diagnostics always uses -LogFilesFolder (folder path) $argString = $scriptDef.FolderArg -f $logTarget } elseif ($isFile) { # Autopilot scripts: pass file directly with -File / -ZIPFile $argString = $scriptDef.FileArg -f $logTarget } else { # If a folder was given but the Autopilot script expects a file, # look for a .zip or .cab inside $archiveFile = Get-ChildItem -Path $logTarget -Include *.zip, *.cab -Recurse -File | Select-Object -First 1 if ($archiveFile) { $argString = $scriptDef.FileArg -f $archiveFile.FullName } else { # Fall back: try passing the folder anyway $argString = $scriptDef.FolderArg -f $logTarget } } try { $psArgs = "-ExecutionPolicy Bypass -File `"$scriptPath`" $argString" Start-Process -FilePath "powershell.exe" -ArgumentList $psArgs -NoNewWindow -Wait Set-UIBusy -Busy $false -Message "Analysis completed successfully." $lblStatus.ForeColor = $colSuccess } catch { Set-UIBusy -Busy $false -Message "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 { $psArgs = "-ExecutionPolicy Bypass -File `"$scriptPath`" $($scriptDef.LocalArg)" Start-Process -FilePath "powershell.exe" -ArgumentList $psArgs.Trim() -NoNewWindow -Wait Set-UIBusy -Busy $false -Message "Local analysis completed successfully." $lblStatus.ForeColor = $colSuccess } catch { Set-UIBusy -Busy $false -Message "Local analysis encountered an error." $lblStatus.ForeColor = $colError } Update-Availability } ######################################################################################### # 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 }) ######################################################################################### # Show Form ######################################################################################### $form.ShowDialog() | Out-Null |