arm template virtualNetworkName creation appendix issue - azure

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.

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.

ARM TTK throwing error for the test : Location Should Not Be Hardcoded in Azure DevOps Pipeline

I am running ARM TTK as one of the tasks in my Azure DevOps Pipeline for validating ARM Templates before a PR is merged.
One of the tests that i am using is : Location Should Not Be Hardcoded which is mentioned in the link : ARM-TTK-Tests by the name of "Location Uses Parameter"
My ARM Template name is not "azuredeploy.json" or "mainTemplate.json" . My ARM Template name is "winvm-arm.json".
I have the location defined in parameters like shown below :
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
I also have the reference to this parameter in my resource block like shown below :
resources": [
{
"apiVersion": "2020-05-01",
"type": "Microsoft.Network/networkInterfaces",
"name": "[concat(variables('vmName'), '-nic')]",
"location": "[parameters('location')]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig1",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
}
So everything is correct as per what it should be then why is the test still flagging the error below :
Location Should Not Be Hardcoded (12 ms)
The location parameter of nested templates must not have a defaultValue property. It is "[resourceGroup().location]"
Is it because my main template name is not "azuredeploy.json" or "mainTemplate.json" and it is being treated as a nested template , is that the reason
Thank you Brian Moore. Posting your suggestion as an answer to help other community members.
this is because anything that is not the "main template" is assumed to be a nested template... main template is determined by the filename (unless you specify otherwise).
I think short term you'll have to follow the pattern - until we have a more standard way of determining name / nested.
You can refer to "Location Should Not Be Hardcoded" test catching errors in good templates

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 ARM template depend on resources in copy loop

I am creating ARM template which takes in hash table of subnets and creates those. However, it looks that I need to wait for the first subnet to be ready before creating the second etc. But I do not know how I could depend on the previous subnet in copy loop. My template resource looks like this currently:
{
"apiVersion": "2018-06-01",
"type": "Microsoft.Network/virtualNetworks/subnets",
"name": "[concat(parameters('vnetName') , '/' , parameters('subnets').settings[copyIndex()].name)]",
"location": "[variables('location')]",
"copy": {
"name": "subnetLoop",
"count": "[variables('subnetcount')]"
},
"dependsOn": ["[parameters('vnetName')]",
"[resourceId(variables('rgname'), 'Microsoft.Network/virtualNetworks/subnets', parameters('vNetName'), parameters('subnets').settings[copyIndex()].name)]"
],
"properties": {
"addressPrefix": "[parameters('subnets').settings[copyIndex()].addressPrefix]",
}
Which does not work because the first subnet cannot reference itself.
you can use "mode": "serial" to workaround that.
"copy": {
"name": "subnetLoop",
"count": "[variables('subnetcount')]",
"mode": "serial"
},
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#resource-iteration
but you really need to look at properties loop, check this link:
https://learn.microsoft.com/en-us/azure/architecture/building-blocks/extending-templates/objects-as-parameters#using-a-property-object-in-a-copy-loop

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.

Resources