Create Resource Group and Deploy Resource - azure

According to the Microsoft Documentation, it is now possible to create Resource Groups and deploy resources to the newly created resource group. There is a small catch though, at the very beginning, we have this disclaimer -
Subscription level deployment is different from resource group deployment in the following aspects:
Schema and commands
The schema and commands you use for subscription-level deployments are different than resource group deployments.
For the schema, use https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#
This throws a major issue, the azuredeploy.json is no longer recognized as the deployment template as it is not using the resource deployment schema (https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#).
So, the other option was to create the Resource Group as a Nested Template and put a dependsOn for the child resources that will be created, this now allowed me to Deploy/Validate the file. However, this possesses a new issue. Even though a dependsOn dictates that the resource group is created, it still fails to recognize this and comes back with an error - resource group could not be found, hence the resources could not be deployed. I tried using a Linked Template (I know this does not make any difference, but still)
Anyone, managed to do this by any chance?
Created Resource Groups and deployed resources at the same time.
Overcome the hurdle of trying to use DependsOn and still not get the right deployment or validation?
Adding my code.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string",
"defaultValue": "North Europe"
},
"FirstResourceGroupName": {
"type": "string",
"defaultValue": "myFirstRG"
},
"FirstBlobStorageName": {
"type": "string",
"defaultValue": "North Europe"
}
},
"variables": {
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"name": "ResourceGroupDeployment",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2018-05-01",
"location": "[parameters('location')]",
"name": "[parameters('FirstResourceGroupName')]",
"properties": {}
}
],
"outputs" : {}
}
}
},
{
//ResourceDeployment
"type": "Microsoft.Resources/deployments",
"name": "StorageDeployment",
"apiVersion": "2017-05-10",
"dependsOn": [
"[concat('Microsoft.Resources/deployments/', 'ResourceGroupDeployment')]"
//"ResourceGroupDeployment"
],
"resourceGroup": "[parameters('FirstResourceGroupName')]",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2017-10-01",
"name": "[parameters('FirstBlobStorageName')]",
"location": "[parameters('location')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS"
}
}
],
"outputs": {}
}
}
}
],
"outputs": {}
}

The solution proposed by MS is good, when you are not using Visual Studio or Portal for deployment. My major issue was the validation of the template which again will not work for subscription level deployment, as it uses a schema that is not recogonised as an ARM.
It may work as #4c74356b41 suggested through any other means i.e. cli\sdks\rest api, but I did not go down that path.
The other solution I had was to run a powershell script by adding a step on the Azure DevOps pipeline. Which was the closest I came to making this work, but again the validation to check if my deployment would succeed, still was up in the air. I did not want my release pipeline to fail because of an invalid template.
Here is what I have gathered, the reason why the validation failed (even with deploying the RG and using a dependsOn) was because the Resource Groups will not be created until you deploy the template. The template deployment will not happen unless it passes validation as the Resource Groups does not exist. So we are stuck in a loop. The two options are either create them manually on the portal before validating (this defies the point of automation) or use a simple powershell step before validating them. The latter is what I have gone with. I know this is unorthodoxed, but works.... and also validates my template.
NOTE - The solution is different from the original problem, as I have used multiple resource group creation. According to MS documentation, you can have up to 5 RG deployed this way.
First, create a resource group file that will hold the resource groups you'd want to create. It will be just a simple JSON file like,
{
"rgNames":
{
"rg1": { "rg": "resource-group-main" },
"rg2": { "rg": "resource-group-backup" }
}
}
Use the same values you have added to this file as a parameter, so you can use them to deploy resources to.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"ResourceGroups": {
"type": "object",
//If you are changing this value !!!! Please make sure you are also updating the same in the ResourceGroups.ARM.json !!!!
"allowedValues": [
{
"rgNames":
{
"rg1": { "rg": "resource-group-main" },
"rg2": { "rg": "resource-group-backup" }
}
}
]
}
}
Second, change the PS script to include the code where it will loop through the list of resource groups it need to deploy.
# Set '$RGTemplateFile' parameter to be the name of the file you added to your project
$rgFile = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PSScriptRoot, $RGTemplateFile))
$rgString = Get-Content -Raw -Path $rgFile | ConvertFrom-Json
# helper to turn PSCustomObject into a list of key/value pairs
function Get-ObjectMembers {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True, ValueFromPipeline=$True)]
[PSCustomObject]$obj
)
$obj | Get-Member -MemberType NoteProperty | ForEach-Object {
$key = $_.Name
[PSCustomObject]#{Key = $key; Value = $obj."$key"}
}
}
$rgValues = $jsonParam | Get-ObjectMembers | foreach {
$_.Value | Get-ObjectMembers | foreach {
[PSCustomObject]#{
RGName = $_.value.rgNames | select -First 1
}
}
}
foreach ($values in $rgValues)
{
New-AzureRmResourceGroup -Name $values.RGName -Location $ResourceGroupLocation -Verbose -Force
}
add the above code, just before where it performs a validation -
if ($ValidateOnly) {
$ErrorMessages = Format-ValidationOutput (Test-AzureRmResourceGroupDeployment -ResourceGroupName $ResourceGroupName `
-TemplateFile $TemplateFile `
-TemplateParameterFile $TemplateParametersFile `
#OptionalParameters)
:
Finally, change the deployment template file (azuredeploy.json) to do either a nested template deployment or a linked template to deploy resources on the RG you have declared.(I have used Linked, as it looks neater)
"variables": {
"rg1Name": "[parameters('ResourceGroups')['rgNames']['rg1'].rg]",
"rg2Name": "[parameters('ResourceGroups')['rgNames']['rg2'].rg]",
"blob1Name": "[parameters('blob1')]",
"blob2Name": "[parameters('blob2')]",
"arm1": "[concat(parameters('_artifactsLocation'), 'rg1/rg1.ARM.json', parameters('_artifactsLocationSasToken'))]",
"arm2": "[concat(parameters('_artifactsLocation'), 'rg2/rg2.ARM.json', parameters('_artifactsLocationSasToken'))]"
},
"resources": [
{
//RG1 Resources Deployment
"type": "Microsoft.Resources/deployments",
"name": "RG1Resources",
"apiVersion": "2017-05-10",
"resourceGroup": "[variables('rg1Name')]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('arm1')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"blob1Name": {
"value": "[variables('blob1Name')]"
}
}
}
},
{
//RG2 Resources Deployment
"type": "Microsoft.Resources/deployments",
"name": "RG2Resources",
"apiVersion": "2017-05-10",
"resourceGroup": "[variables('rg2Name')]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('arm2')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"blobName": {
"value": "[variables('blob2Name')]"
}
}
}
}
],
"outputs": {}
}
Your rg1.ARM.json and rg2.ARM.json files looks like, obviously one could have more than one resource.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"blobName": {
"type": "string"
}
},
"variables": {
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[parameters('blobName')]",
"kind": "StorageV2",
"apiVersion": "2018-07-01",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard_LRS"
},
"properties": {}
}
],
"outputs": {
}
}
Once this is set up, you will be able to validate the file as the PS script will create the RG's for you before it passes through validation.

Example taken from official documentation:
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.1",
"parameters": {
"rgName": {
"type": "string"
},
"rgLocation": {
"type": "string"
},
"storagePrefix": {
"type": "string",
"maxLength": 11
}
},
"variables": {
"storageName": "[concat(parameters('storagePrefix'), uniqueString(subscription().id, parameters('rgName')))]"
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2018-05-01",
"location": "[parameters('rgLocation')]",
"name": "[parameters('rgName')]",
"properties": {}
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2018-05-01",
"name": "storageDeployment",
"resourceGroup": "[parameters('rgName')]",
"dependsOn": [
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('rgName'))]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2017-10-01",
"name": "[variables('storageName')]",
"location": "[parameters('rgLocation')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS"
}
}
]
}
}
}
]
}
does exactly what you need.
https://learn.microsoft.com/en-us/azure/azure-resource-manager/deploy-to-subscription#create-resource-group-and-deploy-resources

Related

ARM, how to make parameter file dependent on other parameter

I'm trying to make my parameter file a bit smarter but for the life of me can't figure out how to do so. I have a parameters.json file with 2 params: env & commonTags. env takes a string from my DevOps pipeline, and I need this parameter to fill a value in the commonTags-object parameter. See code snippet below:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"env": {
},
"location": {
"value": "westeurope"
},
"commonTags": {
"value": {
"contact":"dr#balloon.com",
"costcenter": "99999",
"env": "[parameters('env')]",
"criticality": "[parameters('env') == 'prd' ? 'high', 'low']"
}
}
}
}
The only other option I see is to set the env-specific parameters in the template file. Either by merging with the existing parameters or by setting the value of commonTags in the template file entirely. But I'd rather keep my template files free of parameter values and have these all located in a central parameter file.
Can anybody point me in the right direction? I can't seem to find anything online.
Many thanks!
You cannot nest parameters as you are trying to do here.
Instead, you can make use of conditional directly in your main arm template coupled with inline parameters. Let's take a simple Storage Account as an example.
azuredeploy.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"type": "string"
},
"env": {
"type": "string"
},
"costcenter": {
"type": "string"
},
"contact": {
"type": "string"
},
"storageAccountType": {
"type": "string",
"allowedValues": [
"Standard_LRS",
"Standard_GRS",
"Standard_ZRS",
"Premium_LRS"
],
"metadata": {
"description": "Storage Account type"
}
}
},
"variables": {
"storageAccountName": "[concat('store', uniquestring(resourceGroup().id))]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
},
"tags": {
"contact": "[parameters('contact')]",
"costcenter": "[parameters('costcenter')]",
"env": "[parameters('env')]",
"criticality": "[if(equals(parameters('env'),'prd'),'high','low')]"
},
"kind": "StorageV2"
}
]
}
azuredeploy.parameters.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountType": {
"value": "Standard_LRS"
},
"costcenter": {
"value": "99999"
},
"contact": {
"value": "dr#balloon.com"
},
"location": {
"value": "westeurope"
}
}
}
Note that the tag names remain static - there are ways to pass them inline instead/as well - see this answer.
From these, you can then use Az CLI or Powershell to deploy your template, the parameters kept in a single place and the dynamically provide the remaining one like env.
Example using Powershell :
New-AzResourceGroupDeployment -Name $deploymentName -ResourceGroupName $rgName -TemplateFile .\azuredeploy.json -TemplateParameterFile .\azuredeploy.parameters.json -env $env

ARM template throws incorrect segments lengths for array of storage containers types

I am getting Template validation failed: The template resource 'reports' for type 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Common.Entities.TemplateGenericProperty`1[System.String]' at line '34' and column '79' 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. Please see https://aka.ms/arm-template/#resources for usage details. when I make ARM to create containers from the array in parameters file.
Issue line: "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
Here is my ARM template file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "The name of the storage account"
}
},
"storageContaners": {
"type": "string",
"metadata": {
"description": "The name of the blob containers"
}
}
},
"functions": [],
"variables": {
},
"resources": [
{
"name": "[parameters('storageAccountName')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"location": "[resourceGroup().location]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
}
},
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2021-04-01",
"name": "[parameters('storageContaners')]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
],
"properties": {
"publicAccess": "Blob"
}
}
],
"outputs": {}
}
Here is my ARM parameters file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"value": "mystorageaccount"
},
"storageContaners": {
"value": "reports"
}
}
}
I have tried changing name to different types but no luck.
Can anybody please help me to figure it out the cause?
The name parameters under the nested resources must be one level less than the type.
Here type has 4 level(separated by 3 / ). So name must have 3 level (separated by 2 /).
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('storageContaners')]",
This applies when having nested resources under parent resource.

How to use copyIndex in a nested template resource?

Big picture: I want to use the ARM template to create multiple topics on a service bus.
Known fact: The app service that deploys the template is in a different resource group than the service bus.
Pain point: I'm using a nested template because I'm trying to create resources (topics) that are external to the targeted resource group. Within this nested template, I'm not sure how to get copy to work correctly.
From this MS doc , I believe my syntax is correct.
This is how my parameters are listed:
"sharedResourcesResourceGroupName": {
"type": "string",
"defaultValue": "sharedResourceGroupName",
"metadata": {
"description": "Resource Group in which platform shared resources live"
}
},
"serviceBusNamespaceName": {
"type": "string",
"defaultValue": "serviceBusName",
"metadata": {
"description": "Name of the Service Bus namespace"
}
},
"topics": {
"type": "array",
"metadata": {
"description": "List of topics"
},
"defaultValue": [
"topic1",
"topic2"
]
}
This is my resource object for creating the topics with the copyIndex() method:
{
"apiVersion": "2018-05-01",
"type": "Microsoft.Resources/deployments",
"name": "[concat(parameters('serviceBusNamespaceName'))]",
"resourceGroup": "[parameters('sharedResourcesResourceGroupName')]",
"properties": {
"mode": "Incremental",
"template":{
"$schema": "2018-05-01",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.ServiceBus/namespaces/topics",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('topics')[copyIndex()])]",
"apiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"properties": {},
"copy": {
"name": "topics",
"count": "[length(parameters('topics'))]"
},
"dependsOn": [
"[parameters('serviceBusNamespaceName')]"
]
}
]
}
}
}
I am testing the arm template deployment using the Azure Powershell with these commands:
Connect-AzAccount
Set-AZContext -SubscriptionName subscriptionWhereTheAppServiceLives
New-AzResourceGroupDeployment -ResourceGroupName resourceGroupWhereAppServiceLives -TemplateFile <path to template file>\azuredeploy.json -TemplateParameterFile <path to parameters file>\azuredeploy.parameters.json
The error I'm getting from the Azure powershell console is:
The template function 'copyIndex' is not expected at this location. The function can only be used in a resource with copy specified.
If I remove the "copy" object and replace "name" with something like "[concat(parameters('serviceBusNamespaceName'), '/topicName')]", then the template is able to create ONE topic in the right service bus. But I'm looking to create multiple topics.
Any insight would be greatly appreciated!
I think you can do this:
{
"apiVersion": "2018-05-01",
"type": "Microsoft.Resources/deployments",
"name": "[concat(parameters('serviceBusNamespaceName'), copyIndex())]",
"resourceGroup": "[parameters('sharedResourcesResourceGroupName')]",
"copy": {
"name": "topics",
"count": "[length(parameters('topics'))]"
},
"dependsOn": [
"[parameters('serviceBusNamespaceName')]"
],
"properties": {
"mode": "Incremental",
"template": {
"$schema": "2018-05-01",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.ServiceBus/namespaces/topics",
"name": "[concat(parameters('serviceBusNamespaceName'), '/', parameters('topics')[copyIndex()])]",
"apiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"properties": {}
}
]
}
}
}

'SubscriptionId' property is not supported for nested deployments inside a deployment at subscription scope

I am trying to use a nested template in an Azure Blueprint artifact to deploy a Topic to an Azure Service Bus. This bus is created in a different subscription than the rest of the blueprint. I've been following this article: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-cross-resource-group-deployment
My artifact is defined as:
{
"kind": "template",
"properties": {
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"servicebusNamespace": {
"type": "string"
},
"topicName": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "2019-05-01",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "myResourceGroup",
"subscriptionId": "mySubscriptionId",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.ServiceBus/namespaces/topics",
"apiVersion": "2017-04-01",
"name": "[concat(parameters('servicebusNamespace'), '/', parameters('topicName'))]",
"location": "North Europe",
"properties": {
"defaultMessageTimeToLive": "P14D",
"maxSizeInMegabytes": 1024,
"requiresDuplicateDetection": false,
"duplicateDetectionHistoryTimeWindow": "PT10M",
"enableBatchedOperations": true,
"status": "Active",
"supportOrdering": true,
"autoDeleteOnIdle": "P10675199DT2H48M5.4775807S",
"enablePartitioning": false,
"enableExpress": false
}
}
]
},
"parameters": {}
}
}
]
},
"parameters": {
"servicebusNamespace": {
"value": "[parameters('BP_servicebusNamespace')]"
},
"topicName": {
"value": "[parameters('BP_topicName')]"
}
}
},
"type": "Microsoft.Blueprint/blueprints/artifacts"
}
When importing the blueprint using Import-AzBlueprintWithArtifact -Name $blueprintName -ManagementGroupId $managementGroupId -InputPath $blueprintFolder -Force
I receive the following error:
"ERROR! This artifact is invalid. Error: 'The template resource 'nestedTemplate' at line '13' and column '5' is invalid.
'SubscriptionId' property is not supported for nested deployments inside a deployment at subscription scope. Please see https://aka.ms/arm-template/#resources for usage details.'"
the error tells you what is going on there, you cannot use a subscriptionid property for nested deployments inside a deployment at subscription scope. Try converting it to a linked template, that might work (or linked template inside resource group level deployment)

Reference an existing Azure Automation Account in a different Resource Group

I need to use an existing Azure Automation Account in ARM Templates to create a new compilation job. I know how to do this when the Automation Account is in the same Resource Group where I'm deploying, but I can't figure it out when it's an existing Automation Account in a different Resource Group.
for example:
Parent Template (Resource)
{
"name": "dscCompile",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2016-09-01",
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', 'newGuid')]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[variables('templates').dsc]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"compile-settings": {
"value": {
"configurationData": "[concat('{\"AllNodes\": [{\"NodeName\":\"*\",\"PSDscAllowPlainTextPassword\":true,\"RetryIntervalSec\":30,\"RetryCount\":20},{\"Nodename\":\"localhost\",\"domainName\":\"', parameters('extn-settings').domain, '\",\"adminCreds\":\"', parameters('adminPassword'), '\",\"Role\":\"DC\"}]}')]",
"configurationName": "createPDC",
"location": "Australia Southeast",
"name": "[reference('newGuid').outputs.guid.value)]"
}
},
"tag-values": {
"value": "[parameters('tag-values')]"
}
}
}
Child Template
$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"compile-settings": {
"type": "object",
"metadata": {
"description": "These are settings for a DSC Compile"
}
},
"tag-values": {
"type": "object",
"metadata": {
"description": "These are the Tag values"
}
}
},
"resources": [
{
"name": "[parameters('compile-settings').jobGuid]",
"type": "Microsoft.Automation/automationAccounts/compilationjobs",
"apiVersion": "2015-10-31",
"location": "[parameters('compile-settings').location]",
"tags": "[parameters('tag-values')]",
"dependsOn": [],
"properties": {
"configuration": {
"name": "[parameters('compile-settings').configurationName]"
},
"parameters": {
"ConfigurationData": "[parameters('compile-settings').ConfigurationData]"
}
},
"resources": []
}
],
"outputs": {}
}
Thanks in advance!
Okay, so this wasn't even possible until recently, you can do it with cross resource group deployment.
Basically, you create a template inside a template (aka nested\child template) and pick a different resource group (using the resourceGroup property for that template. no other way.
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "crossResourceGroupDeployment",
"properties": { }
}

Resources