Perform Role Assignments on Azure Resources from Azure Pipelines

Maninderjit (Mani) Bindra
Microsoft Azure
Published in
7 min readOct 1, 2019

In a recent project, as a part of the Azure DevOps (ADO) pipeline step we were required to provide a service principal (SP) access to an Azure resource. This post describes the issues we encountered and a couple of solution options to resolve the issues

Explanation of the requirement in more detail

The aim is to perform a role assignment through an Azure DevOps (AzDO) pipeline.

The diagram below explains a simplified example where we need to provide the testAsigneeSP service principal contributor access to the testroleassignmentsa storage account. This assignment needs to be performed from within an AzDO pipeline. This AzDo pipeline connects to Azure using the azDoServicePrincipal (via the azDoServiceConnection service connection). The azDoServicePrincipal has owner permission on the resource group which contains the storage account.

As we will see this is not as straight forward as it seems. Let us look at the setup and the Initial Attempt to understand what error is received.

requirement overview

In order to take a closer look at the issues let us create the sample resources similar to the ones shown in the diagram above

Setup of Sample Resources

Create the test resource group

az group create --name ado-role-assignment-test-rg --location westus

Create the test storage account

az storage account create -n testroleassignmentsa --resource-group ado-role-assignment-test-rg --location westus --sku Standard_LRS

The output of the above command is shown below. Make sure to note storage account resource id (id column) which will be needed to perform the actual role assignment

{
...
"id": "/subscriptions/yyy-redacted-xxx/resourceGroups/ado-role-assignment-test-rg/providers/Microsoft.Storage/storageAccounts/testroleassignmentsa",
...
"type": "Microsoft.Storage/storageAccounts"
}

Create the service principal for AzDO

$ az ad sp create-for-rbac --name azDoServicePrincipal --role owner --scope '/subscriptions/yyy-redacted-xxx/resourceGroups/ado-role-assignment-test-rg'

In the command above we are give the SP which will be used by AzDO to connect to Azure owner rights on resource group which contains the storage account

The sample output of the above command is shown below. Makes sure to note the appId (aka service principal Id) and the password (aka service principal secret). In the rest of this post this service principal will also be referred to as the AzDO service principal.

{
"appId": "yyyyyyyy-77af-2345-b3bc-xxxxxxxxxxxx",
"displayName": "azDoServicePrincipal",
"name": "http://azDoServicePrincipal",
"password": "---redacted-password---",
"tenant": "yyyyyyyy-833f-6juf-94cc-xxxxxxxxxxxx"
}

Create the test service principal for which we will perform role assignment from within the AzDO pipeline

az ad sp create-for-rbac --name testAsigneeSP --skip-assignment

In the above command we create a service principal without any role assignments. Sample output is shown below. As mentioned earlier we need to note the appId and password. In the rest of this post this service principal will also be referred to as the asignee’s service principal.

{
"appId": "jjjjjjjj-952a-8uhu-9738-pppppppppppp",
"displayName": "testAsigneeSP",
"name": "http://testAsigneeSP",
"password": "---redacted-password---",
"tenant": "yyyyyyyy-833f-6juf-94cc-xxxxxxxxxxxx"
}

Create a Service connection in AzDO to connect to azure using the azDoServicePrincipal service principal

We can do this by navigating to service connections in AzDO, Add new Azure Resource Manager service connection, click on “use the full version of the service connection dialog” and then creating it as shown in the diagram.

Add Service Connection in AzDO

Make sure to verify the connection before hitting ok. Once the service connection is created we can use this in any AzDO pipeline.

The Initial Attempt

We create a new AzDO yaml pipeline to do the following:

  • Use the Azure CLI task
  • Use the Service Connection created above
  • Use an incline script to perform the required role assignment using the az command

Sample script is as follows:

pool:
vmImage: 'ubuntu-latest'
steps:
- task: AzureCLI@1
inputs:
azureSubscription: 'azDoServiceConnection'
scriptLocation: 'inlineScript'
inlineScript: 'az role assignment create --assignee jjjjjjjj-952a-8uhu-9738-pppppppppppp --role Contributor --scope /subscriptions/yyy-redacted-xxx/resourceGroups/ado-role-assignment-test-rg/providers/Microsoft.Storage/storageAccounts/testroleassignmentsa'

Please note that the in the value of assignee you need to add the appId of the testAsigneeSP service principal, and in scope we need to provide the resource Id of the storage account we want to provide the Access to

Error received after initial attempt

Each time the pipeline task failed with the error message

ERROR: Insufficient privileges to complete the operation.

Even though we have given the service principal associated with the ADO pipeline owner permissions on the resource group (and the contained storage account through inheritance ) we keep getting this error.

Let us look at a couple of options to resolve this issue. Option 2 is less permissive and more secure as no access to the Graph API is required, however option2 requires a manual step to get the object Id of the asignee’s service principal using the asignee’s service principal id.

The Solution Option 1: Give the Service Principal access to the AD Graph API

In order to perform role assignment without modifying the role assignment command the AzDO service principal needs access to the AD Graph API. This is needed to fetch the object Id of the asignee’s service principal using the asignee’s service principal id. Only someone with the required permissions on the Azure Directory Tenant can provide this access. The access can be provided as follows :

Get the service principal ID associated with the AzDO Service Connection / AzDo Service principal (in our example, this would be service principal id of azDoServicePrincipal)

To find this id for a given service connection in AzDO you can go to the Service connection and hit the “Update service connection button”

On the next screen click on the “use the full version of the service connection dialog” link.

Next from the following screen copy the “Service principal client ID” value.

Give the ADO Service Principal AD Graph API Access

For this we need the service principal App Id for the service principal which is used by the AzDO pipeline . Once we have that we can logon to the Azure portal (user needs to have permissions to give Graph API access). Go to App Registrations, Select “All applications”, and in the search box put the service principal id. The row for the service principal will appear below the search box. Click on the row.

From the following screen click on the view API Permissions button.

On the next screen click “Add a permission”

After this on the following screen select “Azure Active Directory Graph”

On the Request API permissions screen select “Application permissions”, and check the box for “Directory.Read.All” under the Directory section. After this click add permissions.

Next Admin consent is needed to assign the permissions.

Now we will be able to perform role assignment using the AzDO pipeline on any resource within the resource group, as the AzDO service principal shown in our sample is owner of the resource group. If the initial Pipeline is executed again the role assignment command is successful, and so is the the pipeline execution.

With this option we need to provide the service principal access to the graph API which is not ideal. Let us look at option 2 which in my opinion is a more secure and better option.

The Solution Option 2: Use the service principal Object Id in the az role assignment command

We get the asignee’s service principal object id using the service principal id by executing the following command. Replace the id with the appId you get for the testAsigneeSP service principal.

az ad sp show --id jjjjjjjj-952a-8uhu-9738-pppppppppppp

The output of the command is as shown below. Fetch the objectId

{
"appId": "jjjjjjjj-952a-8uhu-9738-pppppppppppp",
...
"objectId": "uuuuuuuu-0b19-9393-b5fc-vvvvvvvv",
"objectType": "ServicePrincipal",
...
}

Once we have the object Id we use the assignee-object-id switch with this object id, instead of the assignee switch. The changes to the yaml pipeline are highlighted below.

pool:
vmImage: 'ubuntu-latest'
steps:
- task: AzureCLI@1
inputs:
azureSubscription: 'azDoServiceConnection'
scriptLocation: 'inlineScript'
inlineScript: 'az role assignment create --assignee-object-id uuuuuuuu-0b19-9393-b5fc-vvvvvvvv --role Contributor --scope /subscriptions/yyy-redacted-xxx/resourceGroups/ado-role-assignment-test-rg/providers/Microsoft.Storage/storageAccounts/testroleassignmentsa'

Thats it, the pipeline executes successfully.

--

--

Maninderjit (Mani) Bindra
Microsoft Azure

Gopher, Cloud, Containers, K8s, DevOps | LFCS | CKA | CKS | Principal Software Engineer @ Microsoft