Refactor the Bicep file

Completed

After the convert and migrate phases of converting your template to Bicep, you need to improve the file. This process is called refactoring. The third phase of the recommended workflow for migrating your JSON ARM template and Azure resources to Bicep is the refactor phase:

Diagram that shows the refactor phase of the recommended workflow for migrating Azure resources to Bicep.

The main focus of the refactor phase is to improve the quality of your Bicep code. Improvements can include changes like adding code comments that align the template with your template standards.

The refactor phase consists of eight steps, which you can do in any order:

  • Review resource API versions.
  • Review the linter suggestions in your new Bicep file.
  • Revise parameters, variables, and symbolic names.
  • Simplify expressions.
  • Review child and extension resources.
  • Modularize.
  • Add comments.
  • Follow Bicep best practices.

The following example shows the output from the Bicep decompile command of a JSON template that creates an App Service plan:

@description('Location for resources.')
param location string = resourceGroup().location

@allowed([
  'prod'
  'dev'
  'test'
])
@description('The list of allowed environment names.')
param environment string = 'prod'

@allowed([
  'P1v3'
  'P2v3'
  'P3v3'
])
@description('The list of allowed App Service Plan SKUs.')
param appServicePlanSku string = 'P1v3'

@minValue(1)
@maxValue(10)
@description('The number of allowed App Service Plan instances.')
param appServicePlanInstanceCount int = 1

var appServicePlanName_var = 'plan-${environment}-001'

resource appServicePlanName 'Microsoft.Web/serverfarms@2020-12-01' = {
  name: appServicePlanName_var
  location: location
  sku: {
    name: appServicePlanSku
    capacity: appServicePlanInstanceCount
  }
  kind: 'app'
  properties: {}
}

output appServicePlanId string = appServicePlanName.id

If you deploy this Bicep template as-is, the deployment will succeed, but you can improve the template to align it with your template standards.

Review resource API versions

When you use Bicep to interact with your Azure resources, you specify an API version to use. As Azure products change and improve, newer API versions are released to provide access to new functionality. When you export Azure resources, the exported template might not have the latest API version for a resource type. If you need specific properties for future deployments, update the API to the appropriate version. It's a good practice to review the API versions for each exported resource.

The Azure ARM template reference can help you verify the appropriate API versions and resource properties for your template.

Review the linter suggestions in your new Bicep file

When you create Bicep files by using the Bicep extension for Visual Studio Code, the linter runs automatically and highlights errors in your code and provides suggestions. Many of the suggestions and errors include an option to apply a quick fix for the problem. Review these recommendations and adjust your Bicep file.

Revise parameters, variables, and symbolic names

If your infrastructure supports multiple environments, such as production and development, create parameters that support these environments. A good parameter naming convention makes it easy to customize your deployments per environment.

The names of parameters, variables, and symbolic names generated by the decompiler might not match your standard naming convention. Review the generated names and make adjustments as necessary.

In the following example, the variable named appServicePlanName_var has _var appended to the end of the original variable name:

@minValue(1)
@maxValue(10)
@description('The number of allowed App Service Plan instances.')
param appServicePlanInstanceCount int = 1

var appServicePlanName_var = 'plan-${environment}-001'

resource appServicePlanName 'Microsoft.Web/serverfarms@2020-12-01' = {

For clarity, it's a good idea to remove _var from the variable name. But, when you rename the variable, its new name conflicts with the symbolic name of the App Service plan resource. So it's a good idea to rename resources first, and then rename the variables that are used in their definitions.

Tip

When you rename identifiers, be sure to rename them consistently in all parts of your template. This is especially important for parameters, variables, and resources that you refer to throughout your template.

Visual Studio Code offers a convenient way to rename symbols: select the identifier that you want to rename, select F2, enter a new name, and then select Enter:

Screenshot from Visual Studio Code that shows how to rename a symbol.

These steps rename the identifier and automatically update all references to it.

Simplify expressions

The decompile process might not always take advantage of some Bicep features. Review any expressions that are generated in the conversion and simplify them. For example, the decompiled template might include a concat() or format() function that you can simplify by using string interpolation. Review any suggestions from the linter and make adjustments as necessary.

Review child and extension resources

Bicep provides multiple ways to declare child and extension resources, including concatenating the names of your resources, using the parent property, and using nested resources.

Consider reviewing these resources after decompilation to make sure that the structure meets your standards. For example, ensure that you don't use string concatenation to create child resource names. Instead, use the parent property or a nested resource. Similarly, you can reference subnets either as properties of a virtual network or as separate resources.

Modularize

If you're converting a template that has many resources, consider breaking the individual resource types into modules for simplicity. Using modules in Bicep helps reduce the complexity of your template deployments.

Note

It's possible to use your JSON templates as modules in a Bicep deployment. Bicep can recognize JSON modules and reference them similarly to how you use Bicep modules.

Add comments and descriptions

Good Bicep code is self-documenting. In Bicep, you can add comments and descriptions to your code to document your infrastructure. Comments and descriptions can help your teammates understand the code and increase confidence when changes are made. Comments and descriptions are visible when you work with a Bicep file in Visual Studio Code, like when you need to specify a parameter value for a module. But when you deploy a Bicep file to Azure, the comments are ignored.

You can add single-line comments by using the // character sequence. For multi-line comments, start with a /* and end with a */. You can add comments that apply to specific lines in your code or to sections of code.

You can add a multi-line comment at the beginning of the file:

/*
  This Bicep file was developed by the web team.
  It deploys the resources we need for our toy company's website.
*/

You can add single-line comments as headers for sections of code or on individual lines to describe the code:

// Resource - App Service plan
@description('The App Service plan resource name.')
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSku // Specifies the SKU of the App Service plan.
    capacity: appServicePlanInstanceCount
  }
  kind: 'app' // Specifies a Windows App Service plan.
}

// Outputs
@description('The resource ID of the App Service plan.')
output appServicePlanId string = appServicePlan.id // Resource ID of the App Service plan.

Bicep provides a @description decorator that you can use to document the purpose of your parameters, variables, resources, modules, and outputs. You can add the description on the line above the item you're describing:

@description('The name of the App Service plan.')
var appServicePlanName = 'plan-${environment}-001'

Follow Bicep best practices

Make sure that your Bicep file follows standard recommendations. Review Bicep best practices for anything you might have missed.

The converted template

After you make the appropriate improvements, review the final template before you deploy it. The updated template includes revised names, API versions, descriptions, and added comments:

/*
  This Bicep file was developed by the web team.
  It deploys the resources we need for our toy company's website.
*/

// Parameters
@description('Location for all resources.')
param location string = resourceGroup().location

@allowed([
  'prod' // Production environment
  'dev' // Development environment
  'test' // Test environment
])
@description('The list of allowed environment names.')
param environment string = 'prod'

@allowed([
  'P1v3'
  'P2v3'
  'P3v3'
])
@description('The list of allowed App Service plan SKUs.')
param appServicePlanSku string = 'P1v3'

@minValue(1)
@maxValue(10)
@description('The number of allowed App Service plan instances.')
param appServicePlanInstanceCount int = 1

// Variables
@description('The name of the App Service plan.')
var appServicePlanName = 'plan-${environment}-001'

// Resource - App Service plan
@description('The App Service plan resource name.')
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSku // Specifies the SKU of the App Service plan.
    capacity: appServicePlanInstanceCount
  }
  kind: 'app' // Specifies a Windows App Service plan.
}

// Outputs
@description('The resource ID of the App Service plan.')
output appServicePlanId string = appServicePlan.id // Resource ID of the App Service plan.