added documetation

This commit is contained in:
Jurjen Ladenius
2025-11-03 08:12:01 +01:00
parent 8840b0e300
commit a226ca97ac
37 changed files with 8315 additions and 1481 deletions

View File

@@ -1,15 +1,98 @@
Import-Module Microsoft.Graph
<#
.SYNOPSIS
Retrieves user login information from an Entra ID (Azure AD) group with recursive nested group support.
# .\UserLastLoginList.ps1 -GroupName "# Developer ADM"
.DESCRIPTION
This script connects to Microsoft Graph and retrieves all users from a specified Entra ID group,
including users in nested groups. For each user, it displays their display name, user principal name,
and last sign-in date/time information.
Features:
• Recursive group membership enumeration (handles nested groups)
• Circular reference protection to prevent infinite loops
• Last sign-in activity tracking for security and compliance
• Comprehensive error handling and diagnostic output
• Automatic Microsoft Graph authentication with required scopes
.PARAMETER GroupName
The display name of the Entra ID group to analyze. This parameter is mandatory.
The script will search for an exact match of the group display name.
.EXAMPLE
.\UserLastLoginList.ps1 -GroupName "# Developer ADM"
Retrieves all users from the "# Developer ADM" group, including any nested group memberships.
.EXAMPLE
.\UserLastLoginList.ps1 -GroupName "IT Security Team" -Debug
Retrieves users with detailed debug output showing group processing and user enumeration steps.
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
System.Management.Automation.PSCustomObject
Returns a formatted table with the following properties for each user:
- UserPrincipalName: The user's UPN (email-like identifier)
- DisplayName: The user's display name
- LastSignInDateTime: The user's last sign-in date/time, or "No sign-in data available" if unavailable
.NOTES
Requires PowerShell 5.1 or later
Requires Microsoft.Graph PowerShell module
Required Microsoft Graph Permissions:
- User.Read.All: To read user profiles and sign-in activity
- Group.Read.All: To read group memberships and nested groups
The script will automatically prompt for authentication if not already connected to Microsoft Graph.
Sign-in activity data may not be available for all users depending on license and retention policies.
.LINK
https://docs.microsoft.com/en-us/graph/api/user-get
https://docs.microsoft.com/en-us/graph/api/group-list-members
.COMPONENT
Microsoft Graph PowerShell SDK
.ROLE
Identity and Access Management, Security Reporting
.FUNCTIONALITY
Entra ID group analysis, user activity reporting, nested group enumeration
#>
param(
[Parameter(Mandatory = $true)]
[Parameter(
Mandatory = $true,
HelpMessage = "Enter the display name of the Entra ID group to analyze"
)]
[ValidateNotNullOrEmpty()]
[string]$GroupName
)
<#
.SYNOPSIS
Retrieves an Entra ID group by its display name.
.DESCRIPTION
This function searches for an Entra ID group using its display name and returns the group's unique identifier.
Uses Microsoft Graph API to perform an exact match search on the displayName property.
.PARAMETER EntraGroupName
The exact display name of the Entra ID group to search for.
.OUTPUTS
System.String
Returns the group's unique identifier (GUID) if found, or $null if not found or error occurs.
.EXAMPLE
$groupId = Get-EntraGroupByName -EntraGroupName "Developers"
Retrieves the group ID for the "Developers" group.
#>
function Get-EntraGroupByName {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$EntraGroupName
)
@@ -33,18 +116,54 @@ function Get-EntraGroupByName {
}
}
# Initialize Microsoft Graph connection
Write-Host "🔐 Initializing Microsoft Graph connection..." -ForegroundColor Cyan
Write-Debug "Connecting to Microsoft Graph..."
# Connect to Microsoft Graph if not already connected
if (-not (Get-MgContext)) {
Write-Host "🔑 Authenticating to Microsoft Graph with required scopes..." -ForegroundColor Yellow
Connect-MgGraph -Scopes "User.Read.All", "Group.Read.All"
Write-Host "✅ Successfully connected to Microsoft Graph" -ForegroundColor Green
} else {
Write-Host "✅ Already connected to Microsoft Graph" -ForegroundColor Green
}
<#
.SYNOPSIS
Recursively retrieves all users from an Entra ID group, including nested groups.
.DESCRIPTION
This function performs a recursive search through an Entra ID group structure to find all users,
including those in nested groups. It includes circular reference protection to prevent infinite
loops when groups contain circular memberships.
.PARAMETER GroupId
The unique identifier (GUID) of the Entra ID group to process.
.PARAMETER ProcessedGroups
Internal hashtable used to track processed groups and prevent circular reference loops.
This parameter is used internally for recursion and should not be specified by callers.
.OUTPUTS
System.Management.Automation.PSCustomObject[]
Returns an array of custom objects with the following properties:
- UserPrincipalName: The user's UPN
- DisplayName: The user's display name
- LastSignInDateTime: The user's last sign-in date/time or status message
.EXAMPLE
$users = Get-EntraGroupUsersRecursive -GroupId "12345678-1234-1234-1234-123456789012"
Recursively retrieves all users from the specified group and its nested groups.
.NOTES
This function is designed to handle complex group hierarchies and prevents infinite recursion
through circular group memberships. It processes both user and group objects within the membership.
#>
function Get-EntraGroupUsersRecursive {
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$GroupId,
[hashtable]$ProcessedGroups = @{}
)
@@ -88,20 +207,44 @@ function Get-EntraGroupUsersRecursive {
}
}
$groupId = Get-EntraGroupByName -EntraGroupName $GroupName
if ($groupId) {
# Main execution logic
Write-Host ""
Write-Host "🔍 Searching for Entra ID group: '$GroupName'" -ForegroundColor Cyan
$groupId = Get-EntraGroupByName -EntraGroupName $GroupName
if ($groupId) {
Write-Host "✅ Group found! Group ID: $groupId" -ForegroundColor Green
Write-Host ""
# Get users recursively from the group
Write-Host "Getting users recursively from group..."
Write-Host "👥 Retrieving users recursively from group (including nested groups)..." -ForegroundColor Cyan
$recursiveUsers = Get-EntraGroupUsersRecursive -GroupId $groupId
if ($recursiveUsers) {
Write-Host "Found $($recursiveUsers.Count) users (including nested groups):"
Write-Host ""
Write-Host "📊 Analysis Results:" -ForegroundColor Green
Write-Host "===================" -ForegroundColor Green
Write-Host "Found $($recursiveUsers.Count) users (including nested groups)" -ForegroundColor White
Write-Host ""
# Display results sorted by display name
$recursiveUsers | Sort-Object DisplayName | Format-Table -AutoSize
# Additional statistics
$usersWithSignInData = ($recursiveUsers | Where-Object { $_.LastSignInDateTime -ne "No sign-in data available" }).Count
$usersWithoutSignInData = $recursiveUsers.Count - $usersWithSignInData
Write-Host ""
Write-Host "📈 Sign-in Data Summary:" -ForegroundColor Yellow
Write-Host "========================" -ForegroundColor Yellow
Write-Host "Users with sign-in data: $usersWithSignInData" -ForegroundColor White
Write-Host "Users without sign-in data: $usersWithoutSignInData" -ForegroundColor White
}
else {
Write-Warning "No users found in the group hierarchy or an error occurred."
Write-Warning "No users found in the group hierarchy or an error occurred."
}
}
else {
Write-Warning "Group not found."
Write-Warning "Group '$GroupName' not found. Please verify the group name and try again."
}