Display JSON Properties in Kusto Query - specific Subnets - json

Hello i have a little problem i couldn't find an answer i could understand anywhere.
i am about to make a dashboard in azure by using Azure Resource Graph Explorer and their Kusto query language, i would like to display how many Subnets there is i the subscription.
but when i try to seperate and show the subnets in it only show me either the amount of Vnets there is or it show one line of subnets and not how many.
i've tried to project the subnets with following code but dont know what to write to show a single line with two rows with the name "subnets" and how many of them.
Summary: is that; i want to write a Kusto query that i can pin to dashboard that shows how many subnets there is.
Code i tried to write:
resources
| project properties.subnets
output is:
properties_subnets
null
[{"type":"Microsoft.Network/virtualNetworks/subnets","properties":{"provisioningState":"Succeeded", etc. etc. etc.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"virtualNetworks_Vnet_name": {
"defaultValue": "Vnet",
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2020-05-01",
"name": "[parameters('virtualNetworks_Vnet_name')]",
"location": "westeurope",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/16"
]
},
"subnets": [
{
"name": "default",
"properties": {
"addressPrefix": "10.0.0.0/24",
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"name": "default2",
"properties": {
"addressPrefix": "10.0.1.0/24",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
],
"virtualNetworkPeerings": [],
"enableDdosProtection": false,
"enableVmProtection": false
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2020-05-01",
"name": "[concat(parameters('virtualNetworks_Vnet_name'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_Vnet_name'))]"
],
"properties": {
"addressPrefix": "10.0.0.0/24",
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
},
{
"type": "Microsoft.Network/virtualNetworks/subnets",
"apiVersion": "2020-05-01",
"name": "[concat(parameters('virtualNetworks_Vnet_name'), '/default2')]",
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworks_Vnet_name'))]"
],
"properties": {
"addressPrefix": "10.0.1.0/24",
"serviceEndpoints": [],
"delegations": [],
"privateEndpointNetworkPolicies": "Enabled",
"privateLinkServiceNetworkPolicies": "Enabled"
}
}
]
}

resources
| where type=~"Microsoft.Network/virtualNetworks"
| project array_length(properties.subnets)

Related

Facing Issues on Deploying template for Azure File Share Storage Account

I am trying to create a storage account with file share and the rest of the services included. when I execute the template it throws the below error.
Status Message: XML specified is not syntactically valid.
RequestId:5be13537-701a-0056-1f1d-0a506f000000
Time:2021-02-23T19:53:49.1937194Z (Code:InvalidXmlDocument)
CorrelationId: 21fe81f4-b917-4813-ade5-9b96f3b688d6
The storage account's blob, queue, table get provisioned don't know why it throws an error on file share provisioning. Any help guys.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountname": {
"type": "String"
},
"location": {
"type": "String"
},
"skuname": {
"type": "String"
},
"tags": {
"type": "Object"
},
"accessTier": {
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2020-08-01-preview",
"name": "[parameters('storageAccountname')]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
"sku": {
"name": "[parameters('skuname')]",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"allowBlobPublicAccess": true,
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "[parameters('accessTier')]"
}
},
{
"type": "Microsoft.Storage/storageAccounts/fileServices",
"apiVersion": "2020-08-01-preview",
"name": "[concat(parameters('storageAccountname'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountname'))]"
],
"sku": {
"name": "[parameters('skuname')]",
"tier": "Standard"
},
"properties": {
"protocolSettings": {
"smb": {}
},
"cors": {
"corsRules": []
},
"shareDeleteRetentionPolicy": {
"enabled": true,
"days": 7
}
}
}
]
}
From this azure quickstart template, we don't need to provide the resource of the type Microsoft.Storage/storageAccounts/fileServices when you create a standard storage account.
When we only include resource Microsoft.Storage/storageAccounts with kind StorageV2, it will provision all these services: blobServices,fileServices,queueServices,tableServices at the same time.
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"properties": {
"accessTier": "Hot"
}
},
If you only would like to create fileservices, you could select a FileStorage kind of storage account type with Premium performance tiers. The working sample like this:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountname": {
"type": "String"
},
"location": {
"type": "String"
},
"skuname": {
"type": "String"
},
"tags": {
"type": "Object"
}
// "accessTier": {
// "type": "String"
// }
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2020-08-01-preview",
"name": "[parameters('storageAccountname')]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
"sku": {
"name": "[parameters('skuname')]",
"tier": "Premium"
},
"kind": "FileStorage",
"properties": {
"allowBlobPublicAccess": true,
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"keyType": "Account",
"enabled": true
},
"blob": {
"keyType": "Account",
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
// "accessTier": "[parameters('accessTier')]"
}
}
]
}
For more information, read https://learn.microsoft.com/en-us/azure/storage/common/storage-account-overview#performance-tiers
I was running into the same error and chased it down to the "protocolSettings" with the empty "SMB" entry. Removing that block from my template eliminated the error and the resource was created with the default values.
It may not be necessary to include the fileservices resource type, but I make it a standard practice to include all four (blob, file, queue, and table) in case I later want to add containers/shares/etc. in the template so the references to their parents will work (and to maintain my sanity in remembering the structure when I look at the template months later.)
Yes, when the storage account kind is changed to StorageV2, is allowing me to add fileshare.

I am facing an issue while deploy storage account using arm templates

I am facing an issue while deploy storage account using arm templates:
Deployment template validation failed: 'The template resource 'sneha1'
for type
'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Common.Entities.TemplateGenericProperty`1[System.String]'
at line '20' and column '59' has incorrect segment lengths. A nested
resource type must have identical number of segments as its resource
name. A root resource type must have segment length one greater than
its resource name.
This is my template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccounts_sneha_name": {
"defaultValue": "sneha,
"type": "String"
}
},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/sneha",
"apiVersion": "2019-04-01",
"name": "[concat(parameters('storageAccounts_sneha_name'), copyIndex(1) ) ]",
"location": "centralus",
"copy":{
"Name":"rama",
"count": 5
},
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"enabled": true
},
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
},
"accessTier": "Hot"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices",
"apiVersion": "2019-04-01",
"name": "[concat(parameters('storageAccounts_sneha_name'), '/default')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccounts_sneha_name'))]"
],
"properties": {
"cors": {
"corsRules": []
},
"deleteRetentionPolicy": {
"enabled": false
}
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2019-04-01",
"name": "[concat(parameters('storageAccounts_sneha_name'), '/default/container1')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccounts_sneha_name'), 'default')]",
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccounts_sneha_name'))]"
],
"properties": {
"publicAccess": "Blob"
}
}
]
}
what it says is that the resource sneha1 name is malformed, if you can share the resource name I can help you fix it, but in a nutshell name should be 1 segment shorter than type:
name: "xxx",
type: "microsoft.storage/storageAccounts"
or like so:
name: "xxx/diag",
type: "microsoft.storage/storageAccounts/diagnosticSettings"
This issue occurs in case your name property is greater in length than TYPE
"apiVersion": "2016-12-01",
"name": "[concat(parameters('vaultName'), '/', parameters('policyName'))]",
"type": "Microsoft.RecoveryServices/vaults/backupPolicies"
In above example name has two level and type has 3 level .
Example as shown below:
"name":"azVault/policy1"
"type":"Microsoft.RecoveryServices/vaults/backupPolicies"
This might be your problem, remove the sneha in type and try.
"type": "Microsoft.Storage/storageAccounts/sneha",
"name": "[concat(parameters('storageAccounts_sneha_name'), '/copyIndex(1)' ) ]",

Provisioning resource group and azure function at deployment level

I've written up the script below to do the following:
Provision a resource group
In a separate deployment:
Provision a storage account
Provision a server farm
Provision a function app
The problem lies in the setting of the app settings in the function app, when I'm setting up the AzureWebJobsStorage. The resourceId function fails to resolve the storage account. When looking at the documention for the resourceId function, it states:
When used with a subscription-level deployment, the resourceId() function can only retrieve the ID of resources deployed at that level. [docs]
But now I don't know how to resolve this!
Template:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string"
},
"functionName": {
"type": "string"
},
"storageAccName": {
"type": "string"
},
"namingPrefix": {
"type": "string"
}
},
"variables": {
"resourceGroupLocation": "North Europe",
"planName": "[replace(concat(variables('resourceGroupLocation'), 'Plan'),' ','')]",
"resourceGroupName": "[concat(parameters('namingPrefix'), '-', parameters('resourceGroupName'))]",
"functionName": "[concat(parameters('namingPrefix'), '-', parameters('functionName'))]",
"storageAccName": "[toLower(concat(parameters('namingPrefix'), parameters('storageAccName')))]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2018-05-01",
"location": "[variables('resourceGroupLocation')]",
"name": "[variables('resourceGroupName')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-05-01",
"name": "NestedTemplate",
"resourceGroup": "[variables('resourceGroupName')]",
"dependsOn": [
"[variables('resourceGroupName')]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageAccName')]",
"location": "[variables('resourceGroupLocation')]",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"kind": "Storage",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"enabled": true
},
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2016-09-01",
"name": "[variables('planName')]",
"location": "[variables('resourceGroupLocation')]",
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
},
"kind": "functionapp",
"properties": {
"name": "[variables('planName')]",
"computeMode": "Dynamic",
"perSiteScaling": false,
"reserved": false,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2016-08-01",
"name": "[variables('functionName')]",
"location": "[variables('resourceGroupLocation')]",
"dependsOn": [
"[variables('planName')]",
"[variables('appInsightsName')]",
"[variables('storageAccName')]"
],
"kind": "functionapp",
"identity": {
"type": "SystemAssigned"
},
"properties": {
"enabled": true,
"hostNameSslStates": [
{
"name": "[concat(variables('functionName'), '.azurewebsites.net')]",
"sslState": "Disabled",
"hostType": "Standard"
},
{
"name": "[concat(variables('functionName'), '.scm.azurewebsites.net')]",
"sslState": "Disabled",
"hostType": "Repository"
}
],
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).key1)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).key1)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[variables('functionName')]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "node"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.14.1"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
}
]
},
"serverFarmId": "[variables('planName')]",
"reserved": false
}
}
]
}
}
}
]
}
Executed using following line:
New-AzDeployment -Location "North Europe" -TemplateFile $TemplateFilePath -TemplateParameterFile $ParametersFilePath -namingPrefix $namingPrefix;
Output
Resource Microsoft.Storage/storageAccounts 'testStorageAccount' failed with message '{
"error": {
"code": "ResourceNotFound",
"message": "The Resource 'Microsoft.Storage/storageAccounts/testStorageAccount' under resource group '<null>'
was not found."
}
}'
You've run into a few "limitations" in the template language that make this hard at the moment (we're working on improving both).
1) Inline nested deployments have the scope of the top-level deployment when evaluating template language expressions (anything in []) which is sometimes convenient (you can share variables for example) but more often than not causes some problem (like the resourceId function). ARM has always behaved this way but with the advent of subscription level deployments it's a bit more problematic (you run into it more). To get around this you can use linked templates - I know that's not always ideal but they will behave as expected.
2) the second thing you're running into is that list*() functions are evaluated immediately if ARM thinks the resource you're accessing is not within the same deployment. Due to #1, that's what ARM thinks in this case and why trying to concat() the resourceID still doesn't work.
Aside from that, stay away from the providers() function for apiVersions, it's not deterministic and the results of that function can change without you knowing it. The code you had in your original post for listKeys did work a while back and you might see it in samples floating around, but changes in the platform can break that function's behavior. Literal apiVersions are always better in ARM templates.
The docs are confusing and don't describe how resourceId() works at that level. It should really say:
When used in a subscription level deployment resourceId() can only get the resource IDs of resource groups (Microsoft.Resources/resourceGroups), policies (Microsoft.Authorization/policyAssignments), and role definitions (Microsoft.Authorization/roleDefinitions), as these are subscription level specific resources.
Since that's how it actually works. More docs here.
In terms of how to proceed from here, you'll simply need to deploy the resource groups in one template at the subscription level, and the resources in another template at the resource group level.
I hit this issue and found that they more recently support nested templates better by defining inner and outer scopes on the deployment resources and function scopes.
https://learn.microsoft.com/bs-latn-ba/azure/azure-resource-manager/templates/cross-scope-deployment?tabs=azure-cli#how-functions-resolve-in-scopes
https://www.youtube.com/watch?v=96XxVyxrhZI
For anyone who runs into this issue at a later date (probably myself) I was forced to create the resource group in my powershell script, and then use new-AzResourceGroupDeployment instead.
To accommodate this, the changes to the deployment template were minimal (I remove the resource group and brought the nested template one level up). However, I was also accessing the storage account key incorrectly. This has been updated in the code below.
$resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue
if(!$resourceGroup)
{
Write-Host "Creating resource group '$resourceGroupName' in location '$resourceGroupLocation'";
New-AzResourceGroup -Name $resourceGroupName -Location $resourceGroupLocation
}
else{
Write-Host "Using existing resource group '$resourceGroupName'";
}
# Start the deployment
Write-Host "Starting deployment...";
if(Test-Path $parametersFilePath) {
New-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -TemplateFile $TemplateFilePath -TemplateParameterFile $parametersFilePath;
}
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string"
},
"functionName": {
"type": "string"
},
"storageAccName": {
"type": "string"
},
"namingPrefix": {
"type": "string"
}
},
"variables": {
"resourceGroupLocation": "North Europe",
"planName": "[replace(concat(variables('resourceGroupLocation'), 'Plan'),' ','')]",
"resourceGroupName": "[concat(parameters('namingPrefix'), '-', parameters('resourceGroupName'))]",
"functionName": "[concat(parameters('namingPrefix'), '-', parameters('functionName'))]",
"storageAccName": "[toLower(concat(parameters('namingPrefix'), parameters('storageAccName')))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-04-01",
"name": "[variables('storageAccName')]",
"location": "[variables('resourceGroupLocation')]",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"kind": "Storage",
"properties": {
"networkAcls": {
"bypass": "AzureServices",
"virtualNetworkRules": [],
"ipRules": [],
"defaultAction": "Allow"
},
"supportsHttpsTrafficOnly": true,
"encryption": {
"services": {
"file": {
"enabled": true
},
"blob": {
"enabled": true
}
},
"keySource": "Microsoft.Storage"
}
}
},
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2016-09-01",
"name": "[variables('planName')]",
"location": "[variables('resourceGroupLocation')]",
"sku": {
"name": "Y1",
"tier": "Dynamic",
"size": "Y1",
"family": "Y",
"capacity": 0
},
"kind": "functionapp",
"properties": {
"name": "[variables('planName')]",
"computeMode": "Dynamic",
"perSiteScaling": false,
"reserved": false,
"targetWorkerCount": 0,
"targetWorkerSizeId": 0
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2016-08-01",
"name": "[variables('functionName')]",
"location": "[variables('resourceGroupLocation')]",
"dependsOn": [
"[variables('planName')]",
"[variables('appInsightsName')]",
"[variables('storageAccName')]"
],
"kind": "functionapp",
"identity": {
"type": "SystemAssigned"
},
"properties": {
"enabled": true,
"hostNameSslStates": [
{
"name": "[concat(variables('functionName'), '.azurewebsites.net')]",
"sslState": "Disabled",
"hostType": "Standard"
},
{
"name": "[concat(variables('functionName'), '.scm.azurewebsites.net')]",
"sslState": "Disabled",
"hostType": "Repository"
}
],
"siteConfig": {
"appSettings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value)]"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[variables('functionName')]"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "node"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.14.1"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~2"
}
]
},
"serverFarmId": "[variables('planName')]",
"reserved": false
}
}
]
}
Edited -
Sorry I jumped the gun earlier, the issue is with New-AzDeployment which is specifically for deploying subscription level resources.
https://learn.microsoft.com/en-us/powershell/module/az.resources/new-azdeployment?view=azps-2.7.0
Excerpts from the link above -
The New-AzDeployment cmdlet adds a deployment at the current
subscription scope. This includes the resources that the deployment
requires.
An Azure resource is a user-managed Azure entity. A resource can live
in a resource group, like database server, database, website, virtual
machine, or Storage account. Or, it can be a subscription level
resource, like role definition, policy definition, etc.
To add resources to a resource group, use the
New-AzResourceGroupDeployment which creates a deployment at a resource
group. The New-AzDeployment cmdlet creates a deployment at the current
subscription scope, which deploys subscription level resources.

how to use nested copy in Arm Template

I am creating multiple resource policies (backup policies in Recovery Service Vault) for multiple environments. I was able to create them for one environment, how do i replicate them using nested copy for QA.
They will have policy name as AZR-QA-SQL-1Hour-Policy-001
Any help is appreciated.
"variables": {
"sqlDevPolicyName": [
"[concat('AZR-DEV-SQL-1HOUR-POLICY-001')]",
"[concat('AZR-DEV-SQL-4HOUR-POLICY-001')]",
"[concat('AZR-DEV-SQL-8HOUR-POLICY-001')]"
]
}
}
"resources": [
{
"type": "Microsoft.RecoveryServices/vaults",
"apiVersion": "2018-01-10",
"name": "[parameters('vaultName')]",
"location": "[parameters('location')]",
"sku": {
"name": "RS0",
"tier": "Standard"
},
"properties": {}
},
{
"apiVersion": "2018-01-10",
"name": "[concat(parameters('vaultName'), '/', variables('sqlPolicyName')[copyIndex()])]",
"type": "Microsoft.RecoveryServices/vaults/backupPolicies",
"dependsOn": [
"[concat('Microsoft.RecoveryServices/vaults/', parameters('vaultName'))]"
],
"copy": {
"name": "policies",
"count": "[length(variables('sqlDevPolicyName'))]"
},
"location": "[parameters('location')]",
"properties": {
"backupManagementType": "AzureWorkload",
"protectedItemsCount": 0,
"settings": {
"isCompression": false,
"issqlcompression": false,
"timeZone": "[parameters('timeZone')]"
},
"subProtectionPolicy": [
{
"policyType": "Full",
"retentionPolicy": {
"retentionPolicyType": "LongTermRetentionPolicy",
"weeklySchedule": {
"daysOfTheWeek": [
"Sunday"
],
"retentionDuration": {
"count": 15,
"durationType": "Weeks"
},
"retentionTimes": "[parameters('scheduleRunTimes')]"
}
},
According to my understanding of what you are saying you need to do this:
"variables": {
"sqlQAPolicyName": [ // dont need concat() here
"AZR-QA-SQL-1HOUR-POLICY-001",
"AZR-QA-SQL-4HOUR-POLICY-001",
"AZR-QA-SQL-8HOUR-POLICY-001"
]
},
"resources": [
{
same thing here, just need to create it two times, as you now have 2 sets of resources.
and you need to use your QA variable to create backup policies for QA
...
}
]
at least this is how i see it

How to create multiple Service Bus topics using ARM template?

I've looked at the https://github.com/Azure/azure-quickstart-templates/tree/master/101-servicebus-topic example. Now I'm trying to figure out how to create several topics when running the deploy script using nested resource looping (https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-multiple/#looping-on-a-nested-resource).
It would be nice to just define an array in the value for serviceBusTopicName and then somehow creating topics by looping through it, instead of doing it manually.
I am getting the following error when trying to deploy.
Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The template resource '[parameters('serviceBusTopicName')[copyIndex()]]' at line '72' column '14' is not valid.
Copying nested resources is not supported. Please see https://aka.ms/arm-copy/#looping-on-a-nested-resource for usage details.'.
This is the template.json that I have tried to get to work.
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "array",
"metadata": {
"description": "Name of the Topic"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/Namespaces",
"location": "[variables('location')]",
"kind": "Messaging",
"sku": {
"name": "StandardSku",
"tier": "Standard"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusTopicName')]",
"type": "Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"properties": {
"path": "[parameters('serviceBusTopicName')]"
},
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicName'))]"
}
}
]
},
parameters.json
{"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"value": "ServiceBus"
},
"serviceBusTopicName": {
"value": [
"Person",
"Stadium",
"Team"
]
},
"serviceBusApiVersion": {
"value": "2015-08-01"
}
}
As #Lain said, copying nested resource isn't supported as of now Check Here. For deploying multiple resource you have to move it to root. I just finished my POC for deploying multiple topics (Just topics assuming Namespace already exists) here is the code. I am passing topic names as a comma separated string:
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"serviceBusTopicName": {
"type": "string",
"metadata": {
"description": "Comma seperated Topic Names"
}
},
"serviceBusApiVersion": {
"type": "string",
"defaultValue": "2015-08-01",
"metadata": {
"description": "Service Bus ApiVersion used by the template"
}
}
},
"variables": {
"location": "[resourceGroup().location]",
"sbVersion": "[parameters('serviceBusApiVersion')]",
"TopicNames": "[split(parameters('serviceBusTopicName'), ',')]"
},
"resources": [{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', variables('TopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/Namespaces/Topics",
"copy": {
"name": "TopicNameCopy",
"count": "[length(variables('TopicNames'))]"
},
"properties": {
"path": "[variables('TopicNames')[copyIndex()]]"
}
}]
}
The link in the error message goes through this pretty well, but you can't have loops in nested resources, you need to push the resource up to the top level, and then link the resources together using names. This template will do what you seem to be trying to do:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"serviceBusNamespaceName": {
"type": "string",
"metadata": {
"description": "Name of the Service Bus Namespace"
}
},
"serviceBusTopicNames": {
"type": "array",
"metadata": {
"description": "Name of the Service Bus Topic"
}
}
},
"variables": {
"sbVersion": "2015-08-01"
},
"resources": [
{
"apiVersion": "[variables('sbVersion')]",
"name": "[parameters('serviceBusNamespaceName')]",
"type": "Microsoft.ServiceBus/namespaces",
"location": "[resourceGroup().location]",
"properties": {
}
},
{
"apiVersion": "[variables('sbVersion')]",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('serviceBusTopicNames')[copyIndex()])]",
"type": "Microsoft.ServiceBus/namespaces/Topics",
"dependsOn": [
"[concat('Microsoft.ServiceBus/namespaces/', parameters('serviceBusNamespaceName'))]"
],
"copy": {
"name": "datasetcopy",
"count": "[length(parameters('serviceBusTopicNames'))]"
},
"properties": {
"path": "[parameters('serviceBusTopicNames')[copyIndex()]]"
},
"resources": [
]
}
]
}