We have an organization in Azure DevOps services that is connected to an Azure AD. In the AAD there are groups representing each team in the enterprise. The goal is to connect those existing AAD groups to teams in Azure DevOps (i.e adding the groups as members of a team) and thus manage membership exclusively in the AAD.
This means that no one should be able to change the membership of the team (it should be reserved for project admins and above).
However when someone is made team admin they automatically get the rights to alter team membership, and people that are not team admins can't edit team settings like adding iterations or customizing the kanban board (cog wheel in the boards). How can I separate those permissions, i.e allowing users to do team level configurations without being allowed to manage membership of the team?
Even if it is not possible to do in the GUI a solution where ACL's are set directly through the rest api would be of interest. I have made some digging in the Security Namespaces and found out by setting the ManageMembership bit of the Identity namespace of a user on the team makes the user an admin of the team (visible in the GUI). The user can do team level customizations and (as the name of the permission bit suggests) update the membership of the team.
I have not been able to find a permission that allows for team level customization without also granting the permission to manage team membership.
What I have tried so far
Not being a team administrators yields the message
You do not have sufficient permissions to configure cards for this team. You must either be a team administrator or a project administrator.
Adding the user as a team administrator, either via the GUI or by setting the Manage Membership bit on the Team removes the message, but also grants rights to add and remove members, which I do not want
# Input Parameters
$ado_org="TestOragnization" ## Azure devops Organization
$ado_pat="****************************************************" ## Azure devops PAT Token
$projectName = "TestProject" ## Azure devops Project Name
$teamName = "TestTeam" ## Azure devops Team Name (must exist in Project)
$userName = "test@example.com" ## Azure devops user (must exist in organization)
# Construct PAT authentication header
# https://docs.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f "user",$ado_pat)))
$headers = @{"Authorization" = ("Basic {0}" -f $base64AuthInfo)
}
# Get Project (https://docs.microsoft.com/en-us/rest/api/azure/devops/core/projects/get?)
$project = Invoke-RestMethod -Headers $headers `
-Uri ("https://dev.azure.com/{0}/_apis/projects/{1}?api-version=6.0" -f $ado_org, $projectName) `
# Get Team (https://docs.microsoft.com/en-us/rest/api/azure/devops/core/teams/create)
$team = Invoke-RestMethod -Headers $headers `
-Uri ("https://dev.azure.com/{0}/_apis/projects/{1}/teams/{2}?api-version=6.0" `
-f $ado_org, $projectName, $teamName) `
# Get User (https://docs.microsoft.com/en-us/rest/api/azure/devops/ims/identities/read%20identities)
$user = Invoke-RestMethod -Headers $headers -Method GET -ContentType "application/json" `
-Uri ("https://vssps.dev.azure.com/{0}/_apis/identities?searchFilter=General&filterValue={1}&api-version=6.0" `
-f $ado_org, $userName)
# Construct ACL request
$token = "{0}\{1}" -f $project.id, $team.id
$namespace = "5a27515b-ccd7-42c9-84f1-54c998f03866" # Identity namespace
$allowbits = 8 # Manage Membership bit
$body = @{
"token" = $token;
"merge" = $false;
"accessControlEntries" = @(
@{
"descriptor" = $user.value.descriptor
"allow" = $allowbits;
"deny" = 0;
"extendedinfo" = @{};
};
)
}
# Set ACL
# https://docs.microsoft.com/en-us/rest/api/azure/devops/security/access%20control%20entries
Invoke-RestMethod -Headers $headers -Method POST -ContentType "application/json" `
-Uri ("https://dev.azure.com/{0}/_apis/accesscontrolentries/{1}?api-version=6.0" -f $ado_org, $namespace) `
-Body $(ConvertTo-Json -InputObject $body -depth 10)
