ARM template deployment error for Appinsights roleassignments - azure-resource-manager

We are using below ARM template for role assignement in Appinsights with ADO pipelines, where the template parameters are replacing from ADO pipelines paramters. This worked for one resource deployment and when we tried for multiple resources, ARM template deployment failing with below error.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleDefinitionId": {
"type": "string"
},
"principalId": {
"type": "string"
},
"AppInsightName": {
"type": "string"
}
},
"resources":[
{
"type": "Microsoft.Insights/components/providers/roleAssignments",
"apiVersion": "2017-05-01",
"name": "[concat(parameters('AppInsightName'),'/Microsoft.Authorization/',guid('AppInsightName'))]",
"properties": {
"roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleDefinitionId'))]",
"principalId": "[parameters('principalId')]"
}
}
]
}
Parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"RoleDefinitionId": {
"value": "#{roleDefinitionId}#"
},
"principalId": {
"value": "#{principalId}#"
},
"AppInsightName": {
"value": "#{appInsightName}#"
}
}
}
Input to devops pipeline yaml
parameters:
roleList:
- rolesname: reader_Appinsight_group1
environment: development
principalType: Group
principalid: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
roleDefinitionId: acdd72axxxxxxxxxxxxxxxxxxxxx
appInsightName: myappinsight1
resourceGroup: myappinsight1-rg
- rolesname: reader_Appinsight_group2
environment: development
principalType: Group
principalid: xxxxxxxxxxxxxxxxxxxxxxxxxx
roleDefinitionId: acdd72axxxxxxxxxxxxxxxxxxxxxxxx
appInsightName: myappinsight1
resourceGroup: myappinsight1-rg
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}"}]}}
Again I tried with multiple option for the role definition name to be unique, but got different errors
eg:
"resources":[
{
"type": "Microsoft.Insights/components/providers/roleAssignments",
"apiVersion": "2017-05-01",
"name": "[guid(resourceGroup().id, parameters('RoleDefinitionId'), parameters('principalId'))]",
"properties": {
"roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('RoleDefinitionId'))]",
"principalId": "[parameters('principalId')]"
}
}
]
}
Error for the above
The template resource 'xxxxxxxxxxxxxxxxxxxxxxxx for type 'Microsoft.Insights/components/providers/roleAssignments' at line '18' and column '71' has incorrect segment lengths

The name of a roleAssignment needs to be a function of the principal, role and scope. Once a role exists for a given principal, role & scope under a given name, nothing can be changed on that role assignment.
In your template your roleAssignment name is just a function of the appInsights resource name, which means you can have exactly one of those roleAssignments. Your guid() function in the name needs to be:
guid(parameters('RoleDefinitionId'), parameters('principalId'), parameters('AppInsightName'))
Note that you may have to remove some previously created roleAssignments (for the given principal, role and scope) if they were not created with the same naming algorithm before that template will successfully deploy.

Related

Put Azure Key Vault value in parameter array

I am trying to deploy a App service webapp via ARM template and need to put a secret from a key vault into an app setting (env variable).
I have always simply used an array of values from a parameters file to populate these app settings, but now I am struggling to get a keyvault value into that array. Something like shown below in an ARM parameter file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"someStringParam": {
"value": "stringLiteralValueHere"
},
"envVars": {
"value": [
{
"name": "envVarKeyName",
"value": "stringLiteralValueHere"
},
{
"name": "KVsecret1",
"value": ##KEY VAULT SECRET HERE##
}
]
}
}
}
I have tried using a reference to the keyvault for the value but that errors on deployment.
{
"name": "KVsecret1",
"reference": {
"keyVault": {
"id": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.KeyVault/vaults/<vault_name>"
},
"secretName": "secret1"
}
}
I have also tried using a parameter inside of the parameter file, but that just used the literal string for the value.
"parameters": {
"KVsecret1": {
"reference": {
"keyVault": {
"id": "/subscriptions/<subscription_id>/resourceGroups/<resource_group>/providers/Microsoft.KeyVault/vaults/<vault_name>"
},
"secretName": "KVsecret1"
}
},
"envVars": {
"value": [
{
"name": "envVarKeyName",
"value": "stringLiteralValueHere"
},
{
"name": "KVsecret1",
"value": "[parameters('KVsecret1')]"
}
]
}
}
Is this possible??
EDIT: Adding some detail here.
I am also trying to shoe horn a reference to another resource to get put the app insights instrumentation key into an app setting. Below is what I would like to do, but the copy function needs to use the name of the property and that is dynamic in this case as it changes with the each member of the array from the parameter file.
{
"type": "Microsoft.Web/sites/config",
"apiVersion": "2022-03-01",
"name": "[concat(parameters('backEndwebAppName'),'/appsettings')]",
"kind": "string",
"properties": {
"APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(concat('microsoft.insights/components/',parameters('appInsightsName')),'2020-02-02').InstrumentationKey]",
"secret1FromKeyvault": "[parameters('secret1FromKeyvault')]",
"copy": [
{
"name": "envVarsFromParams",
"count": "[length(parameters('backEndEnvVariables'))]",
"input": {
"name": "[parameters('backEndEnvVariables')[copyIndex('envVarsFromParams').name]]",
"value": "[parameters('backEndEnvVariables')[copyIndex('envVarsFromParams').value]]"
}
}
]
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('backEndwebAppName'))]"
]
},
This isn't possible today within the param file, but in your scenario (if it's as simple as your OP example) you can just union the two in your template. So in your parameter file, you have 2 params kvSecret (the reference) and envVars (all your other env vars) and then in the template use:
"variables": {
"keySecretObj": {
"name": "kvSecret",
"value": "[parameters('kvSecret')]"
},
"envVarsFinal": "[union(parameters(variables('kvSecretObj`), parameters(`envVars`))]"
That help?

Landing Zone Automation - Disable Owner requirement from ESLZ ARM template

I am trying to deploy ESLZ Arm template in this link "https://github.com/Azure/Enterprise-Scale/blob/main/docs/reference/adventureworks/README.md" and it requires owner permission to do that. Is it possible to remove the Global Admin and/or Owner requirement and run the template using contributor role
I created a management group under tenant root and assigned contributor role. Now I'm trying to create additional management groups using below ARM template
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"topLevelManagementGroupPrefix": {
"type": "string",
"metadata": {
"description": "Provide prefix for the management group structure."
}
},
"platformMgs": {
"type": "array",
"defaultValue": [
"management",
"connectivity",
"identity"
],
"metadata": {
"description": "Management groups for platform specific purposes, such as management, networking, identity etc."
}
},
"landingZoneMgs": {
"type": "array",
"defaultValue": [
"online",
"corp"
],
"metadata": {
"description": "These are the landing zone management groups."
}
}
},
"variables": {
"enterpriseScaleManagementGroups": {
"ESLZ": "[concat(parameters('topLevelManagementGroupPrefix'))]",
"platform": "[concat(parameters('topLevelManagementGroupPrefix'), '-', 'platform')]"
}
},
"resources": [
{
// Create management group for platform management groups
"type": "Microsoft.Management/managementGroups",
"apiVersion": "2020-05-01",
"scope": "/",
"name": "[variables('enterpriseScaleManagementGroups').platform]",
"properties": {
"displayName": "[variables('enterpriseScaleManagementGroups').platform]",
"details": {
"parent": {
"id": "[tenantResourceId('Microsoft.Management/managementGroups/', parameters('topLevelManagementGroupPrefix'))]"
}
}
}
}
],
"outputs": {}
}
While deploying the template i'm getting permission error, however able to create management group manually. Am I missing something in this template. Any help is really appreciated
You should be able to be able to deploy the template with contributor permissions at the tenant level... You need an owner/userAccessAdmin/global admin to grant those permissions.

Exception in function does not return exceptions on functions in function monitor

The azure function is a .net core class library that will receive the message based on the namespace of the model being sent (in the filter as eventType) as an . All deployments are being done using arm templates, which is where this struggle is originating from. The function and eventgrid are deployed fine, but I don't know what i'm doing wrong with the subscription. If I create the subscription in the portal then the handler receives the message and displays traffic on the monitor. If I create the subscription as below then it appears exactly the same in the portal as the portal created one but nothing shows up in the monitor. Am I missing a resource or connection that still needs to be created? I read about system topics and how they're made implicitly in some instances but can be made explicitly, is that what I'm missing? This would be easier to debug if there was a place to export the template for those subscriptions but I don't see them.
Function handler
[FunctionName("FunctionName")]
public async Task Run([EventGridTrigger]EventGridEvent eventGridEvent)
{
...
}
}
eventgrid creation
{
"type": "Microsoft.EventGrid/topics",
"apiVersion": "2020-06-01",
"name": "[variables('EventGridName')]",
"location": "[resourceGroup().location]"
}
subscription creations
{
"name": "[concat(variables('eventSubscriptions')[copyIndex()].eventGridName, '/Microsoft.EventGrid/', variables('eventSubscriptions')[copyIndex()].name)]",
"type": "Microsoft.EventGrid/topics/providers/eventSubscriptions",
"apiVersion": "2020-01-01-preview",
"location": "[resourceGroup().location]",
"copy": {
"name": "subscriptionCopy",
"count": "[length(variables('eventSubscriptions'))]"
},
"properties": {
"topic": "[concat('/subscriptions/', subscription().subscriptionId,'/resourcegroups/', resourceGroup().name, '/providers/Microsoft.EventGrid/topics/', variables('eventSubscriptions')[copyIndex()].eventGridName)]",
"destination": {
"endpointType": "AzureFunction",
"properties": {
"resourceId": "[concat('/subscriptions/', subscription().subscriptionId,'/resourcegroups/', resourceGroup().name, '/providers/Microsoft.Web/sites/', variables('eventSubscriptions')[copyIndex()].functionApp, '/functions/' , variables('eventSubscriptions')[copyIndex()].functionName)]",
"maxEventsPerBatch": 1,
"preferredBatchSizeInKilobytes": 64
}
},
"filter": {
"includedEventTypes": [
"[variables('eventSubscriptions')[copyIndex()].eventType]"
]
},
"labels": [],
"eventDeliverySchema": "EventGridSchema"
},
"dependsOn": [
]
}

ARM Template - Creating dependency between two nested templates

I have two nested deployments:
One for resource group A which deploys a managed identity.
Second for resource group B which deploys a Keyvault and sets an access policy to the ManagedIdentity in resource group A.
I would like to have the nested deployment of the resource group B to be dependent on the nested deployment of resource group A.
(My main template is deploying to resource group C)
I have tried:
"dependsOn": [
"[variables('resourceGroupADeploymentName')]"
],
But I keep failing on:
The Resource 'Microsoft.ManagedIdentity/userAssignedIdentities/managedIdentityA' under resource group 'A' was not found.
After a while I see the ManagedIdentityA created in resource group A - so it means the dependsOn did not work and the resourceGroupB deployment did not wait for ResourceGroupA deployment.
I also tried using dependsOn resourceId but it did not work. (and also adding the dependsOn to the actual KeyVault resource deployment within ResourceGroupB)
Any idea how can I have a resource from resource group B dependent on resource from resource group A?
This is my ARM Template for resourceGroupB:
{
"name": "resourceGroupBDeploymentName",
"type": "Microsoft.Resources/deployments",
"apiVersion": "[variables('resourceDeploymentApiVersion')]",
"resourceGroup": "[parameters('B')]",
"subscriptionId": "[parameters('S')]",
"dependsOn": [
"[variables('resourceGroupADeploymentName')]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.KeyVault/vaults/accessPolicies",
"name": "[concat(parameters('deploymentKvName'), '/add')]",
"apiVersion": "[variables('kvApiVersion')]",
"properties": {
"accessPolicies": [
{
"tenantId": "[parameters('S')]",
"objectId": "[reference(variables('ManagedIdentityResourceGroupA'), '2018-11-30').principalId]",
"permissions": {
"keys": [],
"secrets": [],
"certificates": [
"Get"
]
}
}
]
}
}
}
]
}
}
}
This is my ARM Template for resourceGroupA:
{
"name": "[variables('ManagedIdentityResourceGroupA')]",
"type": "Microsoft.Resources/deployments",
"apiVersion": "[variables('resourceDeploymentApiVersion')]",
"resourceGroup": "[parameters('A')]",
"subscriptionId": "[parameters('S')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"name": "[variables('ManagedIdentityResourceGroupA')]",
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"tags": {},
"location": "[resourceGroup().location]"
}
]
}
}
},
I dont think there is a way around that yet. Dependencies only work in the same resource gruop. you can use deployment script resource to run a script that would check the status and that can act as a dependsOn

How do I access the server farm resource id for a web app from within linked ARM template files?

I've got a master ARM deployment file with these resources:
{
"apiVersion": "2015-01-01",
"name": "SharedServicePlanTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"templateLink": { "uri": "[concat(variables('templateBase'), 'serviceplan.template.json')]" },
"parametersLink": { "uri": "[concat(variables('parametersBase'), 'serviceplan.shared.json')]" },
"mode": "Incremental"
}
},
{
"name": "my_website",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', 'ServicePlanShared')]"
],
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', 'ServicePlanShared')]": "Resource",
"displayName": "my_website"
},
"properties": {
"name": "my_website",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', 'ServicePlanShared')]"
}
}
When I try to deploy, I get the following error:
New-AzureRmResourceGroupDeployment : InvalidTemplate: Deployment template validation failed: 'The resource
'Microsoft.Web/serverfarms/ServicePlanShared' is not defined in the template.
I thought that was the whole reason for using the resourceId function, though. I can merge my serviceplan.template.json and the website resource into the same template file, but I'd rather not do that since I will have multiple websites using that plan, and I want to be able to deploy them separately.
Change your dependsOn property to:
"dependsOn" : ["SharedServicePlanTemplate"]
One gotcha with your nested approach is if the name of your service plan changes in the linked parameters file, the resource won't be found. Passing that in as a parameter (whether you use the linked parameters file or pass it through) might be a better approach. A bit orthogonal but something to think about.

Resources