Tutorial: Add modules to Azure Resource Manager Bicep file

In the previous tutorial, you learned how to use a parameter file to deploy your Bicep file. In this tutorial, you learn how to use Bicep modules to encapsulate complex details of the raw resource declaration. The modules can be shared and reused within your solution. It takes about 12 minutes to complete.

Note

This article contains Bicep examples. Bicep is currently in preview. For more information, see Project Bicep.

Prerequisites

We recommend that you complete the tutorial about parameter file, but it's not required.

You must have Visual Studio Code with the Bicep extension, and either Azure PowerShell or Azure CLI. For more information, see Bicep tools.

Review Bicep file

At the end of the previous tutorial, your Bicep file had the following contents:

@minLength(3)
@maxLength(11)
param storagePrefix string

@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
  'Premium_LRS'
])
param storageSKU string = 'Standard_LRS'

param location string = resourceGroup().location
param appServicePlanName string = 'exampleplan'

@minLength(2)
@description('Base name of the resource such as web app name and app service plan.')
param webAppName string

@description('The Runtime stack of current web app.')
param linuxFxVersion string = 'php|7.0'

param resourceTags object = {
  Environment: 'Dev'
  Project: 'Tutorial'
}

var uniqueStorageName = '${storagePrefix}${uniqueString(resourceGroup().id)}'
var webAppPortalName = '${webAppName}${uniqueString(resourceGroup().id)}'

resource stg 'Microsoft.Storage/storageAccounts@2019-04-01' = {
  name: uniqueStorageName
  location: location
  tags: resourceTags
  sku: {
    name: storageSKU
  }
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
  }
}

resource appPlan 'Microsoft.Web/serverfarms@2016-09-01' = {
  name: appServicePlanName
  location: location
  tags: resourceTags
  sku: {
    name: 'B1'
    tier: 'Basic'
    size: 'B1'
    family: 'B'
    capacity: 1
  }
  kind: 'linux'
  properties: {
    perSiteScaling: false
    reserved: true
    targetWorkerCount: 0
    targetWorkerSizeId: 0
  }
}

resource site 'Microsoft.Web/sites@2016-08-01' = {
  name: webAppPortalName
  location: location
  tags: resourceTags
  kind: 'app'
  properties: {
    serverFarmId: appPlan.id
    siteConfig: {
      linuxFxVersion: linuxFxVersion
    }
  }
}

output storageEndpoint object = stg.properties.primaryEndpoints

This Bicep file works well. But for larger solutions, you want to break your Bicep file into many related modules so you can share and reuse these modules. The current Bicep file deploys a storage account, an app service plan, and a website. Let's separate the storage account into a module.

Create Bicep module

Every Bicep file can be consumed as a module, so there is no specific syntax for defining a module. Create a storage.bicep file with the following contents:

@minLength(3)
@maxLength(11)
param storagePrefix string

@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
  'Premium_LRS'
])
param storageSKU string = 'Standard_LRS'

param location string
param resourceTags object

var uniqueStorageName = '${storagePrefix}${uniqueString(resourceGroup().id)}'

resource stg 'Microsoft.Storage/storageAccounts@2019-04-01' = {
  name: uniqueStorageName
  location: location
  tags: resourceTags
  sku: {
    name: storageSKU
  }
  kind: 'StorageV2'
  properties: {
    supportsHttpsTrafficOnly: true
  }
}

output storageEndpoint object = stg.properties.primaryEndpoints

This module contains the storage account resource and the related parameters and variables. The values for the location parameter and the resourceTags parameters have been removed. These values will be passed from the main Bicep file.

Consume Bicep module

Replace the storage account resource definition in the existing azuredeploy.bicep with the following Bicep contents:

module stg './storage.bicep' = {
  name: 'storageDeploy'
  params: {
    storagePrefix: storagePrefix
    location: location
    resourceTags: resourceTags
  }
}
  • module: Keyword.
  • symbolic name (stg): This is an identifier for the module.
  • module file: The path to the module in this example is specified using a relative path (./storage.bicep). All paths in Bicep must be specified using the forward slash (/) directory separator to ensure consistent compilation cross-platform. The Windows backslash () character is unsupported.

To retrieve storage endpoint, update the output in azuredeploy.bicep to the following Bicep:

output storageEndpoint object = stg.outputs.storageEndpoint

The completed azuredeploy.bicep has the following contents:

@minLength(3)
@maxLength(11)
param storagePrefix string

@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
  'Premium_LRS'
])
param storageSKU string = 'Standard_LRS'

param location string = resourceGroup().location
param appServicePlanName string = 'exampleplan'
param webAppName string {
  minLength: 2
  metadata: {
    description: 'Base name of the resource such as web app name and app service plan '
  }
}
param linuxFxVersion string {
  metadata: {
    description: 'The Runtime stack of current web app'
  }
  default: 'php|7.0'
}
param resourceTags object = {
  Environment: 'Dev'
  Project: 'Tutorial'
}

var uniqueStorageName = '${storagePrefix}${uniqueString(resourceGroup().id)}'
var webAppPortalName = '${webAppName}${uniqueString(resourceGroup().id)}'

module stg './storage.bicep' = {
  name: 'storageDeploy'
  params: {
    storagePrefix: storagePrefix
    location: location
    resourceTags: resourceTags
  }
}

resource appPlan 'Microsoft.Web/serverfarms@2016-09-01' = {
  name: appServicePlanName
  location: location
  tags: resourceTags
  sku: {
    name: 'B1'
    tier: 'Basic'
    size: 'B1'
    family: 'B'
    capacity: 1
  }
  kind: 'linux'
  properties: {
    perSiteScaling: false
    reserved: true
    targetWorkerCount: 0
    targetWorkerSizeId: 0
  }
}

resource site 'Microsoft.Web/sites@2016-08-01' = {
  name: webAppPortalName
  location: location
  tags: resourceTags
  kind: 'app'
  properties: {
    serverFarmId: appPlan.id
    siteConfig: {
      linuxFxVersion: linuxFxVersion
    }
  }
}

output storageEndpoint object = stg.outputs.storageEndpoint

Deploy template

Use either Azure CLI or Azure PowerShell to deploy the template.

If you haven't created the resource group, see Create resource group. The example assumes you've set the bicepFile variable to the path to the Bicep file, as shown in the first tutorial.

To run this deployment cmdlet, you must have the latest version of Azure PowerShell.

New-AzResourceGroupDeployment `
  -Name addmodule `
  -ResourceGroupName myResourceGroup `
  -TemplateFile $bicepFile `
  -storagePrefix "store" `
  -storageSKU Standard_LRS `
  -webAppName demoapp

Note

If the deployment failed, use the verbose switch to get information about the resources being created. Use the debug switch to get more information for debugging.

Verify deployment

You can verify the deployment by exploring the resource groups from the Azure portal.

  1. Sign in to the Azure portal.
  2. From the left menu, select Resource groups.
  3. You will see the new resource group you deployed in this tutorial.
  4. Select the resource group and view the deployed resources. Notice that they match the values you specified in your template file.

Clean up resources

  1. From the Azure portal, select Resource group from the left menu.
  2. Enter the resource group name in the Filter by name field. If you've completed this series, you have three resource groups to delete - myResourceGroup, myResourceGroupDev, and myResourceGroupProd.
  3. Select the resource group name.
  4. Select Delete resource group from the top menu.

Next steps

Congratulations, you've finished this introduction to deploying Bicep files to Azure. Let us know if you have any comments and suggestions in the feedback section. Thanks!

The next tutorial series goes into more detail about deploying templates.