Create Resource Group With Azure Bicep and Deploy Resources In It
In this short post we will discuss how to deploy a resource group and (optionally) create resources inside of this resource group all during one deployment.
Additionally, we cover different values of targetScope
for the deployment: subscription, managementGroup, and tenant.
To create a resource group, target scope of the deployment should be set to subscription
, and a resource of type Microsoft.Resources/resourceGroups
must be created. If target scope is not subscription
, then resource group should be deployed using a module.
The ability to create a resource group from a template is useful because it eliminates the need to perform creation of resource group manually and allows managing larger deployments.
Contents:
- Overview
- Minimal Example
- Deployment Target Scopes
- Deploying Resource Group and Storage Account
- Related Posts
- Useful Links
Overview
So, we want to define a resource group in a bicep file in order to have it deployed automatically. This is a basic case and it is covered in the Minimal Example section.
Additionally, let’s assume that we also want to deploy some resources into this resource group, in our case it will be a storage account. This scenario is discussed in Deploying Resource Group and Storage Account.
The post also goes briefly about Deployment Target Scopes and how they relate to a resource group deployment. Later, this knowledge helps in understanding slightly more advanced cases of deploying at subscription, managementGroup and tenant target scopes.
In summary, this post talks about the deployment of the following resource types in a combination with different target scopes:
Microsoft.Resources/resourceGroups
- the resource group we createMicrosoft.Storage/storageAccounts
- some resource to deploy into the resource group
Minimal Example
Deploying a resource group in a Bicep file is straightforward. The simplest template should contain an assignment of subscription
target scope and definition of Microsoft.Resources/resourceGroups
resource.
// =========== main.bicep ===========
targetScope = 'subscription'
resource rg 'Microsoft.Resources/resourceGroups@2021-01-01' = {
name: 'rg-contoso'
location: 'westus2'
}
Deployment Target Scopes
Each Bicep file has a targetScope which is set implicitly or explicitly, it is used to perform validation and checks on the resources in the template.
When deploying a resource group, your target scope likely to be subscription
or higher, because target scope resourceGroup
makes less sense when creating a resource group in a template.
Target scope possible values are:
resourceGroup
(default)subscription
managementGroup
tenant
Setting target scope is done by using targetScope
keyword and a scope name, for example:
targetScope = 'subscription'
In the following sections we will cover two cases:
- Deploying main bicep file at the
subscription
target scope - Deploying main bicep file at the
managementGroup
andtenant
target scopes
Deploying Resource Group and Storage Account
In the Minimal Example we saw how to deploy just a resource group. In this part of the post, we are going to also deploy a storage account in the newly created resource group.
Target Scope “subscription”
As already discussed, we need to deploy two resources: a resource group and a storage account.
- Resource group resource (
Microsoft.Resources/resourceGroups
) has to be deployed at thesubscription
scope. Since the scope of the bicep file issubscription
as well, we can simply deploy resource group in the main file. - Storage account resource has to be deployed at the
resourceGroup
scope. Therefore, we need to use modules to deploy it at a scope different from the main file.
Bicep Files
Our main.bicep
file contains deployment of a resource group and a storage account module:
// =========== main.bicep ===========
// Setting target scope
targetScope = 'subscription'
// Creating resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-01-01' = {
name: 'rg-contoso'
location: 'westus2'
}
// Deploying storage account using module
module stg './storage.bicep' = {
name: 'storageDeployment'
scope: rg // Deployed in the scope of resource group we created above
params: {
storageAccountName: 'stcontoso'
}
}
// =========== storage.bicep ===========
// targetScope = 'resourceGroup' - not needed since it is the default value
param storageAccountName string
resource stg 'Microsoft.Storage/storageAccounts@2021-02-01' = {
name: storageAccountName
location: resourceGroup().location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
Compiled ARM Template
Only if you are curious how Bicep files above look like when compiled into an ARM template, see JSON below.
NOTES:
- Storage account is deployed in a nested deployment
Microsoft.Resources/deployments
(line 12) - Scope of the nested deployment is set via
resourceGroup
property (line 15) $schema
property has two values:../subscriptionDeploymentTemplate.json#
for the parent template (line 2) and../deploymentTemplate.json#
for nested deployment (line 27)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2021-01-01",
"name": "rg-contoso",
"location": "westus2"
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2019-10-01",
"name": "storageDeployment",
"resourceGroup": "rg-contoso",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
},
"mode": "Incremental",
"parameters": {
"storageAccountName": {
"value": "stcontoso"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string"
}
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-02-01",
"name": "[parameters('storageAccountName')]",
"location": "[resourceGroup().location]",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2"
}
]
}
},
"dependsOn": [
"[subscriptionResourceId('Microsoft.Resources/resourceGroups', 'rg-contoso')]"
]
}
]
}
Target Scopes “managementGroup” and “tenant”
Now, how to deploy a resource group if our deployment targetScope is not subscription? - Use modules to specify the right scope for a resource group.
Bicep modules have an optional scope
property which can be used to specify a scope different from the bicep file where module is consumed. We’ve already used this functionality for the storage account in the previous section.
Modules: Resource Group and Storage Account
Since our main file’s targetScope
is not subscription
, we need to put resource group into a separate module (just another bicep file).
These modules are the same both for managementGroup
and tenant
target scopes
Below is our resource-group.bicep
file, it deploys the resource group and a storage account module. This file is identical to main.bicep
from the previous chapter where we deployed at the subscription
target scope.
// =========== resource-group.bicep ===========
targetScope = 'subscription' // Resource group must be deployed under 'subscription' scope
param resourceGroupName string
param location string
resource rg 'Microsoft.Resources/resourceGroups@2021-01-01' = {
name: resourceGroupName
location: location
}
module stg './storage.bicep' = {
name: 'storageDeployment'
params: {
storageAccountName: 'stcontoso'
}
scope: rg
}
The storage.bicep
file is the same as in the previous section - simple storage account declaration.
// =========== storage.bicep ===========
// targetScope = 'resourceGroup' - not needed since it is the default value
param storageAccountName string
resource stg 'Microsoft.Storage/storageAccounts@2021-02-01' = {
name: storageAccountName
location: resourceGroup().location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
Deploying at “managementGroup” or “tenant” targetScopes
Now, we just need to consume resource-group.bicep
module inside of our main bicep file.
Important point is to specify the correct scope for the module, this should be subscription for resource group.
Deploying at the tenant
scope is almost identical to deploying at the management group scope. The only difference is the targetScope
of the file, and that’s it.
NOTE: You need permissions at the tenant level to be able to deploy at the tenant
scope. Read more about the required access.
// =========== main.bicep ===========
targetScope = 'managementGroup' // Setting target scope
// targetScope = 'tenant' - if deploying at the tenant scop
param subscriptionId string
param resourceGroupName string = 'rg-contoso'
param dateTime string = utcNow() // Just to make resource group deployment name unique
// Deploying the resource group and a storage account inside of it
module rg './resource-group.bicep' = {
name: 'resourceGroupDeployment-${dateTime}'
params: {
resourceGroupName: resourceGroupName
location: 'westus2'
}
scope: subscription(subscriptionId) // Passing subscription scope
}
Related Posts
- Parameters In Azure Bicep - Ultimate Guide With Examples
- Variables In Azure Bicep - From Basics To Advanced
- Learn Modules In Azure Bicep - Basics To Advanced, How It Works, Nested Modules, Outputs, Scopes
- Reference New Or Existing Resource In Azure Bicep
- Child Resources In Azure Bicep - 3 Ways To Declare, Loops, Conditions
- 5 Ways To Deploy Bicep File With Parameters - Azure DevOps, PowerShell, CLI, Portal, Cloud Shell
- Using Key Vault Secrets As Secure Parameters In Azure Bicep - Template & Module Inputs
- Deploy Azure Bicep In YAML and Classic Release Pipelines (CI/CD) - Azure DevOps
- Reference() Function Explained With Examples - ARM Template