Public/Get-UserEverything.ps1
|
function Get-UserEverything { <# .SYNOPSIS Universal user lookup - returns everything about a user from AD, M365, Intune, and sign-in logs. .DESCRIPTION The main entry point for Admin-UserLookup. Takes any form of user identifier, resolves it, then queries Active Directory, Microsoft 365 (Graph), Intune, and optionally sign-in logs to build a complete user profile. If any subsystem is unavailable, it continues with warnings rather than failing entirely. Optionally generates a dark-themed HTML dashboard with all collected data. .PARAMETER Identity User identifier: SAMAccountName, UPN, email address, or display name. .PARAMETER OutputPath Optional file path for an HTML dashboard report. Parent directory will be created if it does not exist. .PARAMETER IncludeSignInHistory Include sign-in history and risk detections. Off by default because the audit log queries can be slower and require Entra ID P1/P2. .OUTPUTS PSCustomObject with sections: Identity, ADAccount, M365, Devices, SignIns, Metadata (query timestamps and errors). .EXAMPLE Get-UserEverything -Identity "jsmith" .EXAMPLE Get-UserEverything -Identity "john.smith@contoso.com" -OutputPath "C:\Reports\jsmith.html" -IncludeSignInHistory .EXAMPLE # Pipe to Format-List for quick console review Get-UserEverything -Identity "John Smith" | Format-List #> [CmdletBinding()] param( [Parameter(Mandatory, Position = 0, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [string]$Identity, [Parameter()] [string]$OutputPath, [Parameter()] [switch]$IncludeSignInHistory ) process { $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $errors = @() Write-Verbose "=== Get-UserEverything: Starting lookup for '$Identity' ===" # ------------------------------------------------------------------ # Step 1: Resolve identity # ------------------------------------------------------------------ $resolvedUser = $null try { $resolvedUser = Resolve-UserIdentity -Identity $Identity Write-Verbose "Resolved to: $($resolvedUser.SAMAccountName) / $($resolvedUser.UPN)" } catch { $errors += "Identity resolution failed: $($_.Exception.Message)" Write-Warning "Could not resolve identity '$Identity' in Active Directory: $($_.Exception.Message)" # If AD is unavailable, try using the Identity directly as UPN for cloud-only queries Write-Verbose "Attempting cloud-only lookup with '$Identity' as UPN" $resolvedUser = [PSCustomObject]@{ SAMAccountName = $null UPN = $Identity Mail = $Identity ObjectId = $null DisplayName = $Identity Enabled = $null } } # ------------------------------------------------------------------ # Step 2: AD details # ------------------------------------------------------------------ $adDetails = $null if ($resolvedUser.SAMAccountName) { try { Write-Verbose "Fetching AD details..." $adDetails = Get-UserADDetails -Identity $resolvedUser.SAMAccountName } catch { $errMsg = "AD lookup failed: $($_.Exception.Message)" $errors += $errMsg Write-Warning $errMsg } } else { $errors += "AD lookup skipped: SAMAccountName not available (cloud-only user or AD unavailable)" Write-Warning "Skipping AD lookup - no SAMAccountName resolved." } # ------------------------------------------------------------------ # Step 3: M365 details # ------------------------------------------------------------------ $m365Details = $null $upnForCloud = if ($resolvedUser.UPN) { $resolvedUser.UPN } elseif ($resolvedUser.Mail) { $resolvedUser.Mail } else { $Identity } try { Write-Verbose "Fetching M365 details for $upnForCloud..." $m365Details = Get-UserM365Details -Identity $upnForCloud } catch { $errMsg = "M365 lookup failed: $($_.Exception.Message)" $errors += $errMsg Write-Warning $errMsg } # ------------------------------------------------------------------ # Step 4: Devices # ------------------------------------------------------------------ $deviceDetails = $null try { Write-Verbose "Fetching device details for $upnForCloud..." $deviceDetails = Get-UserDevices -Identity $upnForCloud } catch { $errMsg = "Device lookup failed: $($_.Exception.Message)" $errors += $errMsg Write-Warning $errMsg } # ------------------------------------------------------------------ # Step 5: Sign-in history (optional) # ------------------------------------------------------------------ $signInDetails = $null if ($IncludeSignInHistory) { try { Write-Verbose "Fetching sign-in history for $upnForCloud..." $signInDetails = Get-UserSignInHistory -Identity $upnForCloud } catch { $errMsg = "Sign-in history lookup failed: $($_.Exception.Message)" $errors += $errMsg Write-Warning $errMsg } } else { Write-Verbose "Sign-in history skipped (use -IncludeSignInHistory to include)" } # ------------------------------------------------------------------ # Build consolidated result # ------------------------------------------------------------------ $stopwatch.Stop() $result = [PSCustomObject]@{ Identity = $resolvedUser ADAccount = $adDetails M365 = $m365Details Devices = $deviceDetails SignIns = $signInDetails Metadata = [PSCustomObject]@{ QueryTimestamp = Get-Date ElapsedSeconds = [math]::Round($stopwatch.Elapsed.TotalSeconds, 2) Errors = $errors SectionsLoaded = @( if ($adDetails) { 'ADAccount' } if ($m365Details) { 'M365' } if ($deviceDetails) { 'Devices' } if ($signInDetails) { 'SignIns' } ) } } # Add custom type name for formatting $result.PSObject.TypeNames.Insert(0, 'AdminUserLookup.UserEverything') # ------------------------------------------------------------------ # Generate HTML report if requested # ------------------------------------------------------------------ if ($OutputPath) { try { Write-Verbose "Generating HTML dashboard at: $OutputPath" $reportFile = New-HtmlDashboard -UserData $result -OutputPath $OutputPath Write-Host "HTML report saved to: $($reportFile.FullName)" -ForegroundColor Green } catch { $errMsg = "HTML report generation failed: $($_.Exception.Message)" $errors += $errMsg Write-Warning $errMsg } } # ------------------------------------------------------------------ # Summary output # ------------------------------------------------------------------ $sectionCount = $result.Metadata.SectionsLoaded.Count $totalSections = if ($IncludeSignInHistory) { 4 } else { 3 } if ($errors.Count -gt 0) { Write-Host "`nCompleted with $($errors.Count) warning(s). Loaded $sectionCount/$totalSections data sections in $($result.Metadata.ElapsedSeconds)s." -ForegroundColor Yellow } else { Write-Host "`nAll $sectionCount data sections loaded successfully in $($result.Metadata.ElapsedSeconds)s." -ForegroundColor Green } $result } } |