Skip to content

Instantly share code, notes, and snippets.

@jschlackman
Created October 27, 2023 13:47
Show Gist options
  • Save jschlackman/36eb6d2c281b3e5c57f2594078fcc2ca to your computer and use it in GitHub Desktop.
Save jschlackman/36eb6d2c281b3e5c57f2594078fcc2ca to your computer and use it in GitHub Desktop.
# Name: Get-EntraGuestDetails.ps1
# Author: James Schlackman
# Last Modified: Oct 27 2023
#
# Audits guest users in Entra ID, optionally filtered by start and/or end dates for when users were invited to the directory.
#
# Last sign in date for Entra requires an Entra ID Premium license.
#Requires -Modules Microsoft.Graph.Authentication, Microsoft.Graph.Groups, Microsoft.Graph.Users
Param(
# Export file name
[Parameter()] [String]$OutputPath = "$((Get-Date).ToString("yyMMdd")) Entra Guest Details.csv",
# Optional start and end dates for when guest users were invited
[Parameter()] [DateTime] $StartDate = '2023-03-01 00:00:00',
[Parameter()] [DateTime] $EndDate = '2023-08-30 23:59:59'
)
# Function to convert UTC datetimes returned by Graph to local time
$LocalTimeZone = [System.TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
function LocalTime() {
Param(
[Parameter()] [DateTime] $UtcDateTime
)
If ([bool]$UtcDateTime) {
[System.TimeZoneInfo]::ConvertTimeFromUtc($UtcDateTime, $LocalTimeZone)
}
}
# Init empty hash table
$GroupData = @{}
# Retrives the display name of a specified group, and caches it to speed up repeat lookups
function Get-GroupName() {
Param(
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] [String[]] $Id
)
If ([bool]$Id) {
$Id | ForEach-Object {
# Get group name if not already cached
If (![bool]$GroupData[$_]) {
$LoadGroup = Get-MgGroup -GroupId $_
$GroupData.Add($LoadGroup.Id, $LoadGroup.DisplayName)
}
# Return the group name from the cache
$GroupData[$_]
}
}
}
# Connect to Graph
Connect-MgGraph -Scopes User.Read.All,Group.Read.All
# Define user properties to load
$EntraLoadProperties = 'DisplayName','UserPrincipalName','Mail','CreatedDateTime','SignInActivity','Identities','ExternalUserState','ExternalUserStateChangeDateTime','MemberOf'
# Build filter components for start and end dates if specified
If ([bool]$StartDate) {
$StartFilter = "and CreatedDateTime ge $($StartDate.ToUniversalTime().ToString('s'))Z"
} Else {
$StartFilter = ''
}
If ([bool]$EndDate) {
$EndFilter = "and CreatedDateTime le $($EndDate.ToUniversalTime().ToString('s'))Z"
} Else {
$EndFilter = ''
}
# Load the guest list
$GuestUsers = Get-MgUser -Filter "userType eq 'Guest' $StartFilter $EndFilter" -All -Property $EntraLoadProperties
# Parse returned users for report
$GuestUsers = $GuestUsers | ForEach-Object {
$SignIn = $_.SignInActivity.LastSignInDateTime;
# Fallback to last non-interactive sign-in if no standard sign-in date is available
If ([bool]$SignIn){
$SignIn = LocalTime($SignIn)
} Else {
$SignIn = LocalTime($_.SignInActivity.LastNonInteractiveSignInDateTime)
}
[PSCustomObject]@{
Name = $_.DisplayName
EMail = $_.Mail
UserPrincipalName = $_.UserPrincipalName
Invited = LocalTime($_.CreatedDateTime)
State = $_.ExternalUserState
'State Last Changed' = $_.ExternalUserStateChangeDateTime
LastSignIn = $SignIn
'Group Memberships' = (Get-MgUserMemberOf -UserId $_.Id | Get-GroupName) -join ', '
}
}
$GuestUsers | Out-GridView
Write-Host "Guest users found: $(@($GuestUsers).Count)"
Write-Host 'See grid export for details.'
If ([bool]$OutputPath) {
# Optionally export output to file
Write-Host "`nExport details to CSV? " -ForegroundColor Cyan -NoNewline
If ((Read-Host '[y/N]').ToUpper() -eq 'Y') {
Write-Host 'Exporting to ' -NoNewline
Write-Host $OutputPath -ForegroundColor Green
$GuestUsers | Export-Csv -NoTypeInformation -Path ($OutputPath) -Encoding UTF8
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment