I have been using ARM (Azure Resource Management) templates to deploy Azure resources for a fairly long time. Generally speaking, these templates are straightforward to use, but some steps can be a little laborious.
Let’s look at an actual example. I need to deploy an Azure Function App which will need to access an Azure App Configuration. The Azure service must therefore be given the appropriate permissions via Azure RBAC (Role-Based Access Control).
Assigning an identity to the Azure resource
For this example, a system identity is going to be assigned to the Function App. An identity can be assigned using the ARM template for Function App deployment.
In the example above, the identity property stipulates the type as SystemAssigned. Azure takes care of creating an identity for the service. Moreover, Azure will automatically delete this identity when the resource is decommissioned.
Assigning the role via an ARM template
Assigning the role at the time the Azure App Configuration is deployed is simple enough. However, in this case, it is not a viable option because the Azure App Configuration service has existed for a long time, and I don’t want to redeploy that. In addition, it is also possible to assign a role in the same ARM template as the one for the Azure Function App. However, I want to re-use my ARM template for other Azure services. So I am going to assign the role in a separate ARM file as here:
"description": "Azure service name to grant access"
"description": "App configuration name"
"appConfigurationDataReaderRoleId": "[concat(subscription().Id, '/providers/Microsoft.Authorization/roleDefinitions/516239f1-63e1-4d78-a4de-a74fb236a071')]",
"azureServicePrincipalId": "[resourceId('Microsoft.Web/sites', parameters('azureServiceName'))]",
"appConfigurationId": "[resourceId('Microsoft.AppConfiguration/configurationStores', parameters('appConfigurationName'))]"
"name": "[concat(parameters('appConfigurationName'), '/Microsoft.Authorization/', guid(uniqueString(parameters('azureServiceName'))))]",
"principalId": "[reference(variables('azureServicePrincipalId'), '2018-11-01', 'Full').identity.principalId]",
ARM template parameters
Without going to extreme lengths, I have made some aspects of this ARM configurable. There are therefore two parameters, i.e. the Azure service name (the name of the Function App) and the Azure App Configuration service name.
To assign a role to an identity, the role’s identifier as defined by Azure has to be retrieved. Here, I want to give the App Configuration Data Reader role. This is a native Azure role, and its identifiers are supplied by Microsoft. This is why the appConfigurationDataReaderRoleId variable is a character string ending in a GUID.
Next, the identity of the Azure service to which the role is to be assigned (in this case, the Function App) needs to be found. The reference to the resource is retrieved, and stored in the azureServicePrincipalId variable.
Then I need the identifier for the resource to which the authority is to be assigned (in this case, Azure App Configuration). This is done in my appConfigurationId variable.
Lastly, there are two specific points to be noted:
- The resource type: conventionally, the resource type to deploy a role assignment is Microsoft.Authorization/roleAssignments. However, this only works for subscriptions or resource groups. When we wish to assign a right to a specific resource, the type must be “nested”, hence the use of Microsoft.AppConfiguration/configurationStores/providers/roleAssignments.
- The resource name: the name of the resource must be unique and not change. Similarly to the type, when we assign a role to a subscription or resource group, a GUID is sufficient for the name. However, when assigning a right to a specific resource, a nested notation following the example template must be used.