Övning – Omstrukturera Bicep-filen

Slutförd

När du har granskat mallen med dina kollegor bestämmer du dig för att omstrukturera filen så att den blir enklare för dem att arbeta med. I den här övningen använder du de metodtips som du har lärt dig i föregående enheter.

Din uppgift

Granska Bicep-mallen som du sparade tidigare. Tänk på de råd du har läst om hur du strukturerar dina mallar. Försök att uppdatera mallen för att göra det enklare för dina kollegor att förstå.

I nästa avsnitt finns det några pekare på specifika delar av mallen och några tips om saker som du kanske vill ändra. Vi tillhandahåller en föreslagen lösning, men mallen kan se annorlunda ut, vilket är helt OK!

Dricks

När du går igenom refaktoriseringsprocessen är det bra att se till att Bicep-filen är giltig och att du inte oavsiktligt har introducerat några fel. Bicep-tillägget för Visual Studio Code hjälper till med detta. Se upp för eventuella röda eller gula vågiga rader under koden, eftersom de indikerar ett fel eller en varning. Du kan också visa en lista över problemen i filen genom att välja Visa>problem.

Uppdatera parametrarna

  1. Vissa parametrar i mallen är inte tydliga. Tänk till exempel på följande parametrar:

    @allowed([
      'F1'
      'D1'
      'B1'
      'B2'
      'B3'
      'S1'
      'S2'
      'S3'
      'P1'
      'P2'
      'P3'
      'P4'
    ])
    param skuName string = 'F1'
    
    @minValue(1)
    param skuCapacity int = 1
    

    Vad används de till?

    Dricks

    Om du har en parameter som du försöker förstå kan Visual Studio Code hjälpa dig. Välj och håll (eller högerklicka) ett parameternamn var som helst i filen och välj Sök efter alla referenser.

    Behöver mallen ange listan över tillåtna värden för parametern skuName ? Vilka resurser påverkas av att välja olika värden för dessa parametrar? Finns det bättre namn som du kan ge parametrarna?

    Dricks

    När du byter namn på identifierare måste du byta namn på dem konsekvent i alla delar av mallen. Detta är särskilt viktigt för parametrar, variabler och resurser som du refererar till i hela mallen.

    Visual Studio Code är ett praktiskt sätt att byta namn på symboler: välj den identifierare som du vill byta namn på, välj F2, ange ett nytt namn och välj sedan Retur:

    Skärmbild från Visual Studio Code som visar hur du byter namn på en symbol.

    De här stegen byter namn på identifieraren och uppdaterar automatiskt alla referenser till den.

  2. Parametern managedIdentityName har inget standardvärde. Kan du åtgärda det eller, ännu bättre, skapa namnet automatiskt i mallen?

  3. Titta på parameterdefinitionen roleDefinitionId :

    param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
    

    Varför finns det ett standardvärde på b24988ac-6180-42a0-ab88-20f7382dd24c? Vad betyder den långa identifieraren? Hur skulle någon annan veta om man ska använda standardvärdet eller åsidosätta det? Vad kan du göra för att förbättra identifieraren? Är det ens meningsfullt att ha detta som en parameter?

    Dricks

    Den identifieraren är rolldefinitions-ID för deltagare för Azure. Hur kan du använda den informationen för att förbättra mallen?

  4. Hur vet någon vad varje parameter är till för när någon distribuerar mallen? Kan du lägga till några beskrivningar som hjälper mallens användare?

Lägga till en konfigurationsuppsättning

  1. Du pratar med dina kollegor och bestämmer dig för att använda specifika SKU:er för varje resurs, beroende på vilken miljö som distribueras. Du bestämmer dig för dessa SKU:er för var och en av dina resurser:

    Resurs SKU för produktion SKU för icke-produktion
    App Service-plan S1, två instanser F1, en instans
    Lagringskonto GRS LRS
    SQL-databas S1 Grundläggande
  2. Kan du använda en konfigurationsuppsättning för att förenkla parameterdefinitionerna?

Uppdatera de symboliska namnen

Ta en titt på de symboliska namnen för resurserna i mallen. Vad kan du göra för att förbättra dem?

  1. Din Bicep-mall innehåller resurser med en mängd olika versaler för deras symboliska namn, till exempel:

    • storageAccount och webSite, som använder camelCase-versaler.
    • roleassignment och sqlserver, som använder flat versaler.
    • sqlserverName_databaseName och AppInsights_webSiteName, som använder versaler för ormfall.

    Kan du åtgärda dessa för att använda ett format konsekvent?

  2. Titta på den här rolltilldelningsresursen:

    resource roleassignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
      name: guid(roleDefinitionId, resourceGroup().id)
    
      properties: {
        principalType: 'ServicePrincipal'
        roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
        principalId: msi.properties.principalId
      }
    }
    

    Är det symboliska namnet tillräckligt beskrivande för att hjälpa någon annan att arbeta med den här mallen?

    Dricks

    Anledningen till att identiteten behöver en rolltilldelning är att webbappen använder sin hanterade identitet för att ansluta till databasservern. Hjälper det dig att klargöra detta i mallen?

  3. Några resurser har symboliska namn som inte återspeglar de aktuella namnen på Azure-resurser:

    resource hostingPlan 'Microsoft.Web/serverfarms@2023-12-01' = {
      // ...
    }
    resource webSite 'Microsoft.Web/sites@2023-12-01' = {
      // ...
    }
    resource msi 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = {
      // ...
    }
    

    Hanterade identiteter brukade kallas MSI: er, App Service-planer brukade kallas värdplaner och App Service-appar brukade kallas webbplatser.

    Kan du uppdatera dessa till de senaste namnen för att undvika förvirring i framtiden?

Förenkla blobcontainerns definitioner

  1. Titta på hur blobcontainrarna definieras:

    resource container1 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = {
      parent: storageAccount::blobServices
      name: container1Name
    }
    
    resource productmanuals 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-05-01' = {
      name: '${storageAccount.name}/default/${productmanualsName}'
    }
    

    En av dem använder egenskapen parent och den andra inte. Kan du åtgärda dessa så att de är konsekventa?

  2. Blobcontainerns namn ändras inte mellan miljöer. Tror du att namnen måste anges med hjälp av parametrar?

  3. Det finns två blobcontainrar. Kan de distribueras med hjälp av en loop?

Uppdatera resursnamnen

  1. Det finns några parametrar som uttryckligen anger resursnamn:

    param managedIdentityName string
    param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
    param webSiteName string = 'webSite${uniqueString(resourceGroup().id)}'
    param container1Name string = 'productspecs'
    param productmanualsName string = 'productmanuals'
    

    Finns det något annat sätt att göra det här?

    Varning

    Kom ihåg att resurser inte kan byta namn när de har distribuerats. När du ändrar mallar som redan används bör du vara försiktig när du ändrar hur mallen skapar resursnamn. Om mallen distribueras om och resursen har ett nytt namn skapar Azure en annan resurs. Den kan till och med ta bort den gamla resursen om du distribuerar den i fullständigt läge.

    Du behöver inte oroa dig för detta här, eftersom det bara är ett exempel.

  2. Den logiska SQL-serverns resursnamn anges med hjälp av en variabel, även om den behöver ett globalt unikt namn:

    var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
    

    Hur kan du förbättra detta?

Uppdatera beroenden och underordnade resurser

  1. Här är en av dina resurser, som innehåller en dependsOn egenskap. Behöver den verkligen det?

    resource sqlserverName_AllowAllAzureIPs 'Microsoft.Sql/servers/firewallRules@2023-08-01-preview' = {
      name: '${sqlserver.name}/AllowAllAzureIPs'
      properties: {
        endIpAddress: '0.0.0.0'
        startIpAddress: '0.0.0.0'
      }
      dependsOn: [
        sqlserver
      ]
    }
    
  2. Observera hur dessa underordnade resurser deklareras i mallen:

    resource sqlserverName_databaseName 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
      name: '${sqlserver.name}/${databaseName}'
      location: location
      sku: {
        name: 'Basic'
      }
      properties: {
        collation: 'SQL_Latin1_General_CP1_CI_AS'
        maxSizeBytes: 1073741824
      }
    }
    
    resource sqlserverName_AllowAllAzureIPs 'Microsoft.Sql/servers/firewallRules@2023-08-01-preview' = {
      name: '${sqlserver.name}/AllowAllAzureIPs'
      properties: {
        endIpAddress: '0.0.0.0'
        startIpAddress: '0.0.0.0'
      }
      dependsOn: [
        sqlserver
      ]
    }
    

    Hur kan du ändra hur dessa resurser deklareras? Finns det några andra resurser i mallen som också ska uppdateras?

Uppdatera egenskapsvärden

  1. Ta en titt på resursegenskaperna för SQL-databasen:

    resource sqlserverName_databaseName 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
      name: '${sqlserver.name}/${databaseName}'
      location: location
      sku: {
        name: 'Basic'
      }
      properties: {
        collation: 'SQL_Latin1_General_CP1_CI_AS'
        maxSizeBytes: 1073741824
      }
    }
    

    Är det vettigt att hårdkoda SKU:ns egenskapsvärde name ? Och vilka är dessa konstiga värden för collation egenskaperna och maxSizeBytes ?

    Dricks

    Egenskaperna collation och maxSizeBytes är inställda på standardvärdena. Om du inte anger värdena själv används standardvärdena. Hjälper det dig att bestämma vad du ska göra med dem?

  2. Kan du ändra hur lagringen anslutningssträng anges så att det komplexa uttrycket inte definieras i förhållande till resursen?

    resource webSite 'Microsoft.Web/sites@2023-12-01' = {
      name: webSiteName
      location: location
      properties: {
        serverFarmId: hostingPlan.id
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: AppInsights_webSiteName.properties.InstrumentationKey
            }
            {
              name: 'StorageAccountConnectionString'
              value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
            }
          ]
        }
      }
      identity: {
        type: 'UserAssigned'
        userAssignedIdentities: {
          '${msi.id}': {}
        }
      }
    }
    

Ordning på element

  1. Är du nöjd med ordningen på elementen i filen? Hur kan du förbättra filens läsbarhet genom att flytta runt elementen?

  2. Ta en titt på variabeln databaseName . Hör den hemma där den är nu?

    var databaseName = 'ToyCompanyWebsite'
    resource sqlserverName_databaseName 'Microsoft.Sql/servers/databases@2023-08-01-preview' = {
      name: '${sqlserver.name}/${databaseName}'
      location: location
      sku: {
        name: 'Basic'
      }
      properties: {
        collation: 'SQL_Latin1_General_CP1_CI_AS'
        maxSizeBytes: 1073741824
      }
    }
    
  3. Har du lagt märke till den utkommenterade resursen webSiteConnectionStrings? Tror du att det måste finnas i filen?

Lägga till kommentarer, taggar och andra metadata

Tänk på något i mallen som kanske inte är uppenbart eller som behöver ytterligare förklaring. Kan du lägga till kommentarer för att göra det tydligare för andra som kan öppna filen i framtiden?

  1. Ta en titt på resursens webSite identity egenskap:

    resource webSite 'Microsoft.Web/sites@2023-12-01' = {
      name: webSiteName
      location: location
      properties: {
        serverFarmId: hostingPlan.id
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: AppInsights_webSiteName.properties.InstrumentationKey
            }
            {
              name: 'StorageAccountConnectionString'
              value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'
            }
          ]
        }
      }
      identity: {
        type: 'UserAssigned'
        userAssignedIdentities: {
          '${msi.id}': {}
        }
      }
    }
    

    Den syntaxen är konstig, eller hur? Tror du att detta behöver en kommentar för att förklara det?

  2. Titta på rolltilldelningsresursen:

    resource roleassignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
      name: guid(roleDefinitionId, resourceGroup().id)
    
      properties: {
        principalType: 'ServicePrincipal'
        roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId)
        principalId: msi.properties.principalId
      }
    }
    

    Resursnamnet använder guid() funktionen. Skulle det hjälpa att förklara varför?

  3. Kan du lägga till en beskrivning i rolltilldelningen?

  4. Kan du lägga till en uppsättning taggar till varje resurs?

Föreslagen lösning

Här är ett exempel på hur du kan omstrukturera mallen. Mallen kanske inte ser exakt ut så här, eftersom formatmallen kan vara annorlunda.

@description('The location into which your Azure resources should be deployed.')
param location string = resourceGroup().location

@description('Select the type of environment you want to provision. Allowed values are Production and Test.')
@allowed([
  'Production'
  'Test'
])
param environmentType string

@description('A unique suffix to add to resource names that need to be globally unique.')
@maxLength(13)
param resourceNameSuffix string = uniqueString(resourceGroup().id)

@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 tags to apply to each resource.')
param tags object = {
  CostCenter: 'Marketing'
  DataClassification: 'Public'
  Owner: 'WebsiteTeam'
  Environment: 'Production'
}

// Define the names for resources.
var appServiceAppName = 'webSite${resourceNameSuffix}'
var appServicePlanName = 'AppServicePLan'
var sqlServerName = 'sqlserver${resourceNameSuffix}'
var sqlDatabaseName = 'ToyCompanyWebsite'
var managedIdentityName = 'WebSite'
var applicationInsightsName = 'AppInsights'
var storageAccountName = 'toywebsite${resourceNameSuffix}'
var blobContainerNames = [
  'productspecs'
  'productmanuals'
]

@description('Define the SKUs for each component based on the environment type.')
var environmentConfigurationMap = {
  Production: {
    appServicePlan: {
      sku: {
        name: 'S1'
        capacity: 2
      }
    }
    storageAccount: {
      sku: {
        name: 'Standard_GRS'
      }
    }
    sqlDatabase: {
      sku: {
        name: 'S1'
        tier: 'Standard'
      }
    }
  }
  Test: {
    appServicePlan: {
      sku: {
        name: 'F1'
        capacity: 1
      }
    }
    storageAccount: {
      sku: {
        name: 'Standard_LRS'
      }
    }
    sqlDatabase: {
      sku: {
        name: 'Basic'
      }
    }
  }
}

@description('The role definition ID of the built-in Azure \'Contributor\' role.')
var contributorRoleDefinitionId = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
var storageAccountConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccount.id, storageAccount.apiVersion).keys[0].value}'

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

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

resource sqlFirewallRuleAllowAllAzureIPs 'Microsoft.Sql/servers/firewallRules@2023-08-01-preview' = {
  parent: sqlServer
  name: 'AllowAllAzureIPs'
  properties: {
    endIpAddress: '0.0.0.0'
    startIpAddress: '0.0.0.0'
  }
}

resource appServicePlan 'Microsoft.Web/serverfarms@2023-12-01' = {
  name: appServicePlanName
  location: location
  sku: environmentConfigurationMap[environmentType].appServicePlan.sku
  tags: tags
}

resource appServiceApp 'Microsoft.Web/sites@2023-12-01' = {
  name: appServiceAppName
  location: location
  tags: tags
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      appSettings: [
        {
          name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
          value: applicationInsights.properties.InstrumentationKey
        }
        {
          name: 'StorageAccountConnectionString'
          value: storageAccountConnectionString
        }
      ]
    }
  }
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${managedIdentity.id}': {} // This format is required when working with user-assigned managed identities.
    }
  }
}

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

  resource blobServices 'blobServices' existing = {
    name: 'default'

    resource containers 'containers' = [for blobContainerName in blobContainerNames: {
      name: blobContainerName
    }]
  }
}

@description('A user-assigned managed identity that is used by the App Service app to communicate with a storage account.')
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview'= {
  name: managedIdentityName
  location: location
  tags: tags
}

@description('Grant the \'Contributor\' role to the user-assigned managed identity, at the scope of the resource group.')
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(contributorRoleDefinitionId, resourceGroup().id) // Create a GUID based on the role definition ID and scope (resource group ID). This will return the same GUID every time the template is deployed to the same resource group.
  properties: {
    principalType: 'ServicePrincipal'
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', contributorRoleDefinitionId)
    principalId: managedIdentity.properties.principalId
    description: 'Grant the "Contributor" role to the user-assigned managed identity so it can access the storage account.'
  }
}

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: applicationInsightsName
  location: location
  kind: 'web'
  tags: tags
  properties: {
    Application_Type: 'web'
  }
}

Dricks

Om du arbetar med dina kollegor med GitHub eller Azure Repos är det här ett bra tillfälle att skicka en pull-begäran för att integrera dina ändringar i huvudgrenen. Det är en bra idé att skicka pull-begäranden när du har gjort en del refaktoriseringsarbete.