Oct 04 2021 01:47 AM
I have a Microsoft 365 tenant with more than 1000 users in it. I need to get a list of users who have not been active for the last 180 days but are assigned with licenses. The goal is to check if users left the company but never removed their licenses although inactive, which is a waste of money. Please advice.
Oct 04 2021 01:54 AM
The link below should be the perfect answer:
Oct 04 2021 02:08 AM
Oct 04 2021 03:09 AM
SolutionOct 04 2021 11:00 PM
Oct 05 2021 12:34 AM
Oct 05 2021 02:04 AM
Oct 05 2021 02:28 AM
Oct 05 2021 05:55 AM
Sep 02 2022 06:54 AM
@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....
Sep 14 2022 02:23 PM
Mar 17 2023 09:46 AM
Apr 12 2023 03:55 PM
@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%2..." -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 = 10
do {
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
Apr 12 2023 05:22 PM
Apr 13 2023 10:59 AM
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.
Apr 27 2023 07:22 AM
Apr 27 2023 07:35 AM
Apr 27 2023 12:41 PM
Apr 28 2023 03:03 AM
@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?
Apr 28 2023 12:01 PM - edited Apr 28 2023 12:22 PM
@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.
Oct 04 2021 03:09 AM
Solution