Modules/AzureDevOpsDsc.Common/Api/Functions/Private/Api/Team/Get-DevOpsTeamAdministrator.ps1
|
<#
.SYNOPSIS Determines whether an identity holds team administrator rights on an Azure DevOps team. .DESCRIPTION Team administrator rights are not a first-class property on the team itself — they are an Access Control Entry (ACE) in the 'Identity' security namespace, granted on the team's own token ('{ProjectId}\{TeamId}'). An identity is a team administrator when its ACE on that token has the 'ManageMembership' bit set in 'allow'. The permission bit is never hardcoded: it is resolved from the cached security namespace definition (populated from '_apis/securitynamespaces' at cache init), so a change to the namespace on the service side cannot silently desynchronize this resource from what the portal enforces. The ACL read here is scoped to the single team token (via Get-DevOpsACL -Token), not a scan of the whole 'Identity' namespace, so it stays cheap regardless of organization size. .PARAMETER OrganizationName The name of the Azure DevOps organization. .PARAMETER ProjectId The id of the project that owns the team. .PARAMETER TeamId The id of the team (its group origin id), used as the second half of the Identity ACL token. .PARAMETER MemberDescriptor The ACL (identity) descriptor of the member to check — e.g. 'Microsoft.TeamFoundation.Identity;S-1-9-...'. This is the descriptor an ACE is keyed by, not the graph descriptor used for team-membership calls. .EXAMPLE Get-DevOpsTeamAdministrator -OrganizationName 'contoso' -ProjectId $project.id -TeamId $team.id -MemberDescriptor $memberAclDescriptor .OUTPUTS PSCustomObject with IsTeamAdmin, Token, NamespaceId, ManageMembershipBit and ACL. #> Function Get-DevOpsTeamAdministrator { [CmdletBinding()] [OutputType([PSCustomObject])] param ( [Parameter(Mandatory = $true)] [string]$OrganizationName, [Parameter(Mandatory = $true)] [string]$ProjectId, [Parameter(Mandatory = $true)] [string]$TeamId, [Parameter(Mandatory = $true)] [string]$MemberDescriptor ) Write-Verbose "[Get-DevOpsTeamAdministrator] Started." $namespace = Get-CacheItem -Key 'Identity' -Type 'SecurityNamespaces' if (-not $namespace) { Throw "[Get-DevOpsTeamAdministrator] The 'Identity' security namespace was not found in the cache." } $manageMembershipBit = ($namespace.actions | Where-Object { $_.name -eq 'ManageMembership' }).bit if (-not $manageMembershipBit) { Throw "[Get-DevOpsTeamAdministrator] The 'ManageMembership' action was not found on the 'Identity' security namespace." } $token = '{0}\{1}' -f $ProjectId, $TeamId $acl = Get-DevOpsACL -OrganizationName $OrganizationName -SecurityDescriptorId $namespace.namespaceId -Token $token $matchingAcl = $acl | Where-Object { $_.token -eq $token } | Select-Object -First 1 $allow = 0 if ($matchingAcl -and $matchingAcl.acesDictionary.psobject.Properties.Name -contains $MemberDescriptor) { $allow = [int]$matchingAcl.acesDictionary."$MemberDescriptor".allow } $isTeamAdmin = (($allow -band $manageMembershipBit) -eq $manageMembershipBit) Write-Verbose "[Get-DevOpsTeamAdministrator] Token '$token' — member '$MemberDescriptor' IsTeamAdmin: $isTeamAdmin." return [PSCustomObject]@{ IsTeamAdmin = $isTeamAdmin Token = $token NamespaceId = $namespace.namespaceId ManageMembershipBit = $manageMembershipBit ACL = $matchingAcl } } |