Programmatically connecting to Azure Devops with a Service Principal (Subscription)

A previous post of mine Connecting to Azure Devops with a Service Principal has been popular since I have written it. Therefore, I’ve decided to extend on the topic and show how you can do it programatically with AZ DevOps.

You will need a few things already configured:

* See code in the powershell folder from the post on to see how you can create this programatically.

Create a DevOps PAT token

  • Go to your Azure devops
  • Sign in and click on User settings -> Personal access tokens
  • Click New Token
    • Give it a meaningful name so you know what the PAT token is for in the future. (E.g, Devops Service Connection)
    • Select your Organization
    • Select the Expiration date for as long as you need. Maximum 1 Year
    • Select Scopes at Full access (You might want to tighten your permission in a production environment, for this demo Full access is fine).
    • Click Create
  • Once you have clicked Create this is the only chance to grab a copy of the token. Please take a copy of this token as you will require it later.

The Code

You will need to first be logged into Az Cli. You can sign in using a service principal as you might with a pipeline, as long as the account being used is able to list App Registrations, and ‘User Access Administrator’ RBAC role to be able to apply contribute access to the DevOps service principal on the subscription (Line 43) .

The important part to note in the code is how the authentication works with Devops. The Personal Access Token is added to the $Env: variable “AZURE_DEVOPS_EXT_PAT”. (Line 32)

Creates a service connection for a subscription
Please ensure you are already logged to azure using az login
# Azure DevOps Personal Access Token (PAT) for the '; Azure DevOps tenancy
# The Azure DevOps organisation to create the service connection in, available from System.TeamFoundationCollectionUri if running from pipeline.
$TeamFoundationCollectionUri = $($Env:System_TeamFoundationCollectionUri -replace '%20', ' '),
# The name of the project to which this build or release belongs, available from $(System.TeamProject) if running from pipeline
$TeamProject = $Env:System_TeamProject,
$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'
$account = az account show | ConvertFrom-Json
#Clearing default.
az configure defaults group=
$Env:AZURE_DEVOPS_EXT_PAT = $PersonalAccessToken
Write-Information MessageData:"Adding Azure DevOps Extension…"
az extension add name azuredevops
Write-Information MessageData "Configure defaults Organization:$TeamFoundationCollectionUri"
az devops configure defaults organization="$TeamFoundationCollectionUri"
Write-Information MessageData "Getting App Registration: $AppRegistrationName"
$AppReg = az ad app list all query "[?displayName == '$AppRegistrationName']" | ConvertFrom-Json
Write-Information MessageData "Give App Registration Contributor access to Subscription…"
az role assignment create role 'Contributor' assignee $($AppReg.appId)
Write-Information MessageData "Checking if $TeamProject project exists…"
$ProjectDetails = az devops project list query "value[?name == '$TeamProject']" | ConvertFrom-Json
if(-not $ProjectDetails){
Write-Information MessageData "Creating $TeamProject project…"
$ProjectDetails = az devops project create name $TeamProject
Write-Information MessageData "Checking if service endpoint already exists…"
$ServiceEndpoint = az devops serviceendpoint list project "$TeamProject" query "[?name == '$($AppReg.DisplayName)-Subscription']" | Select-Object First 1 | ConvertFrom-Json
if(-not $ServiceEndpoint){
Write-Information MessageData "Creating Service Connection name:$($AppReg.DisplayName)-Subscription for project $TeamProject"
$Env:AZURE_DEVOPS_EXT_AZURE_RM_SERVICE_PRINCIPAL_KEY = $(ConvertFrom-SecureString SecureString:$AppPassword AsPlainText)
$ServiceEndpoint = az devops serviceendpoint azurerm create project "$TeamProject" name "$($AppReg.DisplayName)-Subscription" azurermserviceprincipalid "$($AppReg.appId)" azurermsubscriptionid "$($" azurermsubscriptionname "$($" azurermtenantid "$($Account.tenantId)" | ConvertFrom-Json
Write-Information MessageData "Updating Service Connection to be enabled for all pipelines…"
az devops serviceendpoint update project "$TeamProject" id "$($" enable-forall true | Out-Null

To run the above code, you will need to put in your parameters. Replace with your values then run the script, this will call the script above.

$PersonalAccessToken = '<Put your PAT Token>'
$TeamProject = '<Project Name>'
$TeamFoundationCollectionUri = '<OrganizationName >'
$AppRegistrationName = '<Service Principal Name>'
$AppPassword = '<Service Principal Secret>'
$AppSecurePassword = ConvertTo-SecureString String:$AppPassword AsPlainText Force
.\Install-ServiceConnectionSubscription.ps1 PersonalAccessToken $PersonalAccessToken `
TeamFoundationCollectionUri:$TeamFoundationCollectionUri `
TeamProject:$TeamProject `
AppRegistrationName:$AppRegistrationName `

My team project is called AutomateDevOps, and I used an App Registration called DevOps.

Running Script

Service Principal with Contribute on Subscription

Project ‘AutomateDevOps’ and Service connection ‘DevOps-Subscription’

My next blog post explains how do make a Management Group Service Connection instead of a Subscription level. ‘Programmatically connecting to Azure Devops with a Service Principal (Management Group)

One thought on “Programmatically connecting to Azure Devops with a Service Principal (Subscription)

  1. Pingback: Programmatically connecting to Azure DevOps with a Service Principal (Management Group) | SharePoint and other geeky stuff

Comments are closed.