Forum Discussion
Get list of inactive users with licenses assigned in Microsoft 365
- Oct 04, 2021To be more precise you could:
1/ Get all the users with licenses assigned
The most simple solution is: Get-MsolUser -All | where {$_.isLicensed -eq $true}
To go further, I use several custom scripts :
- To get a list of the licenses assigned to a user (ex : Office 365 E3) : https://github.com/thijoubert/Sharing/blob/main/PowerShell/AAD_Licenses_Users.ps1
- To get a full list of the SKU assigned to a user (ex : SharePoint Online P2) : https://github.com/thijoubert/Sharing/blob/main/PowerShell/AAD_Licenses_Users_Details.ps1
2/ Get the last sign-in date for you users:
The most simple solution : https://docs.microsoft.com/en-us/azure/active-directory/reports-monitoring/howto-manage-inactive-user-accounts
You could use Graph explorer with an admin account
3/ Cross-reference the two lists to get your answer
d-rajapaksaAzure AD is an enterprise product. How can it be that a page/list of inactive users is not available in the interface, by default? It's actually mindblowing....
Pretty user interface and prebuilt reports are second class user cases.
- rayk_slandMar 17, 2023Copper Contributorpretty snooty reply. but it's nonsense. As a charity user I get hounded to make sure my users are all active, that I must de-license the inactive ones, but microsoft provides no easy report to tell me which those are. non-usability is not an 'enterprise' feature. It's a stupid feature.
- DJP1973Apr 13, 2023Copper Contributor
Its not snooty at all, its a fact man. That's how the cloud is done. API automation first, used GUI second or not at all. Some more experience with Azure / Cloud will teach you that.
- richardgnzApr 12, 2023Copper Contributor
rayk_sland I use the following powershell code to get info of licensed users who have not logged in for at least 30 days.
# The function of this script is to return all users that have license's and have been inactive in AzureAD sigin logs for x amount of days.
# For customers who have Azure AD Premium P2 subscriptions, the sign-in logs are retained for 30 days.
# By default, Azure AD retains sign-in logs for 30 days, but the retention period can be increased up to two years by using Azure Monitor and Storage accounts# The command Get-AzureADAuditSignInLogs often gets throttled by Microsoft so there is logic in the script to pause when this is detected and try again.
# This script requires install-module AzureADPreview -force
# Import-module azureadpreview# You might need to Uninstall-Module AzureAD first to make sure Get-AzureADAuditSignInLogs is a command that you can useā¦.
# Connect-AzureAD# Download the CSV file containing the product name and SkuPartNumber mappings
Invoke-WebRequest -Uri "https://download.microsoft.com/download/e/3/e/e3e9faf2-f28b-490a-9ada-c6089a1fc5b0/Product%20names%20and%20service%20plan%20identifiers%20for%20licensing.csv" -OutFile "c:\temp\product_names.csv"# Import the CSV file into a variable and create a dictionary using the String_Id as key and Product_Display_Name as value
$productNames = Import-Csv "c:\temp\product_names.csv"
$skuDict = @{}
foreach($product in $productNames)
{
$skuDict[$product.String_Id] = $product.Product_Display_Name
}$DaysInactive = 30
$InactiveDate = (Get-Date).AddDays(-$DaysInactive)$AllUsers = Get-AzureADUser -All $true
$InactiveUsers = @()foreach ($User in $AllUsers) {
$LicenseDetail = Get-AzureADUserLicenseDetail -ObjectId $User.ObjectId
$License = ($LicenseDetail | ForEach-Object { $skuDict[$_.SkuPartNumber] }) -join ", "Write-Output "Checking $($User.UserPrincipalName) with License: $License"
if ($LicenseDetail -ne $null) {
$RetryCount = 0
$MaxRetries = 5
$RetryDelayInSeconds = 10do {
try {
Write-Output "Getting sign-in logs for $($User.DisplayName)"
$AllSignInLogs = Get-AzureADAuditSignInLogs -Filter "UserDisplayName eq '$($User.DisplayName)'"
#Write-Output "All sign-in logs:"
#Write-Output $AllSignInLogs$SignIn = $AllSignInLogs | Sort-Object -Property CreatedDateTime -Descending | Select-Object -First 1
Write-Output "Most recent sign-in log entry:"
Write-Output $SignIn.CreatedDateTime
$ErrorOccured = $false
}
catch {
$ErrorMessage = $_.Exception.Message
if ($ErrorMessage.Contains("Too Many Requests")) {
Write-Output "Throttling detected. Waiting for $($RetryDelayInSeconds) seconds before retrying..."
Start-Sleep -Seconds $RetryDelayInSeconds
$ErrorOccured = $true
$RetryCount++
}
else {
Write-Output "Error occurred: $ErrorMessage"
$ErrorOccured = $false
}
}
} while ($ErrorOccured -eq $true -and $RetryCount -lt $MaxRetries)if ($SignIn -eq $null -or $SignIn.CreatedDateTime -lt $InactiveDate) {
$InactiveUsers += [PSCustomObject]@{
DisplayName = $User.DisplayName
UserPrincipalName = $User.UserPrincipalName
LastSignin = $Signin
Enabled = $User.AccountEnabled
License = $License
}
write-output 'InactiveUser! '$User.DisplayName $License
}
}
}$InactiveUsers | Export-Csv -Path "c:\temp\InactiveUsers.csv" -NoTypeInformation
- Osiris1910Apr 27, 2023Copper ContributorThis works really well, except its not getting any data in the 'LastSignin' column. Could this be something that is not enabled in my tenant. I see the following error showing up often in the PS window when running this script
Getting sign-in logs for Joe Bloggs
Error occurred: The term 'Get-AzureADAuditSignInLogs' is not recognized as the name of a cmdlet