diff --git a/Powershell/Lists/Azure/AzurePIM.ps1 b/Powershell/Lists/Azure/AzurePIM.ps1 index bda56e1..555a388 100644 --- a/Powershell/Lists/Azure/AzurePIM.ps1 +++ b/Powershell/Lists/Azure/AzurePIM.ps1 @@ -30,7 +30,8 @@ function GetEligibleAssignments { [string] $scope ) - $access_token = (Get-AzAccessToken -TenantId "e9792fd7-4044-47e7-a40d-3fba46f1cd09").Token + $access_token_secure = (Get-AzAccessToken -TenantId "e9792fd7-4044-47e7-a40d-3fba46f1cd09").Token + $access_token = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($access_token_secure)) $url = "https://management.azure.com/$scope/providers/Microsoft.Authorization/roleEligibilityScheduleInstances?api-version=2020-10-01&`$filter=atScope()" diff --git a/Powershell/Lists/Azure/AzureStorageBlobList.ps1 b/Powershell/Lists/Azure/AzureStorageBlobList.ps1 index f76c2e6..e107a5d 100644 --- a/Powershell/Lists/Azure/AzureStorageBlobList.ps1 +++ b/Powershell/Lists/Azure/AzureStorageBlobList.ps1 @@ -29,7 +29,7 @@ else { # .\AzureStoragebloblist.ps1 -subscriptionId "14c2354d-45a9-4e0f-98ff-be58cdbcddc7" -resourcegroupName "ec-measurement" -storageAccountName "stecmeasurementprod" # .\AzureStoragebloblist.ps1 -subscriptionId "2a07dfa7-69ee-4608-b2d5-14124fcccc31" -resourcegroupName "questionnaire-server-weu" -storageAccountName "questionnairedataweu" # .\AzureStoragebloblist.ps1 -subscriptionId "54794e27-b714-4346-81bc-05eae7ccb5a5" -resourcegroupName "question-management-api-weu" -storageAccountName "qmprojectionsweu" -excludedContainers "`$logs","`$blobchangefeed" - +# .\AzureStoragebloblist.ps1 -subscriptionId "7feeb150-9ee0-4aea-992a-5f3a89d933e6" -resourcegroupName "Results" -storageAccountName "myeffectoryresults" -excludedContainers "`$logs","`$blobchangefeed", "attachments", "azure-webjobs-hosts", "azure-webjobs-secrets", "azure-webjobs-dashboard", "azure-webjobs-hosts", "azure-webjobs-secrets", "hierarchydatesettings-leases", "projectcalculations-leases","resultscleanup-applease","resultscleanup-leases","resultsgroupscorecalculator-leases","testhubname-leases" class BlobCheck { [string] $SubscriptionId = "" diff --git a/Powershell/Lists/Entra/ADMusers.ps1 b/Powershell/Lists/Entra/ADMusers.ps1 deleted file mode 100644 index 260227f..0000000 --- a/Powershell/Lists/Entra/ADMusers.ps1 +++ /dev/null @@ -1,112 +0,0 @@ -# import AD Module -Import-Module activedirectory - #Set-ExecutionPolicy unrestricted - -#--------------------------- -#-------- Variable --------- -#--------------------------- -$i=0 - -#--------------------------- -#------- Functions --------- -#--------------------------- -function Get-ADGroupName ([string] $ADDistinguishedName) -{ - $ADGrpName= (Get-ADGroup -Filter "DistinguishedName -eq '$ADDistinguishedName'").SamAccountName - return $ADGrpName -} - - -function Get-GroupMemberOf ([string] $ADGroupName) -{ - if ($ADGroupName -eq "# Developer") { return } # prevent recursing - - $ii++; $a=0 - $Message=""; $Prefix="" - $GroupCategory=""; $GroupScope=""; $GroupName="" - - For ($a=0; $a -lt $ii; $a++) {$Prefix = $Prefix + " "} - - $DNs=(Get-ADGroup $ADGroupName -Properties *).MemberOf - if ($DNs.count -ne 0) - { - foreach ($DN in $DNs) - { - $GroupName = (Get-ADGroupName $DN) - $GroupCategory = (Get-ADGroup $GroupName -Properties *).GroupCategory - $GroupScope = (Get-ADGroup $GroupName -Properties *).GroupScope - - $Message="$Prefix $ADGroupName => $GroupName [$GroupCategory - $GroupScope]" - Write-Output $Message - - Get-GroupMemberOf $GroupName ' ' - }# End ForEach - }# End IF -}#End Function - -function Get-UserMemberships ([string] $ADUserSID) -{ - $ADUser = Get-ADUser $ADUserSID -Properties * - $ADUserMembers=$ADUser.MemberOf - - $ADUserName = $ADUser.name - Write-Host "AD-User: $ADUserName ($ADUserSID)" - Write-Output "AD-User: $ADUserName" - - #PrimaryGroup - $ADPrimaryGroupDN = (Get-ADUser -Properties * -Filter "SID -eq '$ADUserSID'").PrimaryGroup - $ADPrimaryGroupName=(Get-ADGroupName $ADPrimaryGroupDN) - $ADGroupCategory=(Get-ADGroup $ADPrimaryGroupName).GroupCategory - $ADGroupScope=(Get-ADGroup $ADPrimaryGroupName).GroupScope - $Message = "Primary Group: $ADPrimaryGroupName [$ADGroupCategory, $ADGroupScope]" - Write-Output $Message - - #Other groups - foreach ($ADUserMember in $ADUserMembers) - { - $i++ - $ADGroupName = (Get-ADGroupName $ADUserMember) - $ADGroupCategory=(Get-ADGroup $ADGroupName).GroupCategory - $ADGroupScope=(Get-ADGroup $ADGroupName).GroupScope - $Message = "($i) $ADGroupName [$ADGroupCategory, $ADGroupScope]" - Write-Output $Message - Get-GroupMemberOf $ADGroupName ' ' - Write-Output " " - } -} - -function Get-AllMembershipsOfUsers([string] $ADGroupName) -{ - $i=0 - - $devadmaccounts = get-adgroupmember -Identity $ADGroupName -Recursive - foreach ($devADM in $devadmaccounts) - { - Get-UserMemberships $devADM.SID - Write-Output "======================================================================================================" - } - -} - - -#----------------------------------------------------------------- -Clear-Host - -[string] $ADGroupName -[string] $date = Get-Date -Format "yyyy-MM-dd HHmm" - -$ADGroupName = "# Developer ADM" -Get-AllMembershipsOfUsers $ADGroupName | Out-file "$date Developer ADM.txt" - - $ADGroupName = "Domain Admins" - Get-AllMembershipsOfUsers $ADGroupName | Out-file "$date Domain Admins.txt" - - $ADGroupName = "# Developer" - Get-AllMembershipsOfUsers $ADGroupName | Out-file "$date Developer.txt" - - $ADGroupName = "# Interne Automatisering Team-Assistent" - Get-AllMembershipsOfUsers $ADGroupName | Out-file "$date Interne Automatisering Team-Assistent.txt" - - $ADGroupName = "# Interne Automatisering" - Get-AllMembershipsOfUsers $ADGroupName | Out-file "$date Interne Automatisering.txt" - diff --git a/Powershell/Lists/Entra/GroupMemberships.ps1 b/Powershell/Lists/Entra/GroupMemberships.ps1 new file mode 100644 index 0000000..a94249e --- /dev/null +++ b/Powershell/Lists/Entra/GroupMemberships.ps1 @@ -0,0 +1,225 @@ +param( + [Parameter(Mandatory=$true)] + [string]$GroupId +) + +# GroupMemberships.ps1 -GroupId "# Developer ADM" +# GroupMemberships.ps1 -GroupId "Domain Admins" +# GroupMemberships.ps1 -GroupId "# Developer" +# GroupMemberships.ps1 -GroupId "# Interne Automatisering Team-Assistent" +# GroupMemberships.ps1 -GroupId "# Interne Automatisering" + +[string] $date = Get-Date -Format "yyyy-MM-dd HHmm" +[string] $OutputPath = ".\$date ($GroupId) group members.csv" +[string] $membershipOutputPath = ".\$date ($GroupId) group memberships - parent groups .csv" + +# Connect to Microsoft Graph if not already connected +Write-Host "Connecting to Microsoft Graph..." +Connect-MgGraph -Scopes "Group.Read.All", "GroupMember.Read.All" -NoWelcome + +# If GroupId is actually a group name, resolve it to the actual GroupId +if ($GroupId -notmatch '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$') { + Write-Host "Resolving group name '$GroupId' to GroupId..." + try { + $group = Get-MgGroup -Filter "displayName eq '$GroupId'" -ErrorAction Stop + if ($group) { + if ($group.Count -gt 1) { + Write-Warning "Multiple groups found with name '$GroupId'. Using the first one." + $GroupId = $group[0].Id + } else { + $GroupId = $group.Id + } + Write-Host "Resolved to GroupId: $GroupId" + } else { + Write-Error "Group with name '$GroupId' not found." + exit 1 + } + } catch { + Write-Error "Error resolving group name: $($_.Exception.Message)" + exit 1 + } +} + +# Function to get groups that this group is a member of (reverse membership) +function Get-GroupMembershipRecursive { + param( + [string]$GroupId, + [int]$Level = 0, + [hashtable]$ProcessedMemberships = @{} + ) + + # Check for circular reference in membership chain + if ($ProcessedMemberships.ContainsKey($GroupId)) { + Write-Warning "Circular membership reference detected for group: $GroupId" + return @() + } + + # Check for maximum depth + if ($Level -gt 50) { + Write-Warning "Maximum membership recursion depth reached for group: $GroupId" + return @() + } + + # Mark group as being processed for membership + $ProcessedMemberships[$GroupId] = $true + $membershipResults = @() + + try { + # Get groups that this group is a member of + $memberOfGroups = Get-MgGroupMemberOf -GroupId $GroupId -All + + foreach ($parentGroup in $memberOfGroups) { + if ($parentGroup.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.group') { + $parentGroupDetails = Get-MgGroup -GroupId $parentGroup.Id -Select "DisplayName,Mail,GroupTypes" + + $membershipObject = [PSCustomObject]@{ + ChildGroupId = $GroupId + ParentGroupId = $parentGroup.Id + ParentGroupName = $parentGroupDetails.DisplayName + ParentGroupType = $parentGroupDetails.GroupTypes -join "," + ParentGroupEmail = $parentGroupDetails.Mail + MembershipLevel = $Level + } + + $membershipResults += $membershipObject + + # Recursively get parent groups of this parent group + if ($parentGroup.Id -ne $GroupId) { + $nestedMemberships = Get-GroupMembershipRecursive -GroupId $parentGroup.Id -Level ($Level + 1) -ProcessedMemberships $ProcessedMemberships + $membershipResults += $nestedMemberships + } + } + } + } + catch { + Write-Error "Error getting group memberships for $GroupId`: $($_.Exception.Message)" + } + finally { + # Remove from processed memberships when done + $ProcessedMemberships.Remove($GroupId) + } + + return $membershipResults +} + + +# Initialize collections +$groupMembers = @() +$processedGroups = @{} +$groupStack = @() + +# Function to get group members recursively +# This function will handle circular references and maximum recursion depth +function Get-GroupMembersRecursive { + param( + [string]$GroupId, + [int]$Level = 0, + [string]$ParentGroupName = "" + ) + + # Check for circular reference + if ($processedGroups.ContainsKey($GroupId)) { + Write-Warning "Circular reference detected for group: $GroupId" + return + } + + # Check for stack overflow (max depth) + if ($Level -gt 50) { + Write-Warning "Maximum recursion depth reached for group: $GroupId" + return + } + + # Mark group as being processed + $processedGroups[$GroupId] = $true + $groupStack += $GroupId + + try { + # Get the group information + $group = Get-MgGroup -GroupId $GroupId -ErrorAction Stop + + # Get group members + $members = Get-MgGroupMember -GroupId $GroupId -All + + foreach ($member in $members) { + # Create custom object for the result + $memberObject = [PSCustomObject]@{ + ParentGroupId = $GroupId + ParentGroupName = $group.DisplayName + ParentGroupType = $group.GroupTypes -join "," + MemberId = $member.Id + MemberType = $member.AdditionalProperties['@odata.type'] -replace '#microsoft.graph.', '' + Level = $Level + Path = ($groupStack -join " -> ") + " -> " + $member.Id + } + + # Get member details based on type + switch ($member.AdditionalProperties['@odata.type']) { + '#microsoft.graph.user' { + $user = Get-MgUser -UserId $member.Id -Select "DisplayName,UserPrincipalName,Mail" + $memberObject | Add-Member -NotePropertyName "MemberName" -NotePropertyValue $user.DisplayName + $memberObject | Add-Member -NotePropertyName "MemberUPN" -NotePropertyValue $user.UserPrincipalName + $memberObject | Add-Member -NotePropertyName "MemberEmail" -NotePropertyValue $user.Mail + } + '#microsoft.graph.group' { + $memberGroup = Get-MgGroup -GroupId $member.Id -Select "DisplayName,Mail,GroupTypes" + $memberObject | Add-Member -NotePropertyName "MemberName" -NotePropertyValue $memberGroup.DisplayName + $memberObject | Add-Member -NotePropertyName "MemberUPN" -NotePropertyValue "" + $memberObject | Add-Member -NotePropertyName "MemberEmail" -NotePropertyValue $memberGroup.Mail + $memberObject | Add-Member -NotePropertyName "MemberGroupTypes" -NotePropertyValue ($memberGroup.GroupTypes -join ",") + } + default { + $memberObject | Add-Member -NotePropertyName "MemberName" -NotePropertyValue "Unknown" + $memberObject | Add-Member -NotePropertyName "MemberUPN" -NotePropertyValue "" + $memberObject | Add-Member -NotePropertyName "MemberEmail" -NotePropertyValue "" + } + } + + $script:groupMembers += $memberObject + + # If member is a group, recurse into it + if ($member.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.group') { + # Check if this group is already in the current path to prevent immediate loops + if ($member.Id -notin $groupStack) { + Get-GroupMembersRecursive -GroupId $member.Id -Level ($Level + 1) -ParentGroupName $group.DisplayName + } else { + Write-Warning "Immediate circular reference detected. Skipping group: $($memberGroup.DisplayName)" + } + } + } + } + catch { + Write-Error "Error processing group $GroupId`: $($_.Exception.Message)" + } + finally { + # Remove from stack and processed groups when done + $groupStack = $groupStack[0..($groupStack.Length-2)] + $processedGroups.Remove($GroupId) + } +} + +# Start the recursive process +Write-Host "Starting recursive group membership scan for group: $GroupId" +Get-GroupMembersRecursive -GroupId $GroupId + +# Export results to CSV +if ($groupMembers.Count -gt 0) { + $groupMembers | Export-Csv -Path $OutputPath -NoTypeInformation + Write-Host "Results exported to: $OutputPath" + Write-Host "Total members found: $($groupMembers.Count)" +} else { + Write-Host "No members found in the specified group." +} + +# Get group memberships (groups this group belongs to) +Write-Host "Getting group memberships for group: $GroupId" +$groupMemberships = Get-GroupMembershipRecursive -GroupId $GroupId + +# Export group memberships to separate CSV if any found +if ($groupMemberships.Count -gt 0) { + $groupMemberships | Export-Csv -Path $membershipOutputPath -NoTypeInformation + Write-Host "Group memberships exported to: $membershipOutputPath" + Write-Host "Total parent groups found: $($groupMemberships.Count)" +} else { + Write-Host "No group memberships found for the specified group." +} +