Use [deployment().properties.templateLink.uri] on local deployments through ARM templates - azure

When I try to use an ARM linked Template like...
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"name": "[concat('EventHubLinkedTemplate-', parameters('eventHubNames')[copyindex('eventHubNameIterator')])]",
"copy": {
"name": "eventHubNameIterator",
"count": "[length(parameters('eventHubNames'))]"
},
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[uri(deployment().properties.templateLink.uri, '/eventHub/template.json')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"consumerGroups": "[parameters('consumerGroups')]",
"eventHubName": "[concat(variables('eventHubNamespace'), '/', parameters('eventHubNames'))]"
}
}
}
... from parent ARM template, using deploy option of Visual Studio 2019 ARM project, the deployment fails within the next message:
07:48:12 - Resource Microsoft.Resources/deployments 'EventHubLinkedTemplate-test' failed with message '{
"error": {
"code": "InvalidTemplate",
"message": "Unable to process template language expressions for resource '/subscriptions/********-****-****-****-************/resourceGroups/*****/providers/Microsoft.Resources/deployments/EventHubLinkedTemplate-test' at line '127' and column '9'. 'The language expression property 'templateLink' doesn't exist, available properties are 'template, templateHash, parameters, mode, provisioningState'.'",
"additionalInfo": [
{
"type": "TemplateViolation",
"info": {
"lineNumber": 127,
"positionNumber": 9,
"snippet": ""
}
}
]
}
}'
Does anyone knows any way to use [deployment().properties.templateLink.uri] on local deployments through ARM templates?
As far as I can found on the documentation, seems it is not supported yet...
https://github.com/Azure/azure-quickstart-templates/blob/master/1-CONTRIBUTION-GUIDE/best-practices.md
https://github.com/MicrosoftDocs/azure-docs/issues/8748

No, it's impossible.
As the official article said:
The templateLink property is only returned when linking to a remote template with a URL. If you're using a local template, that property isn't available.

No, this is not possible, because for that to work you have to upload the template (else, how you would get a link??).
A simple workaround would be to have a powershell script that uploads everything and starts the deployment. Using Visual Studio for ARM Template development is not the best experience anyway.

Related

Resource [parameters('mgName')] Location must be an expression or 'global'

I am experimenting with Azure Management Groups Arm template.
As you can see in this link, I have this Arm template:
{
"$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"mgName": {
"type": "string",
"defaultValue": "[concat('mg-', uniqueString(newGuid()))]"
}
},
"resources": [
{
"type": "Microsoft.Management/managementGroups",
"apiVersion": "2021-04-01",
"name": "[parameters('mgName')]",
"scope": "/",
"location": "eastus",
"properties": {}
}
],
"outputs": {
"output": {
"type": "string",
"value": "[parameters('mgName')]"
}
}
}
Saved as mg.json and it works fine.
Later I start experimenting with validating and testing Arm template using Test-AzTemplate (https://github.com/Azure/arm-ttk). When I run following command to test Arm Template:
Test-AzTemplate -TemplatePath .\mg.json
I get this test error:
[-] Resources Should Have Location (3 ms)
Resource [parameters('mgName')] Location must be an expression or 'global'
Now when I remove "location": "eastus", line form Arm template, the test does not fail and pass the test.
My Question:
Is this location in Management Group Arm required or not required? And why it is failing when it is part of Microsoft documentation! Any idea?
Location is not required in Management Group. As you can check this Azure Create Management Group REST API documentation, location is not needed here.
That's why in the template either you can remove the location or you can provide 'global' as the value, as the test command output specifies.

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.

Linked ARM templates result in Invalid Template

Let's start with what I'm trying to accomplish.
What I'd like to do is create an ARM template where I'm retrieving secrets from the Azure Key Vault, without specifying too much details on the specific Key Vault. Sounds easy enough and probably something which is implemented in every production system.
Doing a quick search I discovered the syntax for such a thing is the following.
"parameters": {
"adminPassword": {
"reference": {
"keyVault": {
"id": "[resourceId(subscription().subscriptionId, parameters('vaultResourceGroup'), 'Microsoft.KeyVault/vaults', parameters('vaultName'))]"
},
"secretName": "[parameters('secretName')]"
}
},
From what I gather you need to add this in an external template as the used methods can't be used in the 'main' method.
So I started creating a 'main' ARM template and a new template called appservice.json which contains all of the stuff necessary for my App Service, including the appSettings block which need the secrets from Key Vault.
In my main template I've done the following, as described in the documentation.
{
"apiVersion": "2017-05-10",
"name": "linkedTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[uri(deployment().properties.templateLink.uri, 'appservice.json')]",
"contentVersion": "1.0.0.0"
},
However, when deploying I'm confronted with the following error.
"error": {
"code": "InvalidTemplate",
"message": "Unable to process template language expressions for resource '/subscriptions/ba49bae7-2b37-4504-914b-441763a2bcd3/resourceGroups/cfpexchange-jan-test/providers/Microsoft.Resources/deployments/linkedTemplate' at line '1' and column '1526'. 'The language expression property 'templateLink' doesn't exist, available properties are 'name, properties'.'"
}
I also tried the following because I noticed IntelliSense in Visual Studio told me properties doesn't exits and I should use templateLink directly.
{
"apiVersion": "2017-05-10",
"name": "linkedTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[uri(deployment().templateLink.uri, 'appservice.json')]",
"contentVersion": "1.0.0.0"
},
This of course isn't right either.
"error": {
"code": "InvalidTemplate",
"message": "Unable to process template language expressions for resource '/subscriptions/ba49bae7-2b37-4504-914b-441763a2bcd3/resourceGroups/cfpexchange-jan-test/providers/Microsoft.Resources/deployments/linkedTemplate' at line '1' and column '1526'. 'The language expression property 'templateLink' doesn't exist, available properties are 'name, properties'.'"
}
And when using it as a variable, like in the documentation
"variables": {
"sharedTemplateUrl": "[uri(deployment().properties.templateLink.uri, 'shared-resources.json')]"
},
...
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('sharedTemplateUrl')]",
"contentVersion": "1.0.0.0"
},
I'm also getting an error.
2018-07-04T19:14:34.4204720Z ##[error]Deployment template validation failed: 'The template variable 'sharedTemplateUrl' is not valid: The language expression property 'templateLink' doesn't exist, available properties are 'template, parameters, mode, debugSetting, provisioningState'.. Please see https://aka.ms/arm-template-expressions for usage details.'.
At this time I'm a bit lost. From what I understand from the documentation it appears I'm doing everything correct. Apparently I'm not. Any ideas on how to continue with this?
For completeness, the two actual files which I'm using at this time:
The main ARM template: https://github.com/Jandev/CfpExchange/blob/b998bb0c49cf369b2f7584e20556aefb5224ace0/Deployment/CFPExchange.json
The linked template: https://github.com/Jandev/CfpExchange/blob/522f48aa19d19730e5474ce11ead57a48d330389/Deployment/appservice.json
There have been multiple iterations on it in order to try and fix it, but as mentioned noting appeared to be working so far.
First of all, this is how you are supposed to use KV in a nested template. Example with Admin password:
"adminPassword": {
"reference": {
"keyVault": {
"id": "kv_resource_id"
},
"secretName": "[concat('secret', copyindex(1))]"
}
},
This section supposed to be in the nested template parameters WHEN you invoke it (just look at the example link).
Your error seems to be in the variable. So the templateLink property is only available when you deploy your main template from the url, if you use local file to deploy main template it wont work.
Compare this to remote template execution:
New-AzureRmResourceGroupDeployment -ResourceGroupName xxx -TemplateUri 'https://paste.ee/d/XI1Rc/0'
As this is a remote url, it should show you the same output, but this time with a templateLink property.
Name Type Value
=============== ========================= ==========
test Object {
"name": "theDeploymentName",
"properties": {
"templateLink": {
"uri": "theRemoteTemplateUri"
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [],
"outputs": {
"test": {
"type": "Object",
"value": "[deployment()]"
}
}
},
"parameters": {},
"mode": "Incremental",
"provisioningState": "Accepted"
}
}

How to Copy Azure SQL Database using ARM Template

Not sure if it is supported in ARM. I could find power-shell references only.
You cannot currently deploy a dacpac with an ARM template. The link above uses PowerShell but not ARM. You can create however create a database from a source database as a copy using an ARM template.
A simple way to find an example template for any Azure action is to perform the action in the portal - in this case, copy a database - and then open the appropriate resource group blade in the portal, list the deployments, locate the deployment just submitted and open it. Then select ViewTemplate from the menu bar and examine both the Template tab and the Parameters tab. These show you the full template and the parameter values actually used. You can then download the template, with accompanying Powershell script.
For database copy, here is the template:
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"databaseName": {
"type": "string"
},
"serverName": {
"type": "string"
},
"location": {
"type": "string"
},
"createMode": {
"type": "string"
},
"sourceDatabaseId": {
"type": "string"
},
"requestedServiceObjectiveName": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "2014-04-01-preview",
"location": "[parameters('location')]",
"name": "[concat(parameters('serverName'), '/', parameters('databaseName'))]",
"properties": {
"createMode": "[parameters('createMode')]",
"sourceDatabaseId": "[parameters('sourceDatabaseId')]",
"requestedServiceObjectiveName": "[parameters('requestedServiceObjectiveName')]"
},
"type": "Microsoft.Sql/servers/databases"
}
]
}
For database copy createMode = 'Copy'
And be sure to provide a fully qualified resourceId formatted as follows:
"/subscriptions/<sub-id>/resourceGroups/<resourceGroupName>/providers/Microsoft.Sql/Servers/<server-name>/databases/<database-name>"
Make sure the resource group name capitalization is correct and that the server name is all lower case.
You can use the sourceDatabaseId property to reference another database. Then you can specify various createModes depending on what type of database you would like to create:
{
"properties": {
"createMode": "OnlineSecondary",
"sourceDatabaseId": "[resourceId('Microsoft.Sql/servers/databases', variables('sql01Name'), 'databasename')]"
}
}
http://msdn.microsoft.com/en-us/library/azure/mt163685.aspx
The answer above from #Bill Gibson - MSFT works if you are using a Microsoft.Sql/servers resource, however if you're using a Microsoft.Sql/managedInstances resource you'll need to use the appropriate Microsoft.Sql/managedInstance/databases - ARM Template.
The following works for me to perform a PointInTimeRestore accessing a source database that lives in another resource group (the variables and parameters are left as an exercise to the reader):
{
"type": "Microsoft.Sql/managedInstances/databases",
"name": "[concat(variables('destinationSqlManagedInstanceName'), '/', 'AdventureWorks')]",
"apiVersion": "2021-11-01",
"location": "[parameters('location')]",
"properties": {
"createMode": "PointInTimeRestore",
"restorePointInTime": "2022-12-14T12:00:00Z",
"sourceDatabaseId": "[resourceId(variables('sourceResourceGroupName'), 'Microsoft.Sql/managedInstances/databases', variables('sourceSqlManagedInstanceName'), 'AdventureWorks')]"
}
}
The documentation is broken in a few ways:
When attempting to perform a PointInTimeRestore the properties referenced (SourceDatabaseName, SourceManagedInstanceName, PointInTime) do not exist. Rather the following properties are used: restorePointInTime and sourceDatabaseId which are documented in the documentation.
Additionally, the restorePointInTime indicates that the time should be in ISO8601 format, however this is not the same as what is returned by utcNow(). Testing has shown that you must provide it in this version of the ISO8601 format: yyyy-MM-ddTHH:mm:ssZ which can be done using utcNow('yyyy-MM-ddTHH:mm:ssZ').
I have created an issue to try and get the documentation fixed up here: https://github.com/MicrosoftDocs/azure-docs/issues/102717

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