ARM FunctionApp listkeys retrieve previous custom key - azure-resource-manager

We've got ARM deployment template which is working fine, the only issue there is, as we're creating FunctionApp and adding custom key to it that will be referenced in API Management to connect API with FunctionApp backend. It looks like, even with dependsOn in backend resource in template referencing to FunctionKeys resource, listkeys() still fetches one from before deployment. Has anyone faced this scenario and is there anything in particular I'm missing? Or is dependsOn thinking that the key has been deployed already, and even tho future deployments update the key, backend isn't actually waiting for the completion of FunctionKeys resource deployment?
Template snippets:
backends
{
"type": "Microsoft.ApiManagement/service/backends",
"apiVersion": "2018-01-01",
"name": "[concat(parameters('apiManagementServiceName'), '/', variables('functionName'))]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service', parameters('apiManagementServiceName'))]",
"[resourceId('Microsoft.Web/sites', variables('functionName'))]",
"[resourceId('Microsoft.Web/sites/host/functionKeys', variables('functionName'),'default','apimanagement')]"
],
"properties": {
"url": "[concat('https://', variables('functionName'), '.azurewebsites.net/api')]",
"protocol": "http",
"resourceId": "[concat('https://management.azure.com/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/sites/', variables('functionName'))]",
"credentials": {
"header": {
"x-functions-key": [
"[listkeys(concat(variables('functionAppId'), '/host/default/'),'2016-08-01').functionKeys.apimanagement]"
]
}
}
}
}
functionKeys
{
"type": "Microsoft.Web/sites/host/functionKeys",
"apiVersion": "2018-11-01",
"name": "[concat(variables('functionName'), '/default/apimanagement')]",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('functionName'))]"
],
"properties": {
"name": "apimanagement"
}
},

The listKeys call is scheduled too early... In a greenfield scenario the deployment would fail, in brownfield you "get the old key" instead of the new one. It's a "limitation" in the template engine that you can work around. Basically, you need to nest the deployment that uses the listKeys function (i.e. your backend resource). There's a little bit of detail on it here in the Use a Nested Deployment section

Related

dynamically add a user assigned managed identity to a function app during DevOps Deployment

I am creating ARM templates to deploy an azure function.
I can parameterize most things, but I am unable to parameterize UserAssignedManagedIdentities.
The value for a userAssignedManagedIdentity is not a value in the json ARM template, but is instead an object that contains the reference to the managed identity.
ARM does not allow parameterization in this instance.
I can hard code the reference to the managed identity but this is not ideal when deploying across multiple environments.
Is there a way to dynamically add a user assigned managed identity to a function app during Azure Devops Deployment?
This does not work
This works
Update: Adding more detail.
Below is the section of the template, parameterized, and the error that I get.
"resources": [
{
"type": "Microsoft.Web/sites",
"apiVersion": "2022-03-01",
"name": "[parameters('FunctionAppName')]",
"location": "North Europe",
"kind": "functionapp",
"identity": {
"type": "SystemAssigned, UserAssigned",
"userAssignedIdentities": {
"[concat('/subscriptions/', parameters('SubscriptionID'), '/resourcegroups/', parameters('ResourceGroup'),'/providers/Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('UserAssignedManagedEntityName') ]": {}
}
},
"properties": {
"enabled": true,
"hostNameSslStates": [
{
"name": "[concat(parameters('FunctionAppName'),'.azurewebsites.net')]",
"sslState": "Disabled",
"hostType": "Standard"
},

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": [
]
}

Getting "parent resource not found" during ARM template deployment

I have private DNS zone zone.private which is already deployed in resource group and I'm trying to add A record to it with ARM template below which fails with Status Message: Can not perform requested operation on nested resource. Parent resource 'zone.private' not found. (Code:ParentResourceNotFound)
I'm supposed to be able to refer to refer to resources deployed in the same resource group to deploy nested resources but it fails for whatever reason. I have another zone called zone.domain.com deployed to the same resource group and deploying to that succeeds with no issues.
{
"type": "Microsoft.Network/dnsZones/A",
"apiVersion": "2018-05-01",
"name": "[concat('zone.private', '/', 'webexport-lb')]",
"properties": {
"TTL": 3600,
"ARecords": [
{
"ipv4Address": "1.1.1.1"
}
]
}
},
If you have a private DNS zone, you could use Microsoft.Network/privateDnsZones/A instead of Microsoft.Network/dnsZones/A.
So change it like this:
{
"type": "Microsoft.Network/privateDnsZones/A",
"apiVersion": "2018-09-01",
"name": "[concat('zone.private', '/', 'webexport-lb')]",
"properties": {
"ttl": 3600,
"aRecords": [
{
"ipv4Address": "1.1.1.1"
}
]
}
}

ARM Template - create SQL Server firewall rule for Web App

I have created SQL Server and Database, Web App, published website and database and get to the log on screen of the website.
When I log in I receive a 500 including the IP address of the web app which currently isn't allowed to access the newly created SQL Server.
I'd very much like to harvest the assigned IP address (suspect it is AZURE internal IP address) to create a firewall rule in the template.
I'm successfully doing things like adding app settings for storage account keys and database connections strings. And these are working nicely.
Very frustrating to not be able to find any reference to the internal IP of the web site. I've tried the object explorer in the azure portal.
Suggestions appreciated!
Andy
If you are using Azure SQL, about how to set Azure Database firewall, please refer to the document.
Very frustrating to not be able to find any reference to the internal IP of the web site?
If want to let Azure service to access Azure SQL database, we just need to set
Allow access to Azure services on. And default value is on.
We also could get the outbound IPs, we can get them from the azure resources(https://resources.azure.com/) then add the outboundIpAddresses to the Azure SQL firewall rule allowed IP list.
Note : For Azure WebApp, the outboundIpAddresses are not static ips, they may be changed when we restart the WebApp or change WebApp service plan.
If we want to add firewall rule via ARM template, we could use the following demo code:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"testfirewallAdminLogin": {
"type": "string",
"minLength": 1
},
"testfirewallAdminLoginPassword": {
"type": "securestring"
}},
"variables": {
"testfirewallName": "[concat('testfirewall', uniqueString(resourceGroup().id))]"},
"resources": [
{
"name": "[variables('testfirewallName')]",
"type": "Microsoft.Sql/servers",
"location": "[resourceGroup().location]",
"apiVersion": "2014-04-01-preview",
"dependsOn": [ ],
"tags": {
"displayName": "testfirewall"
},
"properties": {
"administratorLogin": "[parameters('testfirewallAdminLogin')]",
"administratorLoginPassword": "[parameters('testfirewallAdminLoginPassword')]"
},
"resources": [
{
"name": "AllowAllWindowsAzureIps",
"type": "firewallrules",
"location": "[resourceGroup().location]",
"apiVersion": "2014-04-01-preview",
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('testfirewallName'))]"
],
"properties": {
"startIpAddress": "x.x.x.x",
"endIpAddress": "x.x.x.x"
}
}
]
}],
"outputs": {
}
}

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