The template function 'reference' is not expected at this location - azure

I'm trying to execute a script in my template using Microsoft.Resources/deploymentScripts, but also I'm trying to declare de user assigned identity in the same template
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"name": "scriptIdentity",
"apiVersion": "2018-11-30",
"location": "[resourceGroup().location]"
},
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2019-10-01-preview",
"name": "updateAppServiceConfigMountPointScript",
"dependsOn": [
"[resourceId('Microsoft.Web/sites/config', parameters('appservice_name'), 'web')]",
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity')]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccounts_name'), 'default')]"
],
"location": "[resourceGroup().location]",
"kind": "AzurePowerShell",
"identity": {
"type": "userAssigned",
"userAssignedIdentities": {
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity'), '2019-08-01', 'full').identity.principalId]",
"clientId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity'), '2019-08-01', 'full').identity.clientId]"
}
},
"properties": {
"environmentVariables": [
{
"name": "account_name",
"value": "[parameters('storageAccounts_name')]"
},
{
"name": "app_name",
"value": "[parameters('appservice_name')]"
},
{
"name": "resource_group_name",
"value": "[resourceGroup().name]"
}
],
"scriptContent": "$access_key = ((az storage account keys list --account-name $account_name) | ConvertFrom-JSON).value[0]; az webapp config storage-account add --name \\\"$app_name\\\" --resource-group \\\"$resource_group_name\\\" --custom-id \\\"frontend\\\" --storage-type \\\"AzureBlob\\\" --account-name \\\"stelckstorageaccount\\\" --share-name \\\"frontend\\\" --mount-path \\\"/home/site/wwwroot/frontend\\\" --access-key \\\"$access_key\\\"",
"timeout": "PT1M",
"cleanupPreference": "OnSuccess"
}
The template fails in this part:
"userAssignedIdentities": {
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity'), '2019-08-01', 'full').identity.principalId]",
"clientId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity'), '2019-08-01', 'full').identity.clientId]"
}
With the following error:
Deployment template validation failed: 'The template resource 'updateAppServiceConfigMountPointScript' at line '930' and column '9' is not valid: The template function 'reference' is not expected at this location. Please see https://aka.ms/arm-template-expressions for usage details.. Please see https://aka.ms/arm-template-expressions for usage details.'. (Code: InvalidTemplate)
How can I reference the assigned identity in the deploymentscript without the reference function?
RELATED: 'Microsoft.Web/sites/config' azureStorageAccounts fails due to 500 InternalError

Use the resourceId of the identity, e.g.
"userAssignedIdentities": {
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'scriptIdentity')]": {}
}
See https://github.com/Azure/azure-quickstart-templates/blob/master/201-deployment-script-ssh-key-gen/azuredeploy.json for a full sample.

I had the same issue in a Bicep template, and struggled to figure out how to implement bmoore-msft's answer. While it's not an answer to the original question, I'll post it here in case others like me stumble across this page.
The trick was to use string interpolation to get the identity id on the left side of the colon
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${scriptIdentity.id}': {}
}
}

Related

Azure Managed ID - How to assign Multiple RBAC Roles via shell

Background Information
I have the following arm template that assigns a RBAC to a managed ID;
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string",
"metadata": {
"description": "The principal to assign the role to"
}
},
"builtInRoleType": {
"type": "string",
"allowedValues": [
"Owner",
"Contributor",
"Reader",
"StorageQueueDataContributor",
"StorageTableDataContributor"
],
"metadata": {
"description": "Built-in role to assign"
}
},
"roleNameGuid": {
"type": "string",
"defaultValue": "[newGuid()]",
"metadata": {
"description": "A new GUID used to identify the role assignment"
}
}
},
"variables": {
"Owner": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
"Contributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Reader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
"StorageQueueDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
"StorageTableDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]"
},
"resources": [
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[parameters('roleNameGuid')]",
"properties": {
"roleDefinitionId": "[variables(parameters('builtInRoleType'))]",
"principalId": "[parameters('principalId')]"
}
}
]
}
and I'm calling it from a powershell script like this:
az deployment group create `
--resource-group $RESOURCE_GROUP `
--template-file "./rbac-role.json" `
--parameters principalId=$objectid builtInRoleType=StorageTableDataContributor roleNameGuid=0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3
So far so good. But now I'd like to be able to assign mutiple RBAC roles instead of just one. I tried to call it twice, once per role type but that fails with an error:
{"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"BadRequest","message":"{\r\n \"error\": {\r\n \"code\": \"RoleAssignmentUpdateNotPermitted\",\r\n \"message\": \"Tenant ID, application ID, principal ID, and scope are not allowed to be updated.\"\r\n }\r\n}"}]}}
What I've Tried So far
I've tried to pass arrays as parameters. In reading the docs, it seems that ARM Template parameters can accept arrays.
So I've updated my powershell code to look like this:
$RoleTypeArray = #(
'StorageQueueDataContributor'
'StorageTableDataContributor'
)
$RoleGuidArray = #(
'974c5e8b-45b9-4653-ba55-5f855dd0fb88'
'0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'
)
az deployment group create `
--resource-group $RESOURCE_GROUP `
--template-file "./rbac-role.json" `
--parameters principalId=$objectid builtInRoleType=$RoleTypeArray roleNameGuid=$RoleGuidArray
And the template like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string",
"metadata": {
"description": "The principal to assign the role to"
}
},
"builtInRoleType": {
"type": "array",
"defaultValue": [
"Owner",
"Contributor",
"Reader",
"StorageQueueDataContributor",
"StorageTableDataContributor"
],
"metadata": {
"description": "Built-in role to assign"
}
},
"roleNameGuid": {
"type": "array",
"defaultValue": [
"8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
"b24988ac-6180-42a0-ab88-20f7382dd24c",
"acdd72a7-3385-48ef-bd42-f606fba81ae7",
"974c5e8b-45b9-4653-ba55-5f855dd0fb88",
"0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3"
],
"metadata": {
"description": "A new GUID used to identify the role assignment"
}
}
},
"variables": {
"Owner": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
"Contributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Reader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
"StorageQueueDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
"StorageTableDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]"
},
"resources": [
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[parameters('roleNameGuid')[0]]",
"properties": {
"roleDefinitionId": "[variables(parameters('builtInRoleType')[0])]",
"principalId": "[parameters('principalId')]"
}
}
]
}
For now, I've just hardcoded the index to 0 but if this is the right tree to bark up, I'll have to figure out how to loop? Or I can create the same section twice in the JSON ARM template file maybe - once for each of the two roles?
For now, the error I'm getting is:
Failed to parse JSON: StorageQueueDataContributor StorageTableDataContributor
Error detail: Expecting value: line 1 column 1 (char 0)
It's not clear to me at what point its dying.
Adding some debug statements but any tips would be appreciated.
EDIT 1
I've modified my ARM Template to look like this:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string",
"metadata": {
"description": "The principal to assign the role to"
}
},
"builtInRoleType": {
"type": "array",
"defaultValue": [
"Owner",
"Contributor",
"Reader",
"StorageQueueDataContributor",
"StorageTableDataContributor"
],
"metadata": {
"description": "Built-in role to assign"
}
}
},
"variables": {
"Owner": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
"Contributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Reader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
"StorageQueueDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
"StorageTableDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/','0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]"
},
"resources": [
{
"copy": {
"name": "roleAssignment",
"count": "[length(parameters('builtInRoleType'))]"
},
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[guid(subscription().subscriptionId, resourceGroup().name, parameters('builtInRoleType')[copyIndex()], parameters('principalId'))]",
"properties": {
"roleDefinitionId": "[parameters('builtInRoleType')[copyIndex()]]",
"principalId": "[parameters('principalId')]"
}
}
]
}
This is how I'm calling it:
az deployment group create `
--resource-group $RESOURCE_GROUP_NAME `
--template-file "./rbac-role.json" `
--parameters `
principalId=$objectid `
builtInRoleType="['StorageQueueDataContributor', 'StorageTableDataContributor']"
But the error I'm getting is
{
"status": "Failed",
"error": {
"code": "BadRequestFormat",
"message": "The request was incorrectly formatted."
}
}
I think it could be related to the roleDefinitionID.
This is the second time I'm testing the refactored code. The first time through though, the roleDefinitionID still looked like this:
"roleDefinitionId": "[variables(parameters('builtInRoleType')[0])]",
"principalId": "[parameters('principalId')]"
And I ended up with the StorageQueueDataContributor role being assigned twice.
Multiple things here.
The role assignment name has to be unique. Once an assignment has been created you can't change it so you can't use the same guid otherwise it looks like you're trying to update the role assignment which is not allowed.
You can't use powershell array as input parameter. Using az cli, a valid array parameter would look like that:
az deployment group create `
--resource-group $RESOURCE_GROUP `
--template-file "./rbac-role.json" `
--parameters `
principalId=$objectid `
builtInRoleType="['StorageQueueDataContributor', 'StorageTableDataContributor']" `
roleNameGuid="['974c5e8b-45b9-4653-ba55-5f855dd0fb88', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3']"
To remove the need of generating unique role name, you could use the guid() function:
Creates a value in the format of a globally unique identifier based on the values provided as parameters.
So this will generate a unique string but you can run the same template multiple time, it will always be the same unique string:
"name": "[guid(subscription().subscriptionId, resourceGroup().name, parameters('builtInRoleType')[0]], parameters('principalId'))]",
If you want to create multiple assignment at the same time, you could use the copy() function:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string"
},
"builtInRoleType": {
"type": "array",
"allowedValues":[
"Owner",
"Contributor",
"Reader",
"StorageQueueDataContributor",
"StorageTableDataContributor"
]
}
},
"variables": {
"Owner": "8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
"Contributor": "'b24988ac-6180-42a0-ab88-20f7382dd24c",
"Reader": "acdd72a7-3385-48ef-bd42-f606fba81ae7",
"StorageQueueDataContributor": "974c5e8b-45b9-4653-ba55-5f855dd0fb88",
"StorageTableDataContributor": "0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3"
},
"resources": [
{
"copy": {
"name": "roleAssignment",
"count": "[length(parameters('builtInRoleType'))]"
},
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2020-04-01-preview",
"name": "[guid(subscription().subscriptionId, resourceGroup().name, parameters('builtInRoleType')[copyIndex()], parameters('principalId'))]",
"properties": {
"roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleAssignments', variables(parameters('builtInRoleType')[copyIndex()]))]",
"principalId": "[parameters('principalId')]"
}
}
]
}
then you could just invoke the template like that:
az deployment group create `
--resource-group $RESOURCE_GROUP `
--template-file "./rbac-role.json" `
--parameters `
principalId=$objectid `
builtInRoleType="['StorageQueueDataContributor', 'StorageTableDataContributor']"

Unable to create Azure AKS Container Service with Managed Identity using ARM template

I am trying to create an instance of AKS Container Service with managed identity using an ARM template.
No problems if I use the az CLI:
az aks create -g "sa-rg" -n "aks-cluster" --enable-managed-identity
However I cannot obtain the same result using an ARM template.
Let's consider the following base ARM template
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"resources": [
{
"apiVersion": "2021-03-01",
"dependsOn": [],
"location": "australiaeast",
"name": "aks-cluster",
"properties": {
"agentPoolProfiles": [
{
"name": "agentpool",
"count": 1,
"vmSize": "Standard_DS2_v2",
"osType": "Linux",
"osDiskSizeGB": 128,
"`": null,
"osDiskType": "Managed",
"maxPods": 110,
"type": "VirtualMachineScaleSets",
"mode": "System"
}
],
"dnsPrefix": "aks-cluster-dns",
"servicePrincipalProfile": {
"clientId": "msi",
"secret": null
},
"identity": {
"type": "SystemAssigned"
},
"enableRBAC": true
},
"type": "Microsoft.ContainerService/managedClusters"
}
]
}
According to https://github.com/Azure/azure-cli/issues/12219#issuecomment-636143374, to create with managed identity (MSI), only the "identity" object should be needed, not "servicePrincipalProfile". But, if I do so, I get the following exception:
ERROR: {"error":{"code":"InvalidTemplateDeployment","message":"The
template deployment is not valid according to the
validation procedure. The tracking id is
'5a6c6444-c74b-4709-888e-bef816d05ca9'. See inner errors for
details.","details":[{"code":"InvalidParameter","message":"Provisioning
of resource(s) for container service aks-cluster in resource group
sa-rg failed. Message: {\n "code": "InvalidParameter",\n
"message": "Required parameter servicePrincipalProfile is missing
(null).",\n "target": "servicePrincipalProfile"\n }. Details:
"}]}}
However, if I insert "servicePrincipalProfile" (as shown above), I get:
ERROR: {"error":{"code":"InvalidTemplateDeployment","message":"The
template deployment is not valid according to the
validation procedure. The tracking id is
'536bca0b-33b8-45f8-8407-edba873d3657'. See inner errors for
details.","details":[{"code":"InvalidParameter","message":"Provisioning
of resource(s) for container service aks-cluster in resource group
sa-rg failed. Message: {\n "code": "InvalidParameter",\n
"message": "The value of parameter servicePrincipalProfile.secret
is invalid. Please see https://aka.ms/aks-naming-rules for more
details.",\n "target": "servicePrincipalProfile.secret"\n }.
Details: "}]}}
I have tried
"servicePrincipalProfile": {
"clientId": "msi"
"secret": null
},
"identity": {
"type": "SystemAssigned"
},
"servicePrincipalProfile": {
"clientId": "msi"
"secret": ""
},
"identity": {
"type": "SystemAssigned"
},
"servicePrincipalProfile": {
"clientId": "msi"
"secret": "dummy"
},
"identity": {
"type": "SystemAssigned"
},
"servicePrincipalProfile": {
"clientId": "msi"
},
"identity": {
"type": "SystemAssigned"
},
and again the same 4 removing "identity", but I always get the The value of parameter servicePrincipalProfile.secret is invalid
What is the right ARM template to create the Container Service?
Few things:
The identity property should be at the root of the resource,
You only need to specify clientId: "msi" in the servicePrincipalProfile property.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"outputs": {},
"parameters": {},
"resources": [
{
"apiVersion": "2021-03-01",
"dependsOn": [],
"location": "australiaeast",
"name": "aks-cluster",
"identity": {
"type": "SystemAssigned"
},
"properties": {
"agentPoolProfiles": [
{
"name": "agentpool",
"count": 1,
"vmSize": "Standard_DS2_v2",
"osType": "Linux",
"osDiskSizeGB": 128,
"osDiskType": "Managed",
"maxPods": 110,
"type": "VirtualMachineScaleSets",
"mode": "System"
}
],
"dnsPrefix": "aks-cluster-dns",
"servicePrincipalProfile": {
"clientId": "msi"
},
"enableRBAC": true
},
"type": "Microsoft.ContainerService/managedClusters"
}
]
}
The servicePrincipalProfile is only used when you are provisioning a Service Principal outside of the ARM template and need to pass it's properties into the template. In my deployments I've set this to an empty object though it could potentially be omitted entirely as the documentation says it's not a required property.
"servicePrincipalProfile": {},
"identity": {
"type": "SystemAssigned"
},
Though it's not documented in my experience you can also omit the identity property and it will default to SystemAssigned.
I was deploying AKS using a template, and the template look like this.
{
"name": "[parameters('Your-aks-name')]",
"type": "Microsoft.ContainerService/managedClusters",
"apiVersion": "2021-07-01",
"location": "[resourceGroup().location]",
"sku": {
"name": "Basic",
"tier": "Free"
},
"properties": {
"kubernetesVersion": "1.20.7",
"addonProfiles": { ..... },
"enableRBAC": "[parameters('aks_enableRBAC')]",
"dnsPrefix": "[variables('aks-dns-prefix')]",
"agentPoolProfiles": [
{ .... }
],
"servicePrincipalProfile": {
"ClientId": "[parameters('servicePrincipalClientId')]",
"Secret": "[parameters('servicePrincipalClientSecret')]"
},
}
}
So as you can see there, ClientId and Secret are needed.
And when I run, I get the following errors.
The value of parameter servicePrincipalProfile.secret is invalid. Please see https://aka.ms/aks-naming-rules for more details.
So I solved it as follows.
First create a service principle. Its given here in detail.
az ad sp create-for-rbac --skip-assignment --name vivek-globoticket-askcluster-sp
Note the app Id and password.
az ad sp show --id 67def144-ded3-4fe9-a2f1-cdec6689cd2e # use your app id here.
Also, once you create, you can see them in the azure portal as follows.
Search and go into Azure Active Directory blade.
Click on App Registrations and then All Applications or Owned Applications.
If you forgot to note down the password, then you have to delete the sp and then recreate it.
az ad sp delete --id a76ef144-dee3-4fe9-12f1-cdecn689cd2e # use your app id here.
So for servicePrincipalProfile in the Template do the this. For the client id, use AppId and for the secret, use the password.
Now try creating your aks and see what happens.

How to get the Key's latest version in Output section of an ARM Template?

I need to get the latest version of the Key in the output section of the arm template(generated inside Azure's Key Vault) which is generated from an ARM template . How can I get that ? I need to use the output as input for my next Job in pipeline.
Newer versions of the Key Vault provider for ARM deployments support creating keys, which you can reference as shown in the example ARM template below.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vaultName": {
"type": "string"
},
"objectId": {
"type": "string",
"metadata": {
"description": "The unique principal ID within the tenant to which key wrap and unwrap permissions are given."
}
},
"keyName": {
"type": "string",
"defaultValue": "test-key"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"tenantId": {
"type": "string",
"defaultValue": "[subscription().tenantId]",
"metadata": {
"description": "Tenant ID of the ACtive Directory to authenticate access. Default is the current subscription's tenant ID."
}
}
},
"variables": {
"apiVersion": "2019-09-01"
},
"resources": [
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "[variables('apiVersion')]",
"name": "[parameters('vaultName')]",
"location": "[parameters('location')]",
"properties": {
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "[parameters('tenantId')]",
"accessPolicies": [
{
"tenantId": "[parameters('tenantId')]",
"objectId": "[parameters('objectId')]",
"permissions": {
"keys": [
"wrapKey",
"unwrapKey"
]
}
}
]
}
},
{
"type": "Microsoft.KeyVault/vaults/keys",
"apiVersion": "[variables('apiVersion')]",
// The name must include the vault name and key name separated by a slash.
"name": "[concat(parameters('vaultName'), '/', parameters('keyName'))]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.KeyVault/vaults', parameters('vaultName'))]"
],
"properties": {
"kty": "RSA",
"keySize": 4096,
"keyOps": [
"wrapKey",
"unwrapKey"
]
}
}
],
"outputs": {
"keyName": {
"type": "string",
"value": "[parameters('keyName')]"
},
// Despite the delimited resource name above, we need to construct a 2-parameter resource ID to reference the created key.
"keyUri": {
"type": "string",
"value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', parameters('vaultName'), parameters('keyName'))).keyUri]"
},
"keyVersionUri": {
"type": "string",
"value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', parameters('vaultName'), parameters('keyName'))).keyUriWithVersion]"
}
}
}
Note the comments about how you create and reference the keys differently. Simply using the reference() template function against the delimited name results in an invalid template, so you must construct a resourceId() despite being in the sample template.
Adjust access policies as needed. This example gives the principal you pass key wrap and unwrap capabilities, which you can use for block encryption ciphers.
To use this template (e.g. saved as keyvault-template.json),
az group create -n rg-mytestkv -l westus2
az deployment group create -g rg-mytestkv --template-file keyvault-template.json --parameters vaultName=mytestkv objectId=$(az account show --query id)

Azure VM Custom script extension identity access to Storage Account

In the custom script extension of a VM I want to execute this command:
#download azcopy from http://aka.ms/downloadazcopy
c:\azcopy login --identity
C:\azcopy copy https://mystorage.blob.core.windows.net/software C:\Temp --recursive
But for this to work the identity of the VM need to be added as "Storage Blob Data Contributor". In terraform we could do it this way
resource"azurerm_role_assignment""role" {​​​​​​​​
scope= data.azurerm_storage_account.vault.id
role_definition_name="Storage Blob Data Contributor"
principal_id= azurerm_windows_virtual_machine.vm.identity.0.principal_id
}​​​​​​​​
But if we do not use terraform and instead use Azure DevOps and ARM templates, how would you execute it ? Because the VM is not created yet to give identity access. Custom script extension is part of the creation.
You can enable a system-assigned managed identity using an Azure Resource Manager template. Reference here.
Step1
To enable system-assigned managed identity, locate the Microsoft.Compute/virtualMachines resource of interest within the resources section and add the "identity" property at the same level as the "type": "Microsoft.Compute/virtualMachines" property. Use the following syntax:
"identity": {
"type": "SystemAssigned"
},
Step2
When you're done, the following sections should be added to the resource section of your template and it should resemble the following:
"resources": [
{
//other resource provider properties...
"apiVersion": "2018-06-01",
"type": "Microsoft.Compute/virtualMachines",
"name": "[variables('vmName')]",
"location": "[resourceGroup().location]",
"identity": {
"type": "SystemAssigned",
},
},
//The following appears only if you provisioned the optional VM extension (to be deprecated)
{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(variables('vmName'),'/ManagedIdentityExtensionForWindows')]",
"apiVersion": "2018-06-01",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
],
"properties": {
"publisher": "Microsoft.ManagedIdentity",
"type": "ManagedIdentityExtensionForWindows",
"typeHandlerVersion": "1.0",
"autoUpgradeMinorVersion": true,
"settings": {
"port": 50342
}
}
}
]
Step3
Grant it a role "Storage Blob Data Contributor" access to the resource group in which it was created.
Under the parameters section add the following:
"builtInRoleType": {
"type": "string",
"defaultValue": "StorageBlobDataContributor"
},
"rbacGuid": {
"type": "string"
}
Under the variables section add the following:
"StorageBlobDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]"
Under the resources section add the following:
{
"apiVersion": "2017-09-01",
"type": "Microsoft.Authorization/roleAssignments",
"name": "[parameters('rbacGuid')]",
"properties": {
"roleDefinitionId": "[variables(parameters('builtInRoleType'))]",
"principalId": "[reference(variables('vmResourceId'), '2017-12-01', 'Full').identity.principalId]",
"scope": "[resourceGroup().id]"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('vmName'))]"
]
}
Update
To grant the identity with an RBAC role to access a specific storage account. Refer to this answer.
{
"apiVersion": "2018-01-01-preview",
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"name": "[concat(variables('storageAccountName'), '/Microsoft.Authorization/',parameters('rbacGuid'))]",
"properties": {
"roleDefinitionId": "[variables(parameters('builtInRoleType'))]",
"principalId": "[reference(resourceId('Microsoft.Compute/virtualMachines',parameters('vmName')), '2017-12-01', 'Full').identity.principalId]"
},
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', parameters('vmName'))]"
]
}

Azure: Assign Roles via ARM Template to storage container

I'm trying to assign the role "Storage Blob Data Contributor (Preview)" to a specific storage container via arm template. But I just can't figure out the correct syntax.
This is what I have:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"principalId": {
"type": "string",
"metadata": {
"description": "The principal to assign the role to"
}
},
"builtInRoleType": {
"type": "string",
"allowedValues": [
"Contributor",
"Reader",
"StorageBlobDataContributor"
],
"metadata": {
"description": "Built-in role to assign"
}
}
},
"variables": {
"apiVersion": "2017-05-01",
"Owner": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
"Contributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
"Reader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
"StorageBlobDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"TestVariable": "[concat('STORAGEACCOUNTNAME','/Microsoft.Authorization/',guid(subscription().subscriptionId))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('TestVariable')]",
"properties": {
"roleDefinitionId": "[variables('Reader')]",
"principalId": "[parameters('principalId')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/STORAGEACCOUNTNAME/blobServices/containers/blobCONTAINERNAME/providers/Microsoft.Authorization/roleAssignments",
"apiVersion": "[variables('apiVersion')]",
"name": "STORAGEACCOUNTNAME/blobServices/containers/default/blobCONTAINERNAME/Microsoft.Authorization/NEW-GUID",
"properties": {
"roleDefinitionId": "[variables('StorageBlobDataContributor')]",
"principalId": "[parameters('principalId')]"
}
}
],
"outputs": {}
}
I can attach the reader role to the storage account itself succesfully.
But for the container I get the following error:
new-AzResourceGroupDeployment : 09:21:24 - Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource
'STORAGEACCOUNTNAME/blobServices/containers/CONTAINERNAME/Microsoft.Authorization/GUID' for type
'Microsoft.Storage/storageAccounts/STORAGEACCOUNTNAME/blobServices/default/containers/CONTAINERNAME/providers/Microsoft.Authorization/roleAssignments' at line '44' and column '9' has incorrect
segment lengths. A nested resource type must have identical number of segments as its resource name. A root resource type must have segment length one greater than its resource name. Please see
https://aka.ms/arm-template/#resources for usage details.'.
I have tried so many ways trying to attach the role, that I out of idea's.
Can someone help me?
you need to construct something like this:
resourceId/Microsoft.Authorization/roleAssignments/NEW-GUID
and resourceId is normally being constructed as
type: provider/namespace
name: name
provider/namespace/name
for example, for subnet it would be (notice it takes 1 segment from each line in turn, except for the first one, first one is always 2 segments):
type: microsoft.network/virtualnetworks/subnets
name: vnetName/subnetName
microsoft.network/virtualnetworks/vnetName/subnets/subnetName
if that is even possible it would look like something like this:
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"name": "STORAGEACCOUNTNAME/default/CONTAINERNAME/Microsoft.Authorization/NEW-GUID"
Microsoft.Storage/storageAccounts/STORAGEACCOUNTNAME/containers/CONTAINERNAME/providers/Microsoft.Authorization/roleAssignments/NEW-GUID
Made some little adjustments:
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"name": "STORAGEACCOUNTNAME/default/CONTAINERNAME/Microsoft.Authorization/NEW-GUID"
This way I can assign roles on the container itself. Thanks 4c74356b41 for pointing me in the right direction
Using Erik's answer above (which I've up-voted of course, thx Erik!), I was able to solve the similar issue for RBAC permissions on a Queue of a Storage Account using ARM templates.
Here is an example ARM template for adding Sender role to a single Queue of a Storage Account...
<..snip..>
"parameters": {
"PrincipalId": {
"type": "string",
"minLength": 36,
"maxLength": 36
}
},
"variables": {
"SubscriptionId": "[concat('/subscriptions/', subscription().subscriptionId)]",
"RoleDefinitions": "[concat(variables('SubscriptionId'), '/providers/Microsoft.Authorization/roleDefinitions/')]",
"QueueSenderRole": "c6a89b2d-59bc-44d0-9896-0f6e12d7b80a"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/queueServices/queues/providers/roleAssignments",
"name": "mystorageaccount/default/myqueue/Microsoft.Authorization/00000000-1234-0000-5678-000000000000", // NB example only; pick an idempotent but unique value
"apiVersion": "2018-09-01-preview",
"properties": {
"roleDefinitionId": "[concat(variables('RoleDefinitions'), variables('QueueSenderRole'))]",
"principalId": "[parameters('PrincipalId')]"
}
}
]

Resources