Staled Azure AD users

Background

Have you ever thought “Do we have full controll over our offboarding routine?” then this script is perfect in your toolbox for checking for inactive users in Azure Active Directory.

I am stumbling across several Azure Active Directories where there are one or more user account enable and active without beeing in use by someone. That can be personal accounts not used anymore or it could be system accounts thats not in use.

In this post I`ll focus on a script where we get a output as a CSV file on when users last logged into their account.

Pre-requisite

Create a App Registration within your Azure AD! You can use this guide official Microsoft Learn guide
https://learn.microsoft.com/en-us/azure/healthcare-apis/register-application

Remember to take note from:
* Tenant ID
* Application ID
* Application Secret

And your newly created App Reg need a API permission
* AuditLog.Read.All
* User.Read.All

Script content

The script can be downloaded from my Github repostitory https://raw.githubusercontent.com/jurasmus/blogcontent/main/staledusers.ps1

The first part of the script is a set of variables that you need to change before running the script

$tenantID="YOUR TENANT ID"
$contentType = "application/json"
$exportpath = "'.\User_Signin_Activity.csv'"

$Body = @{    
    Grant_Type    = "client_credentials"
    Scope         = "https://graph.microsoft.com/.default"
    client_Id     = "YOUR CLIENT ID"
    Client_Secret = "YOUR CLIENT SECRET"
}

Here we need to add your Tenant ID from your Azure Tenant and a Client ID (Application ID) and Client Secret from your app reg.
You can also set a Export path where the final result will be stored as a CSV.

The next part of the script is connecting to the Graph API authorizing as your App Registration witch has Audit log read and User read access and start getting users for the first page.

$ConnectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantID/oauth2/v2.0/token" -Method POST -Body $Body

$token = $ConnectGraph.access_token
$headers = @{ 'Authorization' = "Bearer $token" }


$queryURL = 'https://graph.microsoft.com/beta/users?$select=displayName,createddatetime,userprincipalname,mail,usertype,signInActivity,accountEnabled,companyName'
$SignInData = Invoke-RestMethod -Method GET -Uri $queryUrl -Headers $headers -contentType $contentType
$relLink = $SignInData.'@odata.nextLink'

Next the script will loop through users page for page and collect data within a variable called $outlist and when it`s done looping through all users it will export the result into a CSV file.

$outList = @()
while ($SignInData.'@odata.nextLink' -ne $null){
   foreach ($relLink in $SignInData.'@odata.nextLink') {
      Write-Output "Getting data from $relLink"
      $SignInData = Invoke-RestMethod -Method GET -Uri $relLink -Headers $headers -contentType $contentType

          foreach ($user in $SignInData.Value) {
            If ($Null -ne $User.SignInActivity)     {
               $LastSignIn = Get-Date($User.SignInActivity.LastSignInDateTime)
               $DaysSinceSignIn = (New-TimeSpan $LastSignIn).Days }
            Else { #No sign in data for user
               $LastSignIn = "Never or > 90 days" 
               $DaysSinceSignIn = "N/A" }

              $Values  = [PSCustomObject] @{
                  UPN                = $User.UserPrincipalName
                  DisplayName        = $User.DisplayName
                  Email              = $User.Mail
                  Created            = Get-Date($User.CreatedDateTime)
                  LastSignIn         = $LastSignIn
                  DaysSinceSignIn    = $DaysSinceSignIn
                  UserType           = $User.UserType
                  accountEnabled     = $user.accountEnabled
                  Company            = $user.companyName}
                $outList += $Values
          }
  }
}

$outList.Count
$outList | Export-Csv -Path $exportpath -Encoding UTF8 -NoTypeInformation

Conclution

This script is perfect for your toolbox when you need to do manual checks of how your user state are. This can easily be transferred into a Azure Runbook for scheduled execution.