How to assign RBAC to multiple user using arm template in azure - azure

I have two AAD Application(Service principal) and want to add RBAC to these two Application using arm template.
I tried deploying with arm template below.
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat(parameters('StorageAccountName'), '/default/',parameters('ContainerName'), '/Microsoft.Authorization/', parameters('roleNameGuid'))]",
"properties": {
"roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"principalId": "[parameters('principalId')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat(parameters('StorageAccountName'), '/default/',parameters('ContainerName'), '/Microsoft.Authorization/', parameters('roleNameGuid'))]",
"properties": {
"roleDefinitionId": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"principalId": "[parameters('principalId2')]"
}
}
When I deploy with this arm template, I got error below.
Deployment template validation failed: 'The resource 'Microsoft.Storage/storageAccounts/MystorageAccounts/blobServices/default/containers/test/providers/Microsoft.Authorization/roleAssignments/aacd4b89-a70f-4be9-a0ba-6b8698dd7129' at line '52' and column '9' is defined multiple times in a template. Please see https://aka.ms/arm-template/#resources for usage details.'. (Code: InvalidTemplate)

You need to use different names for the name option in the resource. For example, you can append a number at the end of the name to distinguish the difference.

Wouldn't this work as well, and take away the hardcoded value of the guid?:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions-string#guid
Yes it will require some input to calculate a different GUID, but you could use property iteration to change that:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#property-iteration

Related

Azure Resource Manager Template: Conditional Deployment

I'm working on an ARM template and need to perform conditional deployment. For instance, I've got two network security groups defined in a variable 'subnets'.
"variables": {
"subnets": [
{
"subnetname": "AzureBastionSubnet",
"nsgname": "nsg_bastion1"
},
{
"subnetname": "client-subnet",
"nsgname": "nsg_client1"
}
]
}
The network security group 'nsg_bastion1' needs special treatment with predefined rules since it is a network security group for an Azure Bastion subnet.
'nsg_client1' will get some custom rules assigned, which don't matter at this point.
In order to differentiate between non-Bastion and Bastion-network security groups, I've created two conditional resource blocks:
"resources": [
// NSG (non-Bastion)
{
"condition": "[not(equals(variables('subnets')[copyIndex()].name, 'AzureBastionSubnet'))]",
"name": "[variables('subnets')[copyIndex()].nsg]",
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[parameters('location')]",
"properties": {},
"copy": {
"name": "nsg-c",
"count": "[length(variables('subnets'))]"
}
},
// NSG (Bastion)
{
"condition": "[equals(variables('subnets')[copyIndex()].name, 'AzureBastionSubnet')]",
"name": "[variables('subnets')[copyIndex()].nsg]",
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[parameters('location')]",
"properties": {},
"resources": [
// Bastion Security Rules .....
],
"copy": {
"name": "nsg-bastion-c",
"count": "[length(variables('subnets'))]"
}
},]
The condition property checks whether the subnet is called 'AzureBastionSubnet' or not. I've verified that this works for both resource blocks alone, but it doesn't when they're included both in the code. It always throws the following error message:
Code=InvalidTemplate; Message=Deployment template validation failed:
'The resource 'Microsoft.Network/networkSecurityGroups/AzureBastionSubnet' at line '' and column '' is defined multiple times in a template.
I'd appreciate any help, thanks in advance!
Ok, I thought we had fixed this but not yet...
Even though your conditions are mutually exclusive, you can't have 2 resources with the same resourceId in the same template.
I'm guessing the difference between the two is in the securityRules? If so, simplest option may be to define 2 separate vars and then use an if() statement to swap between them based on your condition.
Failing that if you want to expand on the template can see if there is a better option...

Azure Storage blob container assign RBAC using ARM

We currently have ARM templates that create storage accounts and containers in a solution however I can't seem to manage to assign the RBAC access to the container in the ARM template. I have tried using Erik's solution here
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2017-09-01",
"name": "[concat(parameters('storageAccountName'),'/default/filedrop/Microsoft.Authorization/{NEW GUID}')]",
"properties": {
"roleDefinitionId": "ba92f5b4-2d11-453d-a403-e96b0029c9fe",
"principalId": "[parameters('ServicePrincipalId')]"
}
The error I get is "error": {
"code": "BadRequestFormat",
"message": "The request was incorrectly formatted."
}
Anyone see where I'm going wrong?
Here is what I used: https://github.com/juunas11/managedidentity-filesharing/blob/8410ed3f3d4061de7d40531c025bf6e474489135/Joonasw.ManagedIdentityFileSharingDemo.ARM/azuredeploy.json#L223-L236
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2018-01-01-preview",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('storageContainerName'), '/Microsoft.Authorization/', guid(resourceGroup().id, 'webAppFilesAccess'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('storageContainerName'))]",
"[resourceId('Microsoft.Web/sites', parameters('webAppName'))]"
],
"properties": {
"principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2016-08-01', 'Full').identity.principalId]",
"roleDefinitionId": "[variables('storageBlobContributorRoleId')]"
}
}
The main difference I can see is that I have a higher API version + I use parameters for a lot of things.
The guid() function is pretty handy since you can give it some text, and if the text is same, it'll give the same GUID every time.

Deploy nested resources separately

Due to separation of duty I need to split an existing ARM template into two single templates - one for the resource and one for the logging
the original templates looks like this:
"resources": [
{ // https://learn.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories
"type": "Microsoft.DataFactory/factories",
"name": "[variables('dataFactoryName')]",
"apiVersion": "[variables('apiVersion')]",
"location": "[resourceGroup().location]",
"tags": {},
"identity": {
"type": "SystemAssigned"
},
"properties": {},
"resources": [
{
"type": "providers/diagnosticSettings",
"name": "[concat('Microsoft.Insights/', variables('logSettingName'))]",
"dependsOn": [
"[resourceId('Microsoft.DataFactory/factories', variables('dataFactoryName'))]"
],
"apiVersion": "2017-05-01-preview",
"location": "[resourceGroup().location]",
"tags": {},
"properties": {
"name": "[variables('logSettingName')]",
"workspaceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('logAnalyticsObject').resourceGroup, '//providers/Microsoft.OperationalInsights/workspaces/', parameters('logAnalyticsObject').name)]",
"logs": "[parameters('logAnalyticsObject').adfv2.logs]",
"metrics": "[parameters('logAnalyticsObject').adfv2.metrics]"
}
}
]
}
The first part is quite easy, I just remove the sub-resource but how to get the second part (resource with "type": "providers/diagnosticSettings") correctly so it can be deployed from a different template?
Is this possible at all or are these strongly tied together?
I already tried different things like
"type": "Microsoft.DataFactory/factories/providers/diagnosticSettings",
"name": "[concat('Microsoft.Insights/', variables('name'))]",
but ended up with error messages like this:
Unable to process template language expressions for resource '/subscriptions/fb1e20c4-0878-4949-ac10-f92a9ac35db4/resourceGroups/swe-sdp-dv0
-rgp-adp/providers/Microsoft.Resources/deployments/DataFactory_LogAnalytics_Resource' at line '67' and column '5'. 'Unable to evaluate template language
function 'resourceId': function requires exactly one multi-segmented argument which must be resource type including resource provider namespace. Current
function arguments 'fb1e20c4-0878-4949-ac10-f92a9ac35db4,swe-sdp-dv0-rgp-anl,Microsoft.Insights,swe-sdp-dv0-oms-001'. Please see
https://aka.ms/arm-template-expressions/#resourceid for usage details.
I think to make it work I would need the right combination of "type", "name" and probably also "dependsOn"
ok, according to this, you would need to do this:
"type": "Microsoft.DataFactory/factories/providers/diagnosticSettings",
"name": "[concat(variables('dataFactoryName'), '/Microsoft.Insights/', variables('name'))]",
you dont need dependsOn, because resources are under different templates.

arm template virtualNetworkName creation appendix issue

I am trying to get a arm template running and have hit an issue with the virtualnetwork creation.
azuredeploy.json
"virtualNetworkName": {
"type": "string",
"metadata": {
"description": "Name of virtual network to be created"
},
"defaultValue": "autohav2VNET"
},
vnet-net.json
"resources": [
{
"name": "[parameters('virtualNetworkName')]",
"type": "Microsoft.Network/virtualNetworks",
"location": "[parameters('location')]",
"apiVersion": "2015-06-15",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('virtualNetworkAddressRange')]"
]
},
"subnets": "[parameters('subnets')]"
}
}
]
The issue I am getting is that the vnet gets created with an appendix such as this: autohav2VNETl5g
So when this gets used to create a loadblancer, the names doe not match the defined parameter and the creation fails.
..../virtualNetworks/AUTOHAV2VNET referenced by resource .... /Microsoft.Network/loadBalancers/sqlLoadBalancer was not found.
Any suggestions?
with the data given it impossible to be sure why this is happening. you are probably passing in a value to the parameter virtualNetworkName. because if you wouldn't, than the vnet name would be: autohav2VNET.
ARM templates do not append anything anywhere just because they are arm templates. they only do what you designed them to do.
to help with debugging: how you are invoking the template and full template + full parameters file.

Azure resource can't find dependancy when deploying

I'm trying to deploy an Azure Windows VM using templates and keep running into the error code: InvalidResourceReference Resource X referenced by Resource Y was not found. Resource X is Microsoft.Network/networkSecurityGroups (named 'FBI') and resource Y is Microsoft.Network/networkInterfaces (named vInterface).
All my required resources are created during this deployment with their dependencies set in the template. The first thing I did was confirm that my FBI resource exists, which it did:
Next I ensured that my FBI security group was listed as a dependency in vInterface to ensure that FBI does get created first before vInterface is created, which it is:
{
"name": "[parameters('networkInterfaceName')]",
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2018-04-01",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressName'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"subnet": {
"id": "[variables('subnetRef')]"
},
"privateIPAllocationMethod": "Dynamic",
"publicIpAddress": {
"id": "[resourceId('VMGroup','Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName'))]"
}
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('VMGroup', 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
}
}
}
I can confirm that the location is the same for both of these resources. Everything looks ok but I can't figure out why my vInterface can't find/see my FBI security group.
For reference here's the full error message:
"error": {
"code": "InvalidResourceReference",
"message": "Resource /subscriptions/---/resourceGroups/VMGroup/providers/Microsoft.Network/networkSecurityGroups/FBI referenced by resource /subscriptions/---/resourceGroups/VMDeployment/providers/Microsoft.Network/networkInterfaces/vInterface was not found. Please make sure that the referenced resource exists, and that both resources are in the same region.",
"details": []
You are probably deploying to a resource group not called vmgroup hence this error.
your resource id's are hardcoded to vmgroup resource group, not to the resource group you are deploying to; change your resourceId() input to:
"[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]"
ps. you have it in 2 places.

Resources