In Azure Repos there are a lot of places where you can set security:
- At the Branch level (develop, master)
- At the Branches level (default for all branches)
- At the Tag level
- At the Tags level (default for all tags)
- At the Repository level (PartsUnlimited-GDBC)
- At the Git Repositories level (for all repositories in a project)
But there is no UI to set the security at the Organization level. This is fine if you're happy with the default security settings in Azure DevOps, but if you want certain settings to apply to all projects (also newly created projects), then it's sometimes useful to set the permissions at the Organization level.
For the Global DevOps Bootcamp we have a few challenges that require changes to be committed to Git through an automated process in order to cause a disruption.
To ensure the changes are able to bypass any branch policies and protected branches, we needed to make sure the service account that makes the change is able to bypass policies.
If you've dug into the security innards of Azure DevOps in the past, you'll have found out that certain permissions are granted to persons or groups and are linked to a token. This token is usually built up out of a root object and a bunch of GUIDs. For example, this is the token for a specific Git Repository:
repoV2/daec401a-49b6-4758-adb5-3f65fd3264e3/f59f38e0-e8c4-45d5-8dee-0d20e7ada1b7
^ ^ ^
| | |
| | -- The Git Repository
| -- The Team Project Guid
|
-- The root object (Repositories)
Simplest way I know of to find these details, is to capture the web request made when a permission is changed:
Once you understand this, it's easy to find the token for the "All Repositories in a Team Project" token. Just take off the Git Repository GUID at the end:
repoV2/daec401a-49b6-4758-adb5-3f65fd3264e3b7/
^ ^
| |
| -- The Team Project Guid
|
-- The root object (Repositories)
And, using the same reasoning, to get to the token for "All repositories in the Project Collection/Organization" token. Just take off the Team Project GUID at the end:
repoV2/
^
|
-- The root object (Repositories)
And now that we have this token, we can use tfssecurity
to set Organization level git permissions:
tfssecurity /a+ "Git Repositories" repoV2/ "PullRequestBypassPolicy" adm: ALLOW /collection:https://dev.azure.com/org
^ ^ ^ ^ ^ ^
| | | | | -- Allow or Deny the permission
| | | | -- The Group (in this case "Project Collection Administrators")
| | | -- The Permission we want to set
| | -- The Token we found above
| -- The Secuity Namespace
-- Add (a+) or Remove (a-) this permission
And, as you can see below, this trick actually works :).
You can use the same technique to secure branches. The token of a branch uses the token of the Repository as a basis and adds the branch to that. Because a /
is a token separator, a branch reference is escaped by replacing /
with ^
. Thus refs/heads/master
becomes: refs^heads^master
:
repoV2/daec401a-49b6-4758-adb5-3f65fd3264e3/f59f38e0-e8c4-45d5-8dee-0d20e7ada1b7/refs^heads^master/
^ ^ ^ ^
| | | |
| | | -- The branch
| | -- The Git Repository
| -- The Team Project Guid
|
-- The root object (Repositories)
This was very useful for the Global DevOps Bootcamp. Instead of having to customize the permissions for 3000 Team Projects, we could now simply set this permission in the 7 organizations that were setup for the event.
$orgs = @("gdbc2019-westeurope", "gdbc2019-westeurope2", "gdbc2019-india", "gdbc2019-centralus", "gdbc2019-australia", "gdbc2019-southamerica", "gdbc2019-canada")
$orgs | %{
$org = $_
& tfssecurity /a+ "Git Repositories" repoV2/ "PullRequestBypassPolicy" adm: ALLOW /collection:https://dev.azure.com/$org
& tfssecurity /a+ "Git Repositories" repoV2/ "PolicyExempt" adm: ALLOW /collection:https://dev.azure.com/$org
}
Note: You can use the REST API to manage security as well, but it requires a little more work to look up the correct identifiers for the Group or User, the Namespace Identifier and more. While generally more complete, the REST API is even harder to understand.
Instead of tfssecurity
you can now also use az devops permissions
to set permissions. You still need most of the intricate knowledge of the tokens:
# tfssecurity /a+ "Git Repositories" repoV2/ "PullRequestBypassPolicy" adm: ALLOW /collection:https://dev.azure.com/$org
az login
az extension add --name "azure-devops"
# Find the group identifier of the group you want to set permissions for
$org = "gdbc2019-westeurope"
# There is a weird edge case here when an Azure DevOps Organization has a Team Project with the same name as the org.
# In that case you must also add a quury to filter on the right domain property `?@.domain == '?'`
$subject = az devops security group list `
--org "https://dev.azure.com/$org/" `
--scope organization `
--subject-types vssgp `
--query "graphGroups[?@.principalName == '[$org]\Project Collection Administrators'].descriptor | [0]"
$namespaceId = az devops security permission namespace list `
--org "https://dev.azure.com/$org/" `
--query "[?@.name == 'Git Repositories'].namespaceId | [0]"
$bit = az devops security permission namespace show `
--namespace-id $namespaceId `
--org "https://dev.azure.com/$org/" `
--query "[0].actions[?@.name == 'PullRequestBypassPolicy'].bit | [0]"
az devops security permission update `
--id $namespaceId `
--subject $subject `
--token "repoV2/" `
--allow-bit $bit `
--merge true `
--org https://dev.azure.com/$org/
az devops
as a modern alternative to tfssecurity
More information:
- Use TFSSecurity to manage groups and permissions for Azure DevOps
- Azure DevOps REST API - Security
- Azure-CLI:
az devops security
Photo credit: Stephen Edmonds.
Top comments (0)