Functions/Get-AzDoProjectPermission.ps1
|
Function Get-AzDoProjectPermission{ <# .SYNOPSIS Gets permissions for an Azure DevOps project. .DESCRIPTION Gets permissions for an Azure DevOps project. Can retrieve all permissions, or filter by a specific user email or group name. .EXAMPLE Get-AzDoProjectPermission -Project "TeamDevOps" This will get all permission groups and their members for the TeamDevOps project. .EXAMPLE Get-AzDoProjectPermission -Project "TeamDevOps" -UserEmail "user@company.com" This will get the permissions/group memberships for a specific user in the project. .EXAMPLE Get-AzDoProjectPermission -Project "TeamDevOps" -GroupName "Contributors" This will get all members of the Contributors group in the project. .EXAMPLE Get-AzDoProjectPermission -Project "TeamDevOps" -IncludeMembers This will get all permission groups and expand their members for the TeamDevOps project. .PARAMETER Project The name of your Azure DevOps Project or Team. .PARAMETER UserEmail Optional. The email address of a specific user to check permissions for. .PARAMETER GroupName Optional. The name of a specific group to get members for. Valid values include: Readers, Contributors, Project Administrators, Build Administrators, etc. .PARAMETER IncludeMembers Optional switch. When specified, includes the members of each group in the output. .INPUTS Input is from command line or called from a script. .OUTPUTS RETURNs permission groups and optionally their members. .NOTES Author: Lars Panzerbjørn Creation Date: 2024.12.05 #> [CmdletBinding(PositionalBinding=$False)] param( [Parameter(Mandatory)] [Alias('TeamName')] [string]$Project, [Parameter()] [string]$UserEmail, [Parameter()] [string]$GroupName, [Parameter()] [switch]$IncludeMembers ) BEGIN{ Write-Verbose "Beginning $($MyInvocation.Mycommand)" #Get Project ID $ProjectInfo = Get-AzDOProjects | Where-Object {$_.name -eq $Project} IF(-not $ProjectInfo) { Write-Error "Project '$Project' not found." RETURN } $ProjectId = $ProjectInfo.id Write-Verbose "Project ID: $ProjectId" } PROCESS{ Write-Verbose "Processing $($MyInvocation.Mycommand)" #Get project security groups $GroupsUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/groups?scopeDescriptor=scp.$ProjectId&api-version=7.0-preview.1" Write-Verbose "Groups URI: $GroupsUri" TRY{ $Groups = Invoke-RestMethod -Uri $GroupsUri -Method GET -Headers $Header Write-Verbose "Found $($Groups.count) groups in project" } CATCH{ Write-Error "Failed to get project groups: $_" RETURN } #If UserEmail specified, find user's group memberships IF($UserEmail) { Write-Verbose "Filtering by user: $UserEmail" #Get user descriptor $UserSearchUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/users?api-version=7.0-preview.1" TRY{ $Users = Invoke-RestMethod -Uri $UserSearchUri -Method GET -Headers $Header $User = $Users.value | Where-Object {$_.mailAddress -eq $UserEmail} IF(-not $User) { Write-Error "User with email '$UserEmail' not found in organisation." RETURN } Write-Verbose "Found user: $($User.displayName)" $UserDescriptor = $User.descriptor } CATCH{ Write-Error "Failed to find user: $_" RETURN } #Get user's memberships $MembershipsUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/memberships/$UserDescriptor`?api-version=7.0-preview.1" Write-Verbose "Memberships URI: $MembershipsUri" TRY{ $Memberships = Invoke-RestMethod -Uri $MembershipsUri -Method GET -Headers $Header } CATCH{ Write-Error "Failed to get user memberships: $_" RETURN } #Filter to only project groups $ProjectGroupDescriptors = $Groups.value.descriptor $UserProjectMemberships = $Memberships.value | Where-Object {$_.containerDescriptor -in $ProjectGroupDescriptors} #Build result with group details $Result = @() FOREACH ($Membership in $UserProjectMemberships) { $MatchingGroup = $Groups.value | Where-Object {$_.descriptor -eq $Membership.containerDescriptor} IF($MatchingGroup) { $Result += [PSCustomObject]@{ UserEmail = $UserEmail UserName = $User.displayName GroupName = $MatchingGroup.displayName GroupType = $MatchingGroup.origin Description = $MatchingGroup.description } } } } #If GroupName specified, get members of that group ELSEIF($GroupName) { Write-Verbose "Filtering by group: $GroupName" $TargetGroup = $Groups.value | Where-Object {$_.displayName -eq $GroupName} IF(-not $TargetGroup) { Write-Error "Group '$GroupName' not found in project '$Project'." RETURN } #Get group members $MembersUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/memberships/$($TargetGroup.descriptor)?direction=down&api-version=7.0-preview.1" Write-Verbose "Members URI: $MembersUri" TRY{ $Members = Invoke-RestMethod -Uri $MembersUri -Method GET -Headers $Header } CATCH{ Write-Error "Failed to get group members: $_" RETURN } #Resolve member details $Result = @() FOREACH ($Member in $Members.value) { $MemberDescriptor = $Member.memberDescriptor #Get member details $MemberUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/descriptors/$MemberDescriptor`?api-version=7.0-preview.1" #Try to get as user first $UserUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/users/$MemberDescriptor`?api-version=7.0-preview.1" $GroupUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/groups/$MemberDescriptor`?api-version=7.0-preview.1" TRY{ $MemberDetails = Invoke-RestMethod -Uri $UserUri -Method GET -Headers $Header -ErrorAction SilentlyContinue $MemberType = "User" } CATCH{ TRY{ $MemberDetails = Invoke-RestMethod -Uri $GroupUri -Method GET -Headers $Header -ErrorAction SilentlyContinue $MemberType = "Group" } CATCH{ $MemberDetails = $null $MemberType = "Unknown" } } IF($MemberDetails) { $Result += [PSCustomObject]@{ GroupName = $GroupName MemberName = $MemberDetails.displayName MemberEmail = $MemberDetails.mailAddress MemberType = $MemberType MemberOrigin = $MemberDetails.origin } } } } #Otherwise, get all groups (optionally with members) ELSE { Write-Verbose "Getting all project groups" IF($IncludeMembers) { $Result = @() FOREACH ($Group in $Groups.value) { Write-Verbose "Getting members for group: $($Group.displayName)" #Get group members $MembersUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/memberships/$($Group.descriptor)?direction=down&api-version=7.0-preview.1" TRY{ $Members = Invoke-RestMethod -Uri $MembersUri -Method GET -Headers $Header IF($Members.value.Count -eq 0) { $Result += [PSCustomObject]@{ GroupName = $Group.displayName GroupDescription = $Group.description MemberCount = 0 MemberName = $null MemberEmail = $null MemberType = $null } } ELSE { FOREACH ($Member in $Members.value) { $MemberDescriptor = $Member.memberDescriptor #Try to get member details $UserUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/users/$MemberDescriptor`?api-version=7.0-preview.1" $GroupUri = "https://vssps.dev.azure.com/$Script:Organisation/_apis/graph/groups/$MemberDescriptor`?api-version=7.0-preview.1" TRY{ $MemberDetails = Invoke-RestMethod -Uri $UserUri -Method GET -Headers $Header -ErrorAction SilentlyContinue $MemberType = "User" } CATCH{ TRY{ $MemberDetails = Invoke-RestMethod -Uri $GroupUri -Method GET -Headers $Header -ErrorAction SilentlyContinue $MemberType = "Group" } CATCH{ $MemberDetails = $null $MemberType = "Unknown" } } $Result += [PSCustomObject]@{ GroupName = $Group.displayName GroupDescription = $Group.description MemberCount = $Members.value.Count MemberName = $MemberDetails.displayName MemberEmail = $MemberDetails.mailAddress MemberType = $MemberType } } } } CATCH{ Write-Warning "Failed to get members for group '$($Group.displayName)': $_" $Result += [PSCustomObject]@{ GroupName = $Group.displayName GroupDescription = $Group.description MemberCount = "Error" MemberName = $null MemberEmail = $null MemberType = $null } } } } ELSE { #Just RETURN the groups without members $Result = $Groups.value | Select-Object @{N='GroupName';E={$_.displayName}}, @{N='Description';E={$_.description}}, @{N='Origin';E={$_.origin}}, @{N='Descriptor';E={$_.descriptor}} } } } END{ Write-Verbose "Ending $($MyInvocation.Mycommand)" $Result } } |