All scripts
Governance 891

Entra Sign-In Logs Export Report

Pulls Entra ID sign-in logs through Microsoft Graph for a chosen date range, flags failed and risky sign-ins, and exports the whole thing to a CSV you can hand off or review in Excel.

Export-EntraSignInLogsReport.ps1
<#
.SYNOPSIS
    Exports Entra ID sign-in logs to a CSV report, with a summary of failed and risky sign-ins.

.DESCRIPTION
    Connects to Microsoft Graph and pulls sign-in log entries for a chosen number of days.
    The script separates successful sign-ins from failed ones, flags entries marked as risky,
    and writes everything to a CSV file so you can review it in Excel or hand it to someone else.
    Useful for a quick security check, an access review, or just keeping an eye on unusual
    sign-in activity without opening the Entra portal every time.

.PARAMETER DaysBack
    How many days of sign-in history to pull. Defaults to 7. The Entra ID free tier only keeps
    7 days of sign-in logs, so anything beyond that needs a Premium license.

.PARAMETER OutputPath
    Folder to save the CSV report to. Defaults to the current folder.

.PARAMETER FailedOnly
    If set, only exports sign-ins that failed, skipping successful ones entirely.

.EXAMPLE
    .\Export-EntraSignInLogsReport.ps1 -DaysBack 14 -OutputPath C:\Reports

.EXAMPLE
    .\Export-EntraSignInLogsReport.ps1 -FailedOnly

.AUTHOR
    Shehryar Hassan
#>

[CmdletBinding()]
param(
    [int]$DaysBack = 7,
    [string]$OutputPath = ".",
    [switch]$FailedOnly
)

if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Reports)) {
    Write-Host "Installing Microsoft.Graph.Reports module..." -ForegroundColor Yellow
    Install-Module Microsoft.Graph.Reports -Scope CurrentUser -Force
}

Import-Module Microsoft.Graph.Reports
Import-Module Microsoft.Graph.Authentication

Write-Host "Connecting to Microsoft Graph..." -ForegroundColor Cyan
Connect-MgGraph -Scopes "AuditLog.Read.All", "Directory.Read.All" -NoWelcome

$startDate = (Get-Date).AddDays(-$DaysBack).ToString("yyyy-MM-ddTHH:mm:ssZ")
$filter = "createdDateTime ge $startDate"

Write-Host "Pulling sign-in logs for the last $DaysBack days, this can take a while for a busy tenant..." -ForegroundColor Cyan
$signIns = Get-MgAuditLogSignIn -Filter $filter -All

if (-not $signIns -or $signIns.Count -eq 0) {
    Write-Host "No sign-in log entries found for that period." -ForegroundColor Yellow
    return
}

$report = foreach ($entry in $signIns) {
    $isFailed = $entry.Status.ErrorCode -ne 0
    if ($FailedOnly -and -not $isFailed) { continue }

    [PSCustomObject]@{
        UserDisplayName   = $entry.UserDisplayName
        UserPrincipalName = $entry.UserPrincipalName
        AppDisplayName    = $entry.AppDisplayName
        CreatedDateTime   = $entry.CreatedDateTime
        IPAddress         = $entry.IPAddress
        City              = $entry.Location.City
        CountryOrRegion   = $entry.Location.CountryOrRegion
        Status            = if ($isFailed) { "Failed" } else { "Success" }
        FailureReason     = $entry.Status.FailureReason
        RiskLevel         = $entry.RiskLevelDuringSignIn
        RiskState         = $entry.RiskState
        ConditionalAccess = $entry.ConditionalAccessStatus
        ClientAppUsed     = $entry.ClientAppUsed
    }
}

$fileName = "EntraSignInLogs_{0}.csv" -f (Get-Date -Format "yyyyMMdd_HHmmss")
$fullPath = Join-Path -Path $OutputPath -ChildPath $fileName
$report | Export-Csv -Path $fullPath -NoTypeInformation

$failedCount = ($report | Where-Object { $_.Status -eq "Failed" }).Count
$riskyCount = ($report | Where-Object { $_.RiskState -and $_.RiskState -ne "none" }).Count

Write-Host ""
Write-Host "Report saved to: $fullPath" -ForegroundColor Green
Write-Host "Total entries: $($report.Count)" -ForegroundColor Green
Write-Host "Failed sign-ins: $failedCount" -ForegroundColor Yellow
Write-Host "Flagged as risky: $riskyCount" -ForegroundColor Yellow

Disconnect-MgGraph | Out-Null

Read it before you run it, and test in a safe tenant first.