mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
added documetation
This commit is contained in:
@@ -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."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user