From e211250ec21ab1ce444e36fd3bd2a352ce5a0d44 Mon Sep 17 00:00:00 2001 From: mr-pmillz <58376398+mr-pmillz@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:24:10 -0400 Subject: [PATCH 1/3] added Invoke-AddUserToAllUpdatableGroups, Invoke-RemoveUserFromAllUpdatableGroups, and updated Get-UpdatableGroups to query app assignments --- GraphRunner.ps1 | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/GraphRunner.ps1 b/GraphRunner.ps1 index c5a7184..07976ef 100644 --- a/GraphRunner.ps1 +++ b/GraphRunner.ps1 @@ -4193,6 +4193,58 @@ function Get-UpdatableGroups{ $results | Export-Csv -Path $OutputFile -NoTypeInformation Write-Host -ForegroundColor Green ("[*] Exported updatable groups to $OutputFile") } + + # Extended logic to map applications assigned to each updatable group + $groupAppAssignments = @() + + foreach ($group in $results) { + Write-Host -ForegroundColor Cyan "[*] Getting app assignments for group: $($group.displayName)" + + $assignmentsUri = "https://graph.microsoft.com/v1.0/groups/$($group.id)/appRoleAssignments" + + try { + $appRoleAssignments = Invoke-RestMethod -Uri $assignmentsUri -Headers $headers -Method Get + + foreach ($assignment in $appRoleAssignments.value) { + # Lookup the assigned application's display name + $spUri = "https://graph.microsoft.com/v1.0/servicePrincipals/$($assignment.resourceId)" + $sp = $null + + try { + $sp = Invoke-RestMethod -Uri $spUri -Headers $headers -Method Get + } catch { + Write-Host -ForegroundColor Red "[!] Failed to retrieve Service Principal $($assignment.resourceId): $($_.Exception.Message)" + continue + } + + $groupAppAssignments += [PSCustomObject]@{ + GroupName = $group.displayName + GroupId = $group.id + AppDisplayName = $sp.displayName + AppId = $sp.appId + AppAssignmentId = $assignment.id + } + } + + if (-not $appRoleAssignments.value) { + $groupAppAssignments += [PSCustomObject]@{ + GroupName = $group.displayName + GroupId = $group.id + AppDisplayName = "" + AppId = "" + AppAssignmentId = "" + } + } + + } catch { + Write-Host -ForegroundColor Red "[!] Failed to retrieve appRoleAssignments for group $($group.displayName): $($_.Exception.Message)" + } + } + + # Export application assignment details + $appsOut = "Group_App_Assignments.csv" + $groupAppAssignments | Export-Csv -Path $appsOut -NoTypeInformation + Write-Host -ForegroundColor Green "[*] Exported group application assignments to $appsOut" } catch { Write-Host -ForegroundColor Red "An error occurred: $_" } @@ -4413,6 +4465,74 @@ function Invoke-AddGroupMember { } } +function Get-GroupIdsFromFile { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [string]$Path + ) + + if (-Not (Test-Path $Path)) { + throw "File '$Path' does not exist." + } + + try { + $groupIds = Get-Content -Path $Path | Where-Object { $_.Trim() -ne "" } + return $groupIds + } catch { + throw "Failed to read from '$Path': $_" + } +} + +function Invoke-AddUserToAllUpdatableGroups { + <# + .SYNOPSIS + Adds a user to all the updatable groups from a file containing updatable group IDs + Author: Phil Miller (@mr-pmillz) + License: MIT + Required Dependencies: None + Optional Dependencies: None + + .DESCRIPTION + + Adds a user to all the updatable groups from a file containing updatable group IDs + + .PARAMETER GroupIdFile + + File containing updatable group IDs + + .PARAMETER UserId + + The ID of the object that you want to remove from the group + + .PARAMETER Tokens + + Token object for auth + + .EXAMPLES + + C:\PS> Invoke-AddUserToAllUpdatableGroups -Tokens $tokens -GroupIdFile updatable_groups.txt -userId 7a3d8bfe-e4c7-46c0-93ec-ef2b1c8a0b4a + #> + + param ( + [string] + $groupIdFile, + [string] + $userId, + [object[]] + $Tokens = "" + ) + + # Read group IDs from file + $groupIds = Get-GroupIdsFromFile -Path $groupIdFile + + # Loop through them + foreach ($groupId in $groupIds) { + Write-Host "Adding user ID $userId to group ID: $groupId" + Invoke-AddGroupMember -Tokens $tokens -groupID $groupId -userId $userId + } +} + function Invoke-RemoveGroupMember { @@ -4496,6 +4616,54 @@ function Invoke-RemoveGroupMember { } } +function Invoke-RemoveUserFromAllUpdatableGroups { + <# + .SYNOPSIS + Removes a user from all the updatable groups from a file containing updatable group IDs + Author: Phil Miller (@mr-pmillz) + License: MIT + Required Dependencies: None + Optional Dependencies: None + + .DESCRIPTION + + Removes a user to all the updatable groups from a file containing updatable group IDs + + .PARAMETER Tokens + + Token object for auth + + .PARAMETER GroupIdFile + + File containing updatable group IDs + + .PARAMETER UserId + + The ID of the object that you want to remove from the group + + .EXAMPLES + + C:\PS> Invoke-AddGuestUserToAllUpdatableGroups -Tokens $tokens -GroupIDFile updatable_groups.txt -userId 7a3d8bfe-e4c7-46c0-93ec-ef2b1c8a0b4a + #> + + param ( + [string] + $groupIdFile, + [string] + $userId, + [object[]] + $Tokens = "" + ) + + # Read group IDs from file + $groupIds = Get-GroupIdsFromFile -Path $groupIdFile + + # Loop through them + foreach ($groupId in $groupIds) { + Write-Host "Removing user ID $userId from group ID: $groupId" + Invoke-RemoveGroupMember -Tokens $tokens -groupID $groupId -userId $userId + } +} function Get-EntraIDGroupInfo { <# From f05b3cb54df4f51913d73e556a4b44b9478b557c Mon Sep 17 00:00:00 2001 From: mr-pmillz <58376398+mr-pmillz@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:28:23 -0400 Subject: [PATCH 2/3] fix example typo --- GraphRunner.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GraphRunner.ps1 b/GraphRunner.ps1 index 07976ef..b5798ad 100644 --- a/GraphRunner.ps1 +++ b/GraphRunner.ps1 @@ -4643,7 +4643,7 @@ function Invoke-RemoveUserFromAllUpdatableGroups { .EXAMPLES - C:\PS> Invoke-AddGuestUserToAllUpdatableGroups -Tokens $tokens -GroupIDFile updatable_groups.txt -userId 7a3d8bfe-e4c7-46c0-93ec-ef2b1c8a0b4a + C:\PS> Invoke-RemoveUserFromAllUpdatableGroups -Tokens $tokens -GroupIDFile updatable_groups.txt -userId 7a3d8bfe-e4c7-46c0-93ec-ef2b1c8a0b4a #> param ( From 7462aaa2942bc600284a480ac18266d89c2574da Mon Sep 17 00:00:00 2001 From: mr-pmillz <58376398+mr-pmillz@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:55:13 -0400 Subject: [PATCH 3/3] write dynamic groups to csv file --- GraphRunner.ps1 | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/GraphRunner.ps1 b/GraphRunner.ps1 index d7adc80..7f23727 100644 --- a/GraphRunner.ps1 +++ b/GraphRunner.ps1 @@ -4274,7 +4274,10 @@ function Get-DynamicGroups{ Param( [Parameter(Position = 0, Mandatory = $False)] [object[]] - $Tokens = "" + $Tokens = "", + [Parameter()] + [string] + $OutputFile = "Dynamic_groups.csv" # Set the default value to "Dynamic_groups.csv" ) if($Tokens){ @@ -4377,6 +4380,10 @@ function Get-DynamicGroups{ Write-Output ("=" * 80) } + if ($OutputFile) { + $results | Export-Csv -Path $OutputFile -NoTypeInformation + Write-Host -ForegroundColor Green ("[*] Exported dynamic groups to $OutputFile") + } } function Invoke-AddGroupMember {