ARM deployment failed for event grid subscription on endpoint webhook url - azure

Using terraform and Azure ARm template , I am trying to create an azure event grid subscription on a function.
This the ARM using for the event grid subscription:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"eventGridTopicName": {
"type": "string",
"metadata": {
"description": "The name of the Event Grid custom topic."
}
},
"eventGridSubscriptionName": {
"type": "string",
"metadata": {
"description": "The name of the Event Grid custom topic's subscription."
}
},
"eventGridSubscriptionUrl": {
"type": "string",
"metadata": {
"description": "The webhook URL to send the subscription events to. This URL must be valid and must be prepared to accept the Event Grid webhook URL challenge request. (RequestBin URLs are exempt from this requirement.)"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "The location in which the Event Grid resources should be deployed."
}
}
},
"resources": [{
"name": "[parameters('eventGridTopicName')]",
"type": "Microsoft.EventGrid/topics",
"location": "[parameters('location')]",
"apiVersion": "2018-01-01"
},
{
"name": "[concat(parameters('eventGridTopicName'), '/Microsoft.EventGrid/', parameters('eventGridSubscriptionName'))]",
"type": "Microsoft.EventGrid/topics/providers/eventSubscriptions",
"location": "[parameters('location')]",
"apiVersion": "2018-01-01",
"properties": {
"destination": {
"endpointType": "WebHook",
"properties": {
"endpointUrl": "[parameters('eventGridSubscriptionUrl')]"
}
},
"filter": {
"includedEventTypes": [
"All"
]
}
},
"dependsOn": [
"[parameters('eventGridTopicName')]"
]
}
]
}
Following the documentation here in order to create the subscription, we have to recover a system key in order to create the complete webhook endpoint. So following this post here, I have used an ARM template to recover the system key called evengrid_extension.
So everything goes well except during the arm deployment of the eventgrid subscription. I have this error:
Error waiting for deployment: Code="DeploymentFailed"
Message="At least one resource deployment operation failed. Please
list deployment operations for details. Please see
https://aka.ms/arm-debug for usage details."
Details=[{"code":"Conflict","message":"{\r\n
\"status\": \"Failed\",\r\n
\"error\": {\r\n \"code\": \"ResourceDeploymentFailure\",\r\n
\"message\": \"The resource operation completed with terminal provisioning state 'Failed'.\",\r\n
\"details\": [\r\n {\r\n
\"code\": \"Url validation\",\r\n
\"message\": \"The attempt to validate the provided endpoint https://myFunctionName.azurewebsites.net/runtime/webhooks/eventgrid
failed.
\For more details, visit https:
//aka.ms/esvalidation.\"\r\n }\r\n ]\r\n }\r\n}"}]
I check my code n terraform in order to be sure that I am using the right value for all parameters in this arm template and everything is ok. I have the right topic name, the right endpoint with all value filled in. So I don't understand what I am missing here. I was wondering too if I am using the right system key. I know that there are a system key named durabletask_extension, and another one named eventgrid_extension. But in fact I have tried with both and the same error occured.
Update
Just notice that the keys i-e durabletask_extension and eventgrid_extension are both system keys. So in my arm template to recover these works well and I recover the right system key by using only eventgrid_extension.
Here my code for terraform:
resource "azurerm_eventgrid_topic" "eventgrid_topic" {
name = "topicName"
location = var.main_location
resource_group_name = azurerm_resource_group.name
}
resource "azurerm_template_deployment" "eventgrid_subscription" {
name = "EventGridSbscription"
resource_group_name = azurerm_resource_group.environment.name
template_body = file("./arm/event-grid-subscription.json")
parameters = {
eventGridTopicName = "${azurerm_eventgrid_topic.eventgrid_topic.name}"
eventGridSubscriptionName = "eventgrid-myFunctionName"
eventGridSubscriptionUrl = "https://${azurerm_function_app.function.name}.azurewebsites.net/runtime/webhooks/eventgrid?functionName=${azurerm_function_app.function.name}&code=${lookup(azurerm_template_deployment.function_key.outputs, "systemKey")}"
location = var.main_location
}
deployment_mode = "Incremental"
depends_on = [
azurerm_template_deployment.function_key
]
}
So I do not understand why my susbription deployment failed, or what I am missing in order to automate this settings with terraform.
Following the doc here I understand too that:
If you don't have access to the application code (for example, if
you're using a third-party service that supports webhooks), you can
use the manual handshake mechanism. Make sure you're using the
2018-05-01-preview API version or later (install Event Grid Azure CLI
extension) to receive the validationUrl in the validation event. To
complete the manual validation handshake, get the value of the
validationUrl property and visit that URL in your web browser. If
validation is successful, you should see a message in your web browser
that validation is successful. You'll see that event subscription's
provisioningState is "Succeeded".
So, there is a way to make a validation using terraform or another way to automate this validation ?

The template is right, you just misunderstand something in the eventGridSubscriptionUrl. Take a look at the URL. The URL shows like this:
Version 2.x runtime
https://{functionappname}.azurewebsites.net/runtime/webhooks/eventgrid?functionName={functionname}&code={systemkey}
Version 1.x runtime
https://{functionappname}.azurewebsites.net/admin/extensions/EventGridExtensionConfig?functionName={functionname}&code={systemkey}
The functionappname is what you set as the value azurerm_function_app.function.name, but functionname is not.
You get the existing function name through the Azure REST API Web Apps - Get Function.
And in Terraform, it seems there is no function resource in the function app for you to create. But you can also use the template to create the function and output the function name. Then you can set it in the URL. You can get more details about function in the Azure Template here and the function name shows in the property.

Related

Azure ARM templates - Stream Analytics identity.principalId as output

I have a working ARM Template for a Stream Analytics job which creates it's own 'managed identity' which can be queried using AZ CLI, e.g.:
'az stream-analytics job show -g <resource_group> -n <stream_analytics_job_name> -o json --query 'identity.principalId'
(Deployment is via Terraform 'azurerm_template_deployment' module).
Added an ARM template output to return this key using 'listkeys':
"outputs": {
"principalId": {
"type": "string",
"value": "[listkeys(resourceId('Microsoft.StreamAnalytics/streamingjobs', parameters('StreamAnalyticsJobName')), parameters('ASAApiVersion')).identity.principalId]"
}
}
}
Now the ARM deployment fails as seen in the resource group deployments list with:
{
"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": "NotFound",
"message": "{\r\n \"code\": \"NotFound\",\r\n \"message\": \"The webpage cannot be found.\",\r\n \"details\": {\r\n \"code\": \"404\",\r\n \"message\": \"The webpage cannot be found.\",\r\n \"correlationId\": \"<redacted>\",\r\n \"requestId\": \"<redacted>\"\r\n }\r\n}"
}
]
}
From the resource group Activity Log, under the 'Write Steam Analytics Job', there are failed operations for listkeys - none of which give any further clues.
'identity.principalId' found for this Stream Analytics job via https://resources.azure.com:
...etc...
"identity": {
"principalId": "<redacted>",
"tenantId": "<redacted>",
"type": "SystemAssigned"
},
Have also tried the following ARM template output with the same result:
"outputs": {
"principalId": {
"type": "string",
"value": "[listkeys(resourceId('Microsoft.StreamAnalytics/streamingjobs', parameters('StreamAnalyticsJobName')), parameters('ASAApiVersion')).principalId]"
}
}
Similar issues found but none for Stream Analytics:
Azure ARM templates : DocumentDB primaryMasterKey as OUTPUT
How to return Redis primaryKey via ARM template output?
https://devkimchi.com/2018/01/05/list-of-access-keys-from-output-values-after-arm-template-deployment/
Research:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/template-outputs?tabs=azure-powershell
https://learn.microsoft.com/en-us/azure/stream-analytics/powerbi-output-managed-identity
https://www.terraform.io/docs/providers/azurerm/r/template_deployment.html
Any help much appreciated, thanks.
Came across the answer elsewhere (How to get Principal Id in app service using Arm template?) as I was trying to obtain a principal ID not a key (my bad).
Using the following ARM Template output worked:
"outputs": {
"principalId": {
"type": "string",
"value": "[reference(resourceId('Microsoft.StreamAnalytics/streamingjobs', parameters('StreamAnalyticsJobName')), parameters('ASAApiVersion'), 'Full').identity.principalId]"
}
}

How to create unique Guid at each deployment or hide the parameter newGuid() function from user in ARM template?

I need unique identifier at each custom deployment using ARM template for assigning resource name(uniqueName) which should be globally unique. As per documentation newGuid() returns a value in the format of a globally unique identifier. This function can only be used in the default value for a parameter.
As newGuid() function can be called in only parameters section, but I don't want to give the input block to user, because user can edit the field while deploying this, so how can I hide that from user or is there any other way to create same unique guid globally at each deployment?
I have tried creating same unique guid using this in variables section, but it works only for few times of deployment. I'm not sure deployment issue but it may possible because guid function does not make unique field all the time.
"variables": {
"uniqueName":"[guid(resourceGroup().id, deployment().name)]"
}
I have this template.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appname": {
"defaultValue": "xyz",
"type": "String"
},
"uniqueName": {
"defaultValue": "[newGuid()]",
"type": "String"
},
"myIdentity": {
"type": "String"
}
},
"variables": {
"location": "[resourceGroup().location]",
"ResourceGroupName": "[resourceGroup().name]"
},
"resources": [
{
"type": "Microsoft.Resources/deploymentScripts",
"apiVersion": "2019-10-01-preview",
"name": "[parameters('uniqueName')]",
"location": "[variables('location')]",
"kind": "AzureCLI",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[resourceID('Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('myIdentity'))]": {}
}
},
"properties": {
"AzCliVersion": "2.0.80",
"timeout": "PT10M",
"arguments": "[parameters('appname')]",
"cleanupPreference": "OnSuccess",
"retentionInterval": "P1D",
"supportingScriptUris": [
"https://some-uri/test.sh"
],
"scriptContent": "[concat('./test.sh ', string(parameters('appname')), ' > $AZ_SCRIPTS_OUTPUT_PATH')]"
}
}
],
"outputs": {
"result": {
"type": "String",
"value": "[base64(string(reference(parameters('resourceName')).outputs))]"
}
}
}
Deployment error after some successful deployments is this.
{"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":"Conflict","message":"{\r\n \"status\": \"canceled\",\r\n \"error\": {\r\n \"code\": \"ResourceDeploymentFailure\",\r\n \"message\": \"The resource operation completed with terminal provisioning state 'canceled'.\",\r\n \"details\": [\r\n {\r\n \"code\": \"DeploymentScriptExceededMaxAllowedTime\"\r\n }\r\n ]\r\n }\r\n}"}]}
Point 1: This issue resolved after adding this property in resources.
After adding this property, make sure some containers are not running. Delete storage accounts and containers manually which are created by previous failed deployments.
"cleanupPreference": "Always"
Always: Delete the automatically created resources once script execution gets in a terminal state. If an existing storage account is used, the script service deletes the file share created in the storage account. Because the deploymentScripts resource may still be present after the resources are cleaned up, the script service persists the script execution results, for example, stdout, outputs, return value, etc. before the resources are deleted.
Initially property was set to "cleanupPreference": "OnSuccess" which was not removing the created storage account and containers in failed deployments and making a issues while next deployment.
for more details check this azure document
Point 2. Creating unique identifier can be done using newGuid() function, but only in parameters section of templates.
Which creates unique Id globally, but if you don't want to show user input box then guid() also works.
"variables": {
"uniqueName":"[guid(resourceGroup().id, deployment().name)]"
}
refer guid for more details.

Enable HTTPS on Azure Front Door custom domain with ARM template deployment

I am deploying an Azure Front Door via an ARM template, and attempting to enable HTTPS on a custom domain.
According to the Azure documentation for Front Door, there is a quick start template to "Add a custom domain to your Front Door and enable HTTPS traffic for it with a Front Door managed certificate generated via DigiCert." However, while this adds a custom domain, it does not enable HTTPS.
Looking at the ARM template reference for Front Door, I can't see any obvious way to enable HTTPS, but perhaps I'm missing something?
Notwithstanding the additional information below, I'd like to be able to enable HTTPS on a Front Door custom domain via an ARM template deployment. Is this possible at this time?
Additional information
Note that there is a REST operation to enable HTTPS, but this does not seem to work with a Front Door managed certificate -
POST https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/frontDoors/{frontDoorName}/frontendEndpoints/{frontendEndpointName}/enableHttps?api-version=2019-05-01
{
"certificateSource": "FrontDoor",
"protocolType": "ServerNameIndication",
"minimumTLSVersion": "1.2"
}
There is also a Az PowerShell cmdlet to enable HTTP, which does work.
Enable-AzFrontDoorCustomDomainHttps -ResourceGroupName "lmk-bvt-accounts-front-door" -FrontDoorName "my-front-door" -FrontendEndpointName "my-front-door-rg"
UPDATE: This implementation currently seems to be unstable and is working only intermittently, which indicates it may not be production ready yet.
This now actually seems to be possible with ARM templates, after tracking down the latest Front Door API (2020-01-01) specs (which don't appear to be fully published in the MS reference websites yet):
https://github.com/Azure/azure-rest-api-specs/tree/master/specification/frontdoor/resource-manager/Microsoft.Network/stable/2020-01-01
There's a new customHttpsConfiguration property in the frontendEndpoint properties object:
"customHttpsConfiguration": {
"certificateSource": "AzureKeyVault" // or "FrontDoor",
"minimumTlsVersion":"1.2",
"protocolType": "ServerNameIndication",
// Depending on "certificateSource" you supply either:
"keyVaultCertificateSourceParameters": {
"secretName": "<secret name>",
"secretVersion": "<secret version>",
"vault": {
"id": "<keyVault ResourceID>"
}
}
// Or:
"frontDoorCertificateSourceParameters": {
"certificateType": "Dedicated"
}
}
KeyVault Managed SSL Certificate Example
Note: I have tested this and appears to work.
{
"type": "Microsoft.Network/frontdoors",
"apiVersion": "2020-01-01",
"properties": {
"frontendEndpoints": [
{
"name": "[variables('frontendEndpointName')]",
"properties": {
"hostName": "[variables('customDomain')]",
"sessionAffinityEnabledState": "Enabled",
"sessionAffinityTtlSeconds": 0,
"webApplicationFirewallPolicyLink": {
"id": "[variables('wafPolicyResourceId')]"
},
"resourceState": "Enabled",
"customHttpsConfiguration": {
"certificateSource": "AzureKeyVault",
"minimumTlsVersion":"1.2",
"protocolType": "ServerNameIndication",
"keyVaultCertificateSourceParameters": {
"secretName": "[parameters('certKeyVaultSecret')]",
"secretVersion": "[parameters('certKeyVaultSecretVersion')]",
"vault": {
"id": "[resourceId(parameters('certKeyVaultResourceGroupName'),'Microsoft.KeyVault/vaults',parameters('certKeyVaultName'))]"
}
}
}
}
}
],
...
}
}
Front Door Managed SSL Certificate Example
Looks like for a FrontDoor managed certificate you would need to set:
Note: I have not tested this
{
"type": "Microsoft.Network/frontdoors",
"apiVersion": "2020-01-01",
"properties": {
"frontendEndpoints": [
{
"name": "[variables('frontendEndpointName')]",
"properties": {
"hostName": "[variables('customDomain')]",
"sessionAffinityEnabledState": "Enabled",
"sessionAffinityTtlSeconds": 0,
"webApplicationFirewallPolicyLink": {
"id": "[variables('wafPolicyResourceId')]"
},
"resourceState": "Enabled",
"customHttpsConfiguration": {
"certificateSource": "FrontDoor",
"minimumTlsVersion":"1.2",
"protocolType": "ServerNameIndication",
"frontDoorCertificateSourceParameters": {
"certificateType": "Dedicated"
}
}
}
}
],
...
}
}
I was able to successfully make an enableHttps REST Call using the Azure Management API.
I got a successful response and can see the resource results in the portal.azure.com and resource.azure.com sites.
However I am pretty sure the Management API, and PowerShell methods are the only ways supported right now. Since there is likely some validation required on the Certificate and Handling, they didn't include that yet in the ARM Templates. Given validation can be quite important, it is best you confirm your configuration is workable in the UI first, before automating it (IMHO).
According to this discussion this seems only possible via the REST API (see e.g. this answer) and not (yet) via ARM.
I managed to get this working with an ARM template. The below link shows you how to do this using Azure Front Door as a certificate source:
https://github.com/Azure/azure-quickstart-templates/blob/master/101-front-door-custom-domain/azuredeploy.json
I drew inspiration from this for deploying a certificate from Azure Key Vault for a custom domain. Here are the relevant elements from the ARM template that I am using:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hubName": {
"type": "string",
"metadata": {
"description": "Name to assign to the hub. This name will prefix all resources contained in the hub."
}
},
"frontdoorName": {
"type": "string",
"metadata": {
"description": "Name to assign to the Frontdoor instance"
}
},
"frontdoorCustomDomain": {
"type": "string",
"metadata": {
"description": "The custom domain name to be applied to the provisioned Azure Frontdoor instance"
}
},
"keyVaultCertificateName": {
"type": "string",
"metadata": {
"description": "Name of the TLS certificate in the Azure KeyVault to be deployed to Azure Frontdoor for supporting TLS over a custom domain",
"assumptions": [
"Azure KeyVault containing the TLS certificate is deployed to the same resource group as the resource group where Azure Frontdoor will be deployed to",
"Azure KeyVault name is the hub name followed by '-keyvault' (refer to variable 'keyVaultName' in this template)"
]
}
},
...
},
"variables": {
"frontdoorName": "[concat(parameters('hubName'), '-', parameters('frontdoorName'))]",
"frontdoorEndpointName": "[concat(variables('frontdoorName'), '-azurefd-net')]",
"customDomainFrontdoorEndpointName": "[concat(variables('frontdoorName'), '-', replace(parameters('frontdoorCustomDomain'), '.', '-'))]",
"keyVaultName": "[concat(parameters('hubName'), '-keyvault')]",
"frontdoorHostName": "[concat(variables('frontdoorName'), '.azurefd.net')]",
...
},
"resources": [
{
"type": "Microsoft.Network/frontdoors",
"apiVersion": "2020-05-01",
"name": "[variables('frontdoorName')]",
"location": "Global",
"properties": {
"resourceState": "Enabled",
"backendPools": [...],
"healthProbeSettings": [...],
"frontendEndpoints": [
{
"id": "[concat(resourceId('Microsoft.Network/frontdoors', variables('frontdoorName')), concat('/FrontendEndpoints/', variables('frontdoorEndpointName')))]",
"name": "[variables('frontdoorEndpointName')]",
"properties": {
"hostName": "[variables('frontdoorHostName')]",
"sessionAffinityEnabledState": "Enabled",
"sessionAffinityTtlSeconds": 0,
"resourceState": "Enabled"
}
},
{
"id": "[concat(resourceId('Microsoft.Network/frontdoors', variables('frontdoorName')), concat('/FrontendEndpoints/', variables('customDomainFrontdoorEndpointName')))]",
"name": "[variables('customDomainFrontdoorEndpointName')]",
"properties": {
"hostName": "[parameters('frontdoorCustomDomain')]",
"sessionAffinityEnabledState": "Enabled",
"sessionAffinityTtlSeconds": 0,
"resourceState": "Enabled"
}
}
],
"loadBalancingSettings": [...],
"routingRules": [...],
"backendPoolsSettings": {
"enforceCertificateNameCheck": "Enabled",
"sendRecvTimeoutSeconds": 30
},
"enabledState": "Enabled",
"friendlyName": "[variables('frontdoorName')]"
}
},
{
"type": "Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration",
"apiVersion": "2020-07-01",
"name": "[concat(variables('frontdoorName'), '/', variables('customDomainFrontdoorEndpointName'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Network/frontdoors', variables('frontdoorName'))]"
],
"properties": {
"protocolType": "ServerNameIndication",
"certificateSource": "AzureKeyVault",
"minimumTlsVersion": "1.2",
"keyVaultCertificateSourceParameters": {
"secretName": "[parameters('keyVaultCertificateName')]",
"vault": {
"id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', variables('keyVaultName'))]"
}
}
}
}
]
}
Azure Front Door classic now seems to support both managed certificates and custom certificates for custom domains. At least there are quickstart templates in the official repo from Microsoft exactly for these cases:
managed certificate
custom certificate
They both use Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration subresource of the Front Door, currently with API version 2020-07-01. Only the parent subresource is documented in the templates reference, though.
The name of the customHttpsConfiguration resource is "default", so when the resource is specified as a top-level resource in the template, its complete name is something like "myfrontdoorafd/www-example-com/default".
Using Bicep (which transpiles to JSON ARM templates and which I highly recommend), the important part of the template looks like this:
param frontDoorName string
param customDomainName string
var frontEndEndpointCustomName = replace(customDomainName, '.', '-')
resource frontDoor 'Microsoft.Network/frontDoors#2020-01-01' = {
name: frontDoorName
properties: {
frontendEndpoints: [
{
name: frontEndEndpointCustomName
properties: {
hostName: customDomainName
...
}
}
...
]
...
}
...
resource frontendEndpoint 'frontendEndpoints' existing = {
name: frontEndEndpointCustomName
}
}
// This resource enables a Front Door-managed TLS certificate on the frontend.
resource customHttpsConfiguration 'Microsoft.Network/frontdoors/frontendEndpoints/customHttpsConfiguration#2020-07-01' = {
parent: frontDoor::frontendEndpoint
name: 'default'
properties: {
protocolType: 'ServerNameIndication'
certificateSource: 'FrontDoor'
frontDoorCertificateSourceParameters: {
certificateType: 'Dedicated'
}
minimumTlsVersion: '1.2'
}
}
Note that the deployment will be in progress till the certificate is actually issued and deployed to all points of presence (PoP) of Azure. This may take really long and even fail due to RequestTimeout. If you want to just start the operation and let it complete asynchronously, use e.g. the enable-https subcommand in Azure CLI. Even after the failure, the customHttpsProvisioningState is Pending and the certificate provisioning process may complete successfully.
Also note that when you have many frontend endpoints and changes happen frequently but most frontend endpoints stay unchanged, the pattern from this template cannot be generalized just by specifying multiple customHttpsConfiguration instances for multiple frontend endpoints. Such a generalization is not efficient and likely hits the rate limit of the underlying API (429 TooManyRequests) because the API is called even when the endpoint already has the HTTPS configuration.
In such a case, I was able to use nested templates and conditional deployment to deploy the customHttpsConfiguration subresource only when the frontend endpoint's property customHttpsProvisioningState has the value of Disabled. This works OK even with tens of frontend endpoints when a new frontend endpoint is added (and it should get a managed certificate). Even in deployment mode Complete, the once-applied configuration persists.

How to check the resource exists in the arm template

How do i identify the azure resource is exists or not in the ARM templates by the resource type and identifier
It is actually kind of possible. You can use resource group tags to mark a current deployed version and skip deployment if the tag is set. All this could be achieved via linked template.
Note that we don't check for resource existence per se but we still allow writing ARM template that could contain one time initialization templates. The last will restore the resource if resource group was deleted and resources were lost (given that you created the resource group again). You can extend this to support per-resource tags which will be more useful in some cases.
The template that starts the deployment may look like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"DeploymentTemplateLink": {
"type": "string"
},
"DeploymentVersion": {
"defaultValue": 1,
"type": "int"
}
},
"variables": {
"rgWithDefaultVersion": {
"tags": {
"Version": "0"
}
}
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "DeploymentTemplate",
"condition": "[less(int(union(variables('rgWithDefaultVersion'), resourceGroup()).tags['Version']), parameters('DeploymentVersion'))]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[parameters('DeploymentTemplateLink')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"DeploymentVersion": {
"value": "[parameters('DeploymentVersion')]"
}
}
}
}
]
}
The linked template's condition looks into tags and returns true only if current version (stored in the tag) is less than the requested one. You don't actually have to maintain versioning: just don't set the DeploymentVersion parameter and it will deploy only for the first time. If you decide to redeploy anyway you have always an option to increase the version, which will cause deployment of the linked template (aka "main deployment").
The main deployment template is on you, but it should contain a tags resource in order to maintain the logic.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"DeploymentVersion": {
"defaultValue": 1,
"type": "int"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Resources/tags",
"name": "default",
"apiVersion": "2019-10-01",
"dependsOn": [],
"properties": {
"tags": {
"Version": "[string(parameters('DeploymentVersion'))]"
}
}
}
]
}
Remark for those who didn't understand the union() and rgWithDefaultVersion thing. ARM template deployment will fail if referenced object doesn't contain a property. In our case we have two such properties: 'tags' and 'Version'. 'Tags' will exist only if particular resource group has or ever had tags. 'Version' will exist only after we already wrote it once (in the main deployment). Therefore before we access them we perform union() operation on returned object with a proper default one, ensuring that we can safely access the mentioned properties.
there is no way of doing that in an arm template. you can use some external source (like powershell) to determine that and pass in parameter with appropriate value, alternatively you can use tags to figure that out (have a tag that represents an existence\absence of a resource).
Resource Manager provides the following functions for getting resource values: Resource functions for Azure Resource Manager templates
You could wrap your template with a piece of powershell\whatever, that would determine if the resource exists, and pass in the parameter value depending on that and use a conditional statement in the template that would decide what to do based on the input (but the input has to come from elsewhere)
I needed a solution to this recently to basically do an incremental update to a SQL server. Since you can't do this; the template will fail with a NameAlreadyExists error.
So I needed to check the resource doesn't exist and only create if it doesn't.
Add a "condition" check for the azure resource id exists; don't create if it does.
{
...
"condition": "[empty(resourceId('[resourceGroup().id]', 'Microsoft.SQL/servers', parameters('serverName')))]",
...
}
You can do this for any resource type.

Configure programmatic deployment for Azure Bing maps

I'm trying to add BingMaps to our resource template.
this is the template so far:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"mapsName": {
"type": "string"
}
},
"variables": {
"location": "[resourceGroup().location]"
},
"resources": [
{
"apiVersion": "2015-07-02",
"type": "Microsoft.BingMaps/mapApis",
"name": "[parameters('mapsName')]",
"location": "westus",
"plan": {
"publisher": "bingmaps",
"product": "mapapis",
"name": "myMapsTest",
"promotionCode": null
},
"properties": {
"provisioningState": "Succeeded"
}
}
],
"outputs": {
}
}
It gives this error message:
New-AzureRmResourceGroupDeployment : 14:22:50 - Resource
Microsoft.BingMaps/mapApis 'myMapsName' failed with message 'User
failed validation to purchase resources. Error message: 'Legal terms
have not been accepted for this item on this subscription. To accept
legal terms, please go to the Azure portal
(http://go.microsoft.com/fwlink/?LinkId=534873) and configure
programmatic deployment for the Marketplace item or create it there
for the first time''
How can I configure programmatic deployment for Azure Bing maps?
The current workaround is: create the marketplace item once under the very same subscription you are going to use for the programmatic deployment. It worked me like charm.. (although I am not happy this interactive hocus pocus at all)
The supposed correct solution is not working yet (issue), but hopefully will. See below:
Seems to be an Azure Subscription issue - what type of subscription do you have (pay as you go, free, EA?).
What location did you try to deploy to?
Also - are you able to provision "Bing Maps API for Enterprise" offering for the marketplace?

Resources