How Permissions with Azure DevOps and it’s API work

Sebastian SchützeSebastian Schütze

In a project where develop a provisioning engine for Azure DevOps at DB Systel we currently have to deal with the not so in intuitive security and permission handling on the API level.

The naive approach

First, we thought to just check the documentation for the default permissions and then define which permissions we want to have according to the permissions given by the UI.

Permission Page for Project Groups

So we just choose what to set and then use pulumi and create the groups with our chosen permissions. The following problems we had:

So how to handle now to create a group / resource with the permission you intend to have.

The not so naive solution

I post a short script from the PowerShell module VSTeam which gives you quickly an idea how to get the construct your permissions for a new project-level group beware it is hard stuff…

I tried to put explanations in the comments but here I reference the most important lines again:

#uncomment below if you don't have the module
#Install-Module -Name VSTeam

$org = "#Org#"
$pat = "#Pat#"
$projectName = "#Project#"
$GroupName = "#GroupName#"
$Description = "#GroupDescription#"

Set-VSTeamAccount -Account $org -PersonalAccessToken $pat

##### Get the permissions we want to Deny here in these calls #####

#get namespaces for different security permissions and their needed bit masks
$namespaces = (Get-VSTeamSecurityNamespace) | Sort-Object Name
#permission bit mask / namespace for projects
$projectNameSpace = $namespaces | Where-Object Name -eq Project
#say which permissions we want to have denied. So called 'Actions' can be vied by looking into the contents of $projectNameSpace

#handle everything on a project
$project = Get-VSTeamProject -Name $projectName
#get the descriptor for this projects. This is they key for connecting the permission bit masks to the project in the ACL (Access Control Lists)
$projectDescriptor = Get-VSTeamDescriptor -StorageKey $project.ID

#create the group
$groupBody = @{
    displayName = $GroupName
    description = $Description
$jsonBody = $groupBody | ConvertTo-Json -Compress -Depth 100

$group = Invoke-VSTeamRequest `
    -NoProject `
    -method POST `
    -subDomain vssps -area Graph -resource Groups `
    -version "5.1-preview.1" `
    -body $jsonBody `
    -QueryString @{scopeDescriptor = $projectDescriptor.Descriptor } `
    -contentType "application/json"

$projectPermissions = $projectNameSpace.Actions | Where-Object { $permissionsDeny.Contains($_["Name"]) }
$projectPermissions | ForEach-Object {

    $permission = $_

    #to get the correct descriptor we have to take the group descriptor which is base64 encoded.
    # for PowerShell I have to add '==' and remove the non Base64 character 'vssgp.' (meaning it is a descriptor for a AzD native group... not AAD)
    $decodedDescriptor = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("$($group.descriptor -replace "vssgp.",'')=="))

    #if this is not odd enough I have to add certain other strings to token and the descriptor reference for the ACE
    # if you want 'Deny' you need to ad the given bit to 'DenyMask'
    # if you want 'Allow' you need to ad the given bit to 'AllowMask'
    Add-VSTeamAccessControlEntry `
        -SecurityNamespaceId $projectNameSpace.ID `
        -Token "`$PROJECT:$($group.domain):" `
        -Descriptor "Microsoft.TeamFoundation.Identity;$decodedDescriptor" `
        -AllowMask 0 `
        -DenyMask $permission.Bit

The result:


The Azure DevOps API is a hell of a complex mess, but I think I understood after creating the script.

So this means. You are not attaching permissions on the project or group. You rather create new security related data entries into Azure DevOps that link the permission and the resources.

In Database terms resources and permissions are linked as foreign keys uniquely together in a relationship database… At least this is how I try to understand this!

Also published on Medium.

Sebastian is an Azure Nerd with focus on DevOps and Azure DevOps (formerly VSTS) that converted from the big world of SharePoint and O365. He was working with O365 since 2013 and loved it ever since. As his focus shifted in 2017 to more DevOps related topics in the Microsoft Stack. He learned to love the possibilities of automation. Besides writing articles in his blog and German magazines, he is still contributing to the SharePoint Developer Community (and PnP SharePoint) to help to make the ALM part a smoother place to live in.

Comments 1
  • Kevin M Sampson
    Posted on

    Kevin M Sampson Kevin M Sampson

    Reply Author

    Great article! I’m learning this myself and found there are ways to give users admin-like access to manage team members and most everything in the project without giving them the ability to delete the project or do anything an auditor might not like

This site uses Akismet to reduce spam. Learn how your comment data is processed.