How to Retrieve Office 365 Login History and Failed Attempts via PowerShell and Graph API


33 views

Office 365 maintains comprehensive login records through its Unified Audit Log. Unlike consumer Outlook.com which displays basic login history in UI, enterprise tenants require explicit configuration and querying through admin tools.

Before querying logs, ensure:

# Connect to Exchange Online
Connect-ExchangeOnline -UserPrincipalName admin@yourdomain.com

# Verify audit logging is enabled (default: ON after 2019)
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled

The most efficient method uses the Exchange Online Management module:

# Get all logins for specific user (last 90 days)
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-90) -EndDate (Get-Date) 
    -UserIds "user@domain.com" -Operations UserLoggedIn -ResultSize 1000

# Filter failed attempts
Search-UnifiedAuditLog -Operations UserLoginFailed -SessionCommand ReturnNextPreviewPage

For programmatic access to granular details:

// GET https://graph.microsoft.com/v1.0/auditLogs/signIns
// With OAuth2 token
{
  "filter": "createdDateTime gt 2023-01-01T00:00:00Z",
  "orderby": "createdDateTime desc",
  "top": 500
}
Field Description
createdDateTime Precise login timestamp (UTC)
userPrincipalName Authenticated account
ipAddress Client IP with geo-location
status/errorCode Success=0, Failure codes
deviceDetail OS, browser, and client app

For continuous monitoring, implement this PowerShell workflow:

# Scheduled task to export suspicious logins
$alertParams = @{
    StartDate = (Get-Date).AddHours(-24)
    Operations = "UserLoginFailed","UserLoggedIn"
    ResultSize = 5000
}
$riskyLogins = Search-UnifiedAuditLog @alertParams | 
    Where-Object { $_.ClientIP -notmatch "192.168.|10." }

$riskyLogins | Export-Csv -Path "C:\Security\SuspiciousLogins_$(Get-Date -f yyyyMMdd).csv"

Note that full audit capabilities require:

  • Azure AD Premium P1/P2 for Graph API access
  • Office 365 E3/E5 for extended retention (90 days vs 7 days)

Office 365 (now Microsoft 365) provides comprehensive sign-in logging capabilities through the Azure AD audit logs. While the consumer Outlook.com interface shows basic login history, enterprise Office 365 tenants require administrative tools to access this data programmatically.

The simplest way to view login history is through the Azure AD portal:


1. Navigate to portal.azure.com
2. Go to Azure Active Directory → Monitoring → Sign-in logs
3. Apply filters for:
   - UserPrincipalName
   - Application
   - IP address ranges
   - Status (success/failure)

For programmatic access, use the AzureAD or MSOnline module:


# Connect to Azure AD
Connect-AzureAD

# Retrieve sign-ins for specific user
Get-AzureADAuditSignInLogs -Filter "userPrincipalName eq 'user@domain.com'"

# Export last 30 days of failed logins
$startDate = (Get-Date).AddDays(-30)
Get-AzureADAuditSignInLogs -Filter "createdDateTime gt $startDate and status/errorCode eq 50126" | 
Export-Csv "FailedLogins.csv" -NoTypeInformation

For advanced integration, use Microsoft Graph API:


// Get sign-ins with JavaScript
const { Client } = require("@microsoft/microsoft-graph-client");
const { TokenCredentialAuthenticationProvider } = require("@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials");
const { ClientSecretCredential } = require("@azure/identity");

const credential = new ClientSecretCredential(
  "tenant-id",
  "client-id",
  "client-secret"
);

const authProvider = new TokenCredentialAuthenticationProvider(credential, {
  scopes: ["https://graph.microsoft.com/.default"]
});

const client = Client.initWithMiddleware({ authProvider });

async function getSignIns(userId) {
  return await client
    .api(/auditLogs/signIns?$filter=userId eq '${userId}')
    .get();
}

Key indicators to monitor include:

  • Logins from unusual locations (Conditional Access logs)
  • Multiple failed attempts followed by success
  • Sign-ins at unusual hours
  • Impossible travel scenarios

Create Azure Monitor alerts for critical events:


# Azure CLI command to create alert rule
az monitor scheduled-query create \
  --name "MultipleFailedLogins" \
  --resource-group "SecOps-RG" \
  --scopes "/subscriptions/{sub-id}/resourceGroups/{rg}/providers/microsoft.insights/components/{app-insights}" \
  --condition "count 'AzureActivity' | where OperationName == 'Sign-in activity' and ResultType == '50126' | summarize FailedCount=count() by UserPrincipalName bin(1h) | where FailedCount >= 5" \
  --description "Alert when user has 5+ failed logins within 1 hour"

Be aware of default retention periods:

  • Azure AD Free: 7 days
  • Azure AD P1: 30 days
  • Azure AD P2: 30 days (extendable with diagnostic settings)