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
Thanks to richardgnz
# The function of this script is to return all users that have licences 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
# Install the required PowerShell modules
Install-Module -Name Microsoft.Graph.Authentication -Scope CurrentUser
Install-Module -Name Microsoft.Graph.Users -Scope CurrentUser
Install-Module -Name Microsoft.Graph.Reports -Scope CurrentUser
# Import the required modules
Import-Module Microsoft.Graph.Authentication
Import-Module Microsoft.Graph.Users
Import-Module Microsoft.Graph.Reports
# Connect to Microsoft Graph using delegated permissions
Connect-MgGraph -Scopes "User.Read.All, AuditLog.Read.All"
# 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 SkuPartNumber as key and Product_Display_Name as value
$productNames = Import-Csv "c:\temp\product_names.csv"
$skuDict = @{}
foreach($product in $productNames)
{
$skuDict[$product.GUID] = $product.Product_Display_Name
}
# Set the number of days of inactivity to consider
$DaysInactive = 30
$InactiveDate = (Get-Date).AddDays(-$DaysInactive)
# Retrieve all users using the Microsoft Graph API
$AllUsers = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, AccountEnabled
# Initialize an empty array to store inactive users
$InactiveUsers = @()
# Iterate through each user and check for inactivity in Azure AD sign-in logs
foreach ($User in $AllUsers) {
$LicenseDetail = Get-MgUserLicenseDetail -UserId $User.Id
if ($LicenseDetail -ne $null) {
$License = ($LicenseDetail | ForEach-Object { $skuDict[$_.SkuId] }) -join ", "
Write-Output "Checking $($User.userPrincipalName) with License: $License"
Write-Output "Getting sign-in logs for $($User.displayName)"
$AllSignInLogs = Get-MgAuditLogSignIn -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
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
Disconnect-MgGraph
- richardgnzApr 28, 2023Copper Contributor
Osiris1910 - Yeah same as you. The issue is with a standard Azure P2 license the signin data is only kept for 30 days. I do not have a tenant configured where this is stored for longer. So users when checked will have a null status for signin if more than 30 days since last login. If logs were configured to be stored longer then you would be able to get a more accurate picture but there would be a cost in doing this.
https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/concept-activity-logs-azure-monitor#:~:text=Storage%20size%20for%20activity%20logs,of%20data%20storage%20per%20day.
- Osiris1910Apr 28, 2023Copper Contributor
richardgnz Fab! I'll give the MSGraph version a run through today.
The AzureADPreview script ran successfully and only exported the accounts which had no sign-in activity for the last X days. What it didnt do was fill in any data in the CSV column 'LastSignIn' to be able to verify this. I could see the data was been captured and checked as the script ran, either showing the last sign-in if within the X day limit or stating 'Inactive User!'
Not sure if you have experienced the same with your csv results?