Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use ARM templates to deploy a roleAssignment for an App Registration Service Principal?

In my current project I'm working with pre-created App Registration Service Principals in Azure AD. I'm using an ARM template to create a StorageV2 account plus some blob containers, then create a roleAssignment giving Storage Blob Contributor rights to one of the Service Principals. The relevant section of the ARM template is at the end of this post.

What I've found is that if I take the ObjectId of a regular AD user, such as myself or my colleague, and set that as PrincipalId, the script runs correctly. However, I can't get this to work with a Service Principal.

If I use the Service Principal's ObjectId, then I get the following error:

Deployment failed. Correlation ID: 40e0c146-165a-47c0-b022-ac04781d8194. {
  "error": {
    "code": "PrincipalTypeNotSupported",
    "message": "Principals of type Application cannot validly be used in role assignments."
  }
}

Having spotted some suggestions for Azure Powershell users that I should use Application (Client) Id instead, I tried that, but get the following error (Guids redacted):

Deployment failed. Correlation ID: 5c725a51-230a-4d85-bb61-b2f4cdf849ff. {
  "error": {
    "code": "PrincipalNotFound",
    "message": "Principal 9f****30 does not exist in the directory db****75."
  }
}

So the ObjectId it can find but not use, and the ClientId it can't find.

I have found that if I use Azure Powershell and use the New-AzureRmRoleAssignment command, I can reproduce the PrincipalTypeNotSupported error by providing the Service Principal's ObjectId to the -ObjectId switch. However, that command also has a -ServicePrincipalName switch as an alternative, and if I give that the Service Principal's ClientId, it works!

Is there any equivalent of -ServicePrincipalName for the ARM templates, and if not, is there any other way to achieve this? I can use Azure Powershell as a workaround, but it's messier than I'd like.

If this is a feature gap, where's the best place to report it?

Relevant section of ARM template follows:

"resources": [
    {
        "name": "[variables('storageAccountName')]",
        "type": "Microsoft.Storage/storageAccounts",
        "location": "[resourceGroup().location]",
        "apiVersion": "2018-07-01",
        "sku": {
            "name": "[parameters('storageAccountSku')]"
        },
        "dependsOn": [],
        "tags": {
            "displayName": "Storage Account"
        },
        "kind": "StorageV2",
        "properties": {
            "accessTier": "Hot",
            "supportsHttpsTrafficOnly": true,
            "networkAcls": {
                "bypass": "AzureServices",
                "virtualNetworkRules": [],
                "ipRules": [],
                "defaultAction": "Deny"
            }
        },
        "resources": [
            {
                "type": "blobServices/containers",
                "name": "[concat('default/', variables('myBlobContainerName'))]",
                "apiVersion": "2018-07-01",
                "dependsOn": [
                    "[variables('storageAccountName')]"
                ],
                "resources": [
                    {
                        "type": "Microsoft.Authorization/roleAssignments",
                        "name": "[variables('myRoleAssignmentGuid')]",
                        "apiVersion": "2018-07-01",
                        "properties": {
                            "roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
                            "principalId": "[variables('myPrincipalId')]"
                        },
                        "dependsOn": [
                            "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'), '/blobServices/default/containers/', variables('myBlobContainerName'))]"
                        ]
                    }
                ]
            }
        ]
    }
]
like image 891
Alexlotl Avatar asked Mar 03 '23 20:03

Alexlotl


1 Answers

Finally solved this one thanks in part to the pointers from @4c74356b41.

When an Application Registration object is created, an identically named object is also created under Enterprise Applications. This has the same ApplicationId, but a different ObjectId, and it's the ObjectId of this Enterprise Application object that our ARM script needs.

You can find this object in the portal by going to the Application Registration entry, then clicking on the link after Managed application in...

Screenshot of App Registration with Link Screenshot of App Registration with Link

Once you're on the corresponding Enterprise Application object, you can get the ObjectId from Properties, and use this the value for principalId in the ARM template.

At the time of writing, the Microsoft Documentation is a bit vague on this, with the terms Application and Service Principal seemingly overloaded. This article says that when you register an application you get an Application object and a Service Principal object, but doesn't use the phrase Enterprise Application once, or refer to App Registration objects per se, so it's unclear which is which.

I'm assuming Application == Application Registration and Service Principal == Enterprise Application. This SO post would seem to suggest this is the case, as would the solution above.

like image 141
Alexlotl Avatar answered Apr 26 '23 22:04

Alexlotl