Exercise - Use variable and output loops

Completed

For your toy company, you need to deploy virtual networks in each country/region where you're launching the teddy bear. Your developers have also asked you to give them the fully qualified domain names (FQDNs) of each of the regional Azure SQL logical servers you've deployed.

In this exercise, you'll add the virtual network and its configuration to your Bicep code, and you'll output the logical server FQDNs.

During the process, you'll:

  • Update your Bicep code to specify a parameter for each virtual network's subnets.
  • Add a variable loop to create a subnet array, which you'll use in the virtual network resource declaration.
  • Add an output loop to create the list of logical server FQDNs.
  • Deploy the Bicep file and verify the deployment.

Add the virtual network to your Bicep file

  1. Open the main.bicep file.

  2. Below the parameter declarations, add the following parameters:

    @description('The IP address range for all virtual networks to use.')
    param virtualNetworkAddressPrefix string = '10.10.0.0/16'
    
    @description('The name and IP address range for each subnet in the virtual networks.')
    param subnets array = [
      {
        name: 'frontend'
        ipAddressRange: '10.10.5.0/24'
      }
      {
        name: 'backend'
        ipAddressRange: '10.10.10.0/24'
      }
    ]
    
  3. Below the parameters, add a blank line, and then add the subnetProperties variable loop:

    var subnetProperties = [for subnet in subnets: {
      name: subnet.name
      properties: {
        addressPrefix: subnet.ipAddressRange
      }
    }]
    
  4. At the bottom of the file, below the databases module loop, add the following resource loop:

    resource virtualNetworks 'Microsoft.Network/virtualNetworks@2024-01-01' = [for location in locations: {
      name: 'teddybear-${location}'
      location: location
      properties:{
        addressSpace:{
          addressPrefixes:[
            virtualNetworkAddressPrefix
          ]
        }
        subnets: subnetProperties
      }
    }]
    

    Note

    This example uses the same address space for all the virtual networks. Ordinarily, when you create multiple virtual networks, you would give them different address spaces in the event that you need to connect them together.

  5. Save the changes to the file.

Add outputs to the database module

  1. Open the modules/database.bicep file.

  2. At the bottom of the file, add the following outputs:

    output serverName string = sqlServer.name
    output location string = location
    output serverFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName
    
  3. Save the changes to the file.

Flow the outputs through the parent Bicep file

  1. Open the main.bicep file.

  2. At the bottom of the file, add the following output loop:

    output serverInfo array = [for i in range(0, length(locations)): {
      name: databases[i].outputs.serverName
      location: databases[i].outputs.location
      fullyQualifiedDomainName: databases[i].outputs.serverFullyQualifiedDomainName
    }]
    
  3. Save the changes to the file.

Verify your Bicep file

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

@description('The Azure regions into which the resources should be deployed.')
param locations array = [
  'westeurope'
  'eastus2'
  'eastasia'
]

@secure()
@description('The administrator login username for the SQL server.')
param sqlServerAdministratorLogin string

@secure()
@description('The administrator login password for the SQL server.')
param sqlServerAdministratorLoginPassword string

@description('The IP address range for all virtual networks to use.')
param virtualNetworkAddressPrefix string = '10.10.0.0/16'

@description('The name and IP address range for each subnet in the virtual networks.')
param subnets array = [
  {
    name: 'frontend'
    ipAddressRange: '10.10.5.0/24'
  }
  {
    name: 'backend'
    ipAddressRange: '10.10.10.0/24'
  }
]

var subnetProperties = [for subnet in subnets: {
  name: subnet.name
  properties: {
    addressPrefix: subnet.ipAddressRange
  }
}]

module databases 'modules/database.bicep' = [for location in locations: {
  name: 'database-${location}'
  params: {
    location: location
    sqlServerAdministratorLogin: sqlServerAdministratorLogin
    sqlServerAdministratorLoginPassword: sqlServerAdministratorLoginPassword
  }
}]

resource virtualNetworks 'Microsoft.Network/virtualNetworks@2024-01-01' = [for location in locations: {
  name: 'teddybear-${location}'
  location: location
  properties:{
    addressSpace:{
      addressPrefixes:[
        virtualNetworkAddressPrefix
      ]
    }
    subnets: subnetProperties
  }
}]

output serverInfo array = [for i in range(0, length(locations)): {
  name: databases[i].outputs.serverName
  location: databases[i].outputs.location
  fullyQualifiedDomainName: databases[i].outputs.serverFullyQualifiedDomainName
}]

Your database.bicep file should look like this example:

@description('The Azure region into which the resources should be deployed.')
param location string

@secure()
@description('The administrator login username for the SQL server.')
param sqlServerAdministratorLogin string

@secure()
@description('The administrator login password for the SQL server.')
param sqlServerAdministratorLoginPassword string

@description('The name and tier of the SQL database SKU.')
param sqlDatabaseSku object = {
  name: 'Standard'
  tier: 'Standard'
}

@description('The name of the environment. This must be Development or Production.')
@allowed([
  'Development'
  'Production'
])
param environmentName string = 'Development'

@description('The name of the audit storage account SKU.')
param auditStorageAccountSkuName string = 'Standard_LRS'

var sqlServerName = 'teddy${location}${uniqueString(resourceGroup().id)}'
var sqlDatabaseName = 'TeddyBear'
var auditingEnabled = environmentName == 'Production'
var auditStorageAccountName = take('bearaudit${location}${uniqueString(resourceGroup().id)}', 24)

resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = {
  name: sqlServerName
  location: location
  properties: {
    administratorLogin: sqlServerAdministratorLogin
    administratorLoginPassword: sqlServerAdministratorLoginPassword
  }
}

resource sqlDatabase 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
  parent: sqlServer
  name: sqlDatabaseName
  location: location
  sku: sqlDatabaseSku
}

resource auditStorageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = if (auditingEnabled) {
  name: auditStorageAccountName
  location: location
  sku: {
    name: auditStorageAccountSkuName
  }
  kind: 'StorageV2'  
}

resource sqlServerAudit 'Microsoft.Sql/servers/auditingSettings@2023-08-01-preview' = if (auditingEnabled) {
  parent: sqlServer
  name: 'default'
  properties: {
    state: 'Enabled'
    storageEndpoint: environmentName == 'Production' ? auditStorageAccount.properties.primaryEndpoints.blob : ''
    storageAccountAccessKey: environmentName == 'Production' ? listKeys(auditStorageAccount.id, auditStorageAccount.apiVersion).keys[0].value : ''
  }
}

output serverName string = sqlServer.name
output location string = location
output serverFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName

If they don't, either copy the examples or adjust your templates to match the examples.

Deploy the Bicep template to Azure

In the Visual Studio Code terminal, deploy the Bicep template to Azure by running the following code. This process can take a couple of minutes to finish, and then you'll have a successful deployment.

az deployment group create --template-file main.bicep

In the Visual Studio Code terminal, deploy the Bicep template to Azure by running the following Azure PowerShell command. This process can take a couple of minutes to finish, and then you'll have a successful deployment.

New-AzResourceGroupDeployment -TemplateFile main.bicep

Caution

Be sure to use the same login and password that you used previously, or the deployment won't finish successfully.

Wait for the deployment to finish.

Verify the deployment

After the deployment is finished, you want to verify that new virtual networks are deployed and that they have subnets configured as you expect.

  1. Go to the Azure portal and make sure that you're in the sandbox subscription.

  2. Select [sandbox resource group name].

  3. Verify that the virtual networks have been deployed to the three Azure locations.

    Screenshot of the Azure portal, showing a list of virtual networks after deployment.

  4. Select the virtual network named teddybear-eastasia.

  5. In the search bar, enter Subnets. Under Settings, select Subnets.

    Screenshot of the Azure portal interface for the virtual network, showing the search field with Subnets entered.

  6. Verify that the deployed subnets have the names and IP addresses that were specified in the subnets parameter's default value.

    Screenshot of the Azure portal that shows two virtual network subnets after deployment.

  7. Check the output of the deployment command. It should include the name and FQDN of all three of the logical servers that were deployed, as shown here:

    Screenshot of the deployment output, displaying the properties of the logical servers.

  1. Go to the Azure portal and make sure that you're in the sandbox subscription.

  2. Select [sandbox resource group name].

  3. Verify that the virtual networks have been deployed to the three Azure locations.

    Screenshot of the Azure portal, showing a list of virtual networks after deployment.

  4. Select the virtual network named teddybear-eastasia.

  5. In the search bar, enter Subnets. Under Settings, select Subnets.

    Screenshot of the Azure portal interface for the virtual network, showing the search field with Subnets entered.

  6. Verify that the deployed subnets have the names and IP addresses that were specified in the subnets parameter's default value.

    Screenshot of the Azure portal that shows two virtual network subnets after deployment.

  7. Check the output of the deployment command. It should include the name and FQDN of all three of the logical servers that were deployed, as shown here:

    Screenshot of the deployment output, displaying the properties of the logical servers.