Exercise - Refactor your template to use modules

Completed

In this exercise, you'll update the Bicep template you previously created so it uses a module for the Azure App Service resources. Modules help to keep the intention of the main template clearer. You can reuse the App Service module in other templates if you choose to.

During the process, you'll:

  • Add a new module and move the App Service resources into it.
  • Reference the module from the main Bicep template.
  • Add an output for the App Service app's host name, and emit it from the module and template deployments.
  • Test the deployment to ensure that the template is valid.

Add a new module file

  1. In Visual Studio Code, create a new folder called modules in the same folder where you created your main.bicep file. In the modules folder, create a file called appService.bicep. Save the file.

  2. Add the following content into the appService.bicep file:

    param location string
    param appServiceAppName string
    
    @allowed([
      'nonprod'
      'prod'
    ])
    param environmentType string
    
    var appServicePlanName = 'toy-product-launch-plan'
    var appServicePlanSkuName = (environmentType == 'prod') ? 'P2v3' : 'F1'
    
    resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
      name: appServicePlanName
      location: location
      sku: {
        name: appServicePlanSkuName
      }
    }
    
    resource appServiceApp 'Microsoft.Web/sites@2023-12-01' = {
      name: appServiceAppName
      location: location
      properties: {
        serverFarmId: appServicePlan.id
        httpsOnly: true
      }
    }
    

    Notice that you've copied the parameters and variables from your main.bicep template, because the appService.bicep template needs to be self-contained.

  3. Save the changes to the file. Notice that Visual Studio Code doesn't show you any red squiggles to indicate warnings about missing variables, missing parameters, or invalid resources.

Add a reference to the module from the parent template

Now that you have a complete module to deploy the App Service resources, you can refer to the module within the parent template. Because the module deploys the App Service resources, you can delete the associated resources and variables from the parent template.

  1. In the main.bicep file, delete the App Service resources and the appServicePlanName and appServicePlanSkuName variable definitions. Don't delete the App Service parameters, because you still need them. Also, don't delete the storage account parameters, variable, or resources.

  2. At the bottom of the main.bicep file, add the following Bicep code:

    module appService 'modules/appService.bicep' = {
      name: 'appService'
      params: {
        location: location
        appServiceAppName: appServiceAppName
        environmentType: environmentType
      }
    }
    

    Notice that you're specifying the parameters for your module by referencing the parameters in the parent template.

  3. Save the changes to the file.

Add the host name as an output

  1. Add the following Bicep code at the bottom of the appService.bicep file:

    output appServiceAppHostName string = appServiceApp.properties.defaultHostName
    

    This code declares that an output for this module, which will be named appServiceAppHostName, will be of type string. The output will take its value from the defaultHostName property of the App Service app.

  2. Save the changes to the file.

    This output is declared within a Bicep file we'll use as a module, so it's going to be available only to the parent template. You also need to return the output to the person who deployed the template.

  3. Open the main.bicep file and add the following code at the bottom of the file:

    output appServiceAppHostName string = appService.outputs.appServiceAppHostName
    

    Notice that this output is declared in a similar way to the output in the module. But this time, you're referencing the module's output instead of a resource property.

  4. Save the changes to the file.

Verify your Bicep files

After you've completed all of the preceding changes, your main.bicep file should look like this example:

param location string = 'eastus'
param storageAccountName string = 'toylaunch${uniqueString(resourceGroup().id)}'
param appServiceAppName string = 'toylaunch${uniqueString(resourceGroup().id)}'

@allowed([
  'nonprod'
  'prod'
])
param environmentType string

var storageAccountSkuName = (environmentType == 'prod') ? 'Standard_GRS' : 'Standard_LRS'

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountSkuName
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

module appService 'modules/appService.bicep' = {
  name: 'appService'
  params: {
    location: location
    appServiceAppName: appServiceAppName
    environmentType: environmentType
  }
}

output appServiceAppHostName string = appService.outputs.appServiceAppHostName

Your appService.bicep file should look like this example:

param location string
param appServiceAppName string

@allowed([
  'nonprod'
  'prod'
])
param environmentType string

var appServicePlanName = 'toy-product-launch-plan'
var appServicePlanSkuName = (environmentType == 'prod') ? 'P2v3' : 'F1'

resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSkuName
  }
}

resource appServiceApp 'Microsoft.Web/sites@2023-12-01' = {
  name: appServiceAppName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
  }
}

output appServiceAppHostName string = appServiceApp.properties.defaultHostName

If either file doesn't match, copy the example or adjust your template to match the example.

Deploy the updated Bicep template

Run the following Azure CLI command in the terminal.

az deployment group create \
  --template-file main.bicep \
  --parameters environmentType=nonprod

Run the following Azure PowerShell command in the terminal.

New-AzResourceGroupDeployment `
  -TemplateFile main.bicep `
  -environmentType nonprod

Check your deployment

  1. In your browser, go back to the Azure portal. Go to your resource group; there are now two successful deployments.

  2. Select the 2 Succeeded link. Notice that you have a deployment called main in the list, and a new deployment called appService.

    Screenshot of the Azure portal interface for the deployments, with the two deployments listed and succeeded statuses.

  3. Select the deployment called main, then select Deployment details to expand the list of deployed resources.

    Notice that our module deployment appears in the list.

    Screenshot of the Azure portal interface for the specific deployment, with one resource listed.

  4. Select the Outputs tab. Notice that there's an output called appServiceAppHostName with the host name of your App Service app. Copy the host name to your clipboard.

    Screenshot of the Azure portal interface for the specific deployment's outputs.

  5. Open a new browser tab and paste the host name that you copied. You should see the default App Service welcome page.

    Screenshot of the default App Service welcome page.

Congratulations! You've successfully deployed the foundations for a great app.