# Show-IntuneDeviceQR.ps1 - Standalone version of the QR-code panel from # Monitor-IntuneProgress.ps1, extracted for use at sites that only need the # device-ID display (no DSC monitoring, no Intune sync triggers, no reboot # orchestration). # # Polls dsregcmd /status every 15s until the device reports an Azure AD # DeviceId, then renders that ID as a half-block QR code in the console # along with the literal text. Stays open until the user presses a key. # # REQUIREMENTS # - Windows PowerShell 5.1 (or PS 7) with .NET Framework available # - QRCoder.dll alongside this script (or at one of the fallback paths # below). Single ~75 KB native dependency, no internet needed. # # USAGE # powershell.exe -NoProfile -ExecutionPolicy Bypass -File Show-IntuneDeviceQR.ps1 # # Or double-click via a one-line .bat: # powershell.exe -NoProfile -ExecutionPolicy Bypass -NoExit -File "%~dp0Show-IntuneDeviceQR.ps1" [CmdletBinding()] param( [int]$PollSeconds = 15, [string]$QRCoderDllPath = '' ) $ErrorActionPreference = 'Continue' # ---------------------------------------------------------------------------- # Locate QRCoder.dll. Search order: # 1. -QRCoderDllPath if explicitly passed # 2. Next to this script ($PSScriptRoot\QRCoder.dll) # 3. Current working directory # 4. WJ canonical staging path (kept for cross-site portability) # ---------------------------------------------------------------------------- function Resolve-QRCoderDll { param([string]$Override) $candidates = @() if ($Override) { $candidates += $Override } if ($PSScriptRoot) { $candidates += (Join-Path $PSScriptRoot 'QRCoder.dll') } $candidates += (Join-Path (Get-Location) 'QRCoder.dll') $candidates += 'C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll' foreach ($c in $candidates) { if ($c -and (Test-Path -LiteralPath $c)) { return (Resolve-Path -LiteralPath $c).Path } } return $null } # ---------------------------------------------------------------------------- # Get the AAD DeviceId from dsregcmd. Returns $null until the device is # Azure AD joined AND dsregcmd has populated the field. # ---------------------------------------------------------------------------- function Get-AadDeviceId { try { $dsreg = dsregcmd /status 2>&1 } catch { return $null } $line = $dsreg | Select-String 'DeviceId' | Select-Object -First 1 if (-not $line) { return $null } $parts = $line.ToString().Split(':') if ($parts.Count -lt 2) { return $null } $id = $parts[1].Trim() if ([string]::IsNullOrWhiteSpace($id)) { return $null } return $id } # ---------------------------------------------------------------------------- # Half-block QR renderer. Each output character is 1 module wide, 2 modules # tall, using: # U+2588 = both top and bottom modules set # U+2580 = top only # U+2584 = bottom only # space = neither # Cuts QR height in half vs full-block rendering. Adds a 4-module quiet # zone manually since QRCoder's ModuleMatrix excludes it by default. # ---------------------------------------------------------------------------- function Format-QrHalfBlocks { param([string]$Payload, [string]$DllPath, [int]$IndentSpaces = 8) Add-Type -Path $DllPath $gen = New-Object QRCoder.QRCodeGenerator $data = $gen.CreateQrCode($Payload, [QRCoder.QRCodeGenerator+ECCLevel]::L) $matrix = $data.ModuleMatrix $size = $matrix.Count $pad = 4 $total = $size + 2 * $pad $upper = [char]0x2580 $lower = [char]0x2584 $full = [char]0x2588 $left = ' ' * $IndentSpaces $lines = New-Object System.Collections.Generic.List[string] for ($y = 0; $y -lt $total; $y += 2) { $sb = New-Object System.Text.StringBuilder [void]$sb.Append($left) for ($x = 0; $x -lt $total; $x++) { $mx = $x - $pad $my1 = $y - $pad $my2 = $y + 1 - $pad $top = ($my1 -ge 0 -and $my1 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my1].Get($mx)) $bot = ($my2 -ge 0 -and $my2 -lt $size -and $mx -ge 0 -and $mx -lt $size -and $matrix[$my2].Get($mx)) if ($top -and $bot) { [void]$sb.Append($full) } elseif ($top) { [void]$sb.Append($upper) } elseif ($bot) { [void]$sb.Append($lower) } else { [void]$sb.Append(' ') } } $lines.Add($sb.ToString()) } return $lines } # ---------------------------------------------------------------------------- # Bigger console so the QR + text frame fits in one viewport. # ---------------------------------------------------------------------------- try { $rui = $Host.UI.RawUI $maxH = $rui.MaxPhysicalWindowSize.Height $targetWindow = [Math]::Min(58, [int]$maxH) $targetBuffer = [Math]::Max($targetWindow, 200) $bs = $rui.BufferSize if ($bs.Height -lt $targetBuffer) { $bs.Height = $targetBuffer; $rui.BufferSize = $bs } $ws = $rui.WindowSize if ($ws.Height -lt $targetWindow) { $ws.Height = $targetWindow; $rui.WindowSize = $ws } } catch {} # ---------------------------------------------------------------------------- # Wait for QRCoder.dll # ---------------------------------------------------------------------------- $dllPath = Resolve-QRCoderDll -Override $QRCoderDllPath # Mark-of-the-Web: when files arrive via SMB / zip download / email, # Windows attaches a Zone.Identifier alternate data stream that flags the # file as "from another computer". Add-Type then refuses to load the DLL # with "Operation is not supported" / "Could not load file or assembly". # Unblock-File silently strips the ADS. Best-effort: errors are non-fatal # (file may already be unblocked, or the user may not have write access). if ($dllPath) { try { Unblock-File -LiteralPath $dllPath -ErrorAction SilentlyContinue } catch {} } if ($PSCommandPath) { try { Unblock-File -LiteralPath $PSCommandPath -ErrorAction SilentlyContinue } catch {} } if (-not $dllPath) { Write-Host "" Write-Host "ERROR: QRCoder.dll not found." -ForegroundColor Red Write-Host "Searched (in order):" if ($QRCoderDllPath) { Write-Host " - $QRCoderDllPath (-QRCoderDllPath)" } if ($PSScriptRoot) { Write-Host " - $(Join-Path $PSScriptRoot 'QRCoder.dll')" } Write-Host " - $(Join-Path (Get-Location) 'QRCoder.dll')" Write-Host " - C:\Enrollment\shopfloor-setup\Shopfloor\QRCoder.dll" Write-Host "" Write-Host "Drop QRCoder.dll alongside this script and re-run." -ForegroundColor Yellow exit 2 } # ---------------------------------------------------------------------------- # Poll loop - retry every $PollSeconds until DeviceId appears # ---------------------------------------------------------------------------- Write-Host "" Write-Host " Waiting for Azure AD device registration..." -ForegroundColor Cyan Write-Host " Polling dsregcmd every $PollSeconds s. Press Ctrl+C to abort." -ForegroundColor DarkGray Write-Host "" $attempt = 0 while ($true) { $attempt++ $deviceId = Get-AadDeviceId if ($deviceId) { break } Write-Host (" [{0}] attempt {1,3} no DeviceId yet, retrying in {2}s..." -f (Get-Date -Format 'HH:mm:ss'), $attempt, $PollSeconds) -ForegroundColor DarkGray Start-Sleep -Seconds $PollSeconds } # ---------------------------------------------------------------------------- # Render # ---------------------------------------------------------------------------- Clear-Host Write-Host "" Write-Host " GE Aerospace -- Intune Device ID" -ForegroundColor Cyan Write-Host " $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor DarkGray Write-Host "" Write-Host " Device ID: $deviceId" -ForegroundColor Green Write-Host "" try { $qrLines = Format-QrHalfBlocks -Payload $deviceId -DllPath $dllPath foreach ($l in $qrLines) { Write-Host $l } } catch { Write-Host " (QR generation failed: $($_.Exception.Message))" -ForegroundColor Red } Write-Host "" Write-Host " Scan with phone camera or Intune admin app." -ForegroundColor DarkGray Write-Host "" Write-Host " Press any key to close..." -ForegroundColor Yellow try { [void][Console]::ReadKey($true) } catch { [void](Read-Host) } exit 0