Managing user activity within enterprise environments like Microsoft 365 is crucial for maintaining operational efficiency and security. Administrators need effective tools to monitor user engagement and quickly identify inactive Microsoft 365 accounts that may pose security risks or inflate licensing costs. This article covers necessary prerequisites and provides a step-by-step tutorial on how to use Microsoft Graph PowerShell commands to fetch and analyze inactive user data.
Prerequisites
Install the Microsoft Graph PowerShell SDK. You can install it using the following command if you haven't already:
You need administrative credentials to access and modify user licenses.
Graph PowerShell Script for Finding Inactive Users in Microsoft 365
# Interactive login Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All" # Define the inactivity threshold (e.g., 30 days) $inactivityThreshold = (Get-Date).AddDays(-30) Write-Host "Inactivity Threshold set to: $inactivityThreshold" # Fetch users and explicitly request SignInActivity property $users = Get-MgUser -All -Property "displayName, userPrincipalName, signInActivity" | Where-Object { Write-Host "Checking user: $($_.DisplayName) with Last Sign-In: $($_.SignInActivity.LastSignInDateTime)" if ($_.SignInActivity -ne $null -and $_.SignInActivity.LastSignInDateTime -ne $null) { $lastSignIn = [datetime]::MinValue if ([datetime]::TryParse($_.SignInActivity.LastSignInDateTime, [ref]$lastSignIn)) { return $lastSignIn -lt $inactivityThreshold } } return $false } # Check if users were found if ($users.Count -gt 0) { Write-Host "Found $($users.Count) inactive users." # Output the inactive users $users | Select-Object DisplayName, UserPrincipalName, @{Name="LastSignInDateTime"; Expression={$_.SignInActivity.LastSignInDateTime}} } else {
Write-Host "No inactive users found."
}# Disconnect from Microsoft GraphDisconnect-MgGraph
How the Script Works?
Connect to Microsoft Graph
Connect-MgGraph: This command is used to establish a connection to Microsoft Graph..
-Scopes "User.Read.All", "AuditLog.Read.All": Specifies the permissions needed for the script to run. User.Read.All allows the script to read basic profile information of all users. AuditLog.Read.All is typically required to access detailed sign-in activity, which is necessary to determine user inactivity or inactive users.
Define the Inactivity Threshold
$inactivityThreshold = (Get-Date).AddDays(-30)Write-Host "Inactivity Threshold set to: $inactivityThreshold"
Get-Date: Fetches the current date and time.
AddDays(-30): Adjusts the current date to 30 days ago. This is used to define the "inactivity threshold"; users who have not signed in since this date are considered inactive.
Write-Host:Outputs a message to the console showing what the inactivity threshold is set to.
Fetch Users and Explicitly Request SignInActivity Property
Get-MgUser -All -Property "displayName, userPrincipalName, signInActivity": Retrieves all users from Microsoft 365 and specifically requests the displayName, userPrincipalName, and signInActivity properties.
Where-Object: Filters the users based on a condition
Inside the Where-Object block, it first prints out each user's name and last sign-in date.
Checks if the SignInActivity and its LastSignInDateTime are not null.
Tries to parse the LastSignInDateTimeinto a [datetime] object ($lastSignIn).
If successful, checks if this date is earlier than the inactivityThreshold. If it is, the user is considered inactive, and the user object is returned.
Check If Users Were Found
if ($users.Count -gt 0) { Write-Host "Found $($users.Count) inactive users."# Output the inactive users $users | Select-Object DisplayName, UserPrincipalName, @{Name="LastSignInDateTime"; Expression={$_.SignInActivity.LastSignInDateTime}}
}else{ Write-Host "No inactive users found."
}
This conditional statement checks if any users were returned from the filter
If yes, it prints the number of inactive users found and then lists each user's display name, principal name, and last sign-in date.
If no users meet the criteria, it prints "No inactive users found."
Disconnect From Microsoft Graph
Disconnect-MgGraph: This command cleanly disconnects the PowerShell session from Microsoft Graph and helps to clear any authentication tokens or session information.
Running the Script
Navigate to the location where you have placed the script file and run the file with a ./<your-file-name>.ps1 command.
The script prompts you to sign in if you are not signed into Microsoft 365 already.
The script checks for inactive users (based on the set inactivity threshold) and lists them out.
Errors You Might Face
Permissions not available: Make sure that the required permissions (User.ReadWrite.All, Directory.ReadWrite.All ) are not only added in the Azure portal under your app registration but also have been granted admin consent, especially in organizational contexts.
Not running PowerShell as administrator: Ensure you always run the PowerShell as an administrator.
Execution policy set to restricted: If execution policy is set to restricted, then you cannot execute scripts. Execute Get-ExecutionPolicy cmdlet to find out the current execution policy. Your execution policy should be set to RemoteSigned. To set execution policy to RemoteSigned, execute the following command: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser