Ćwiczenie — refaktoryzacja pliku Bicep
Po przejrzeniu szablonu ze współpracownikami decydujesz się na refaktoryzację pliku, aby ułatwić im pracę. W tym ćwiczeniu zastosujesz najlepsze rozwiązania poznane w poprzednich lekcjach.
Zadanie
Przejrzyj zapisany wcześniej szablon Bicep. Zastanów się nad poradami, które znasz, jak utworzyć strukturę szablonów. Spróbuj zaktualizować szablon, aby ułatwić współpracownikom zrozumienie.
W następnych sekcjach znajdują się wskazówki dotyczące określonych części szablonu i wskazówki dotyczące elementów, które warto zmienić. Udostępniamy sugerowane rozwiązanie, ale Twój szablon może wyglądać inaczej, co jest całkowicie ok!
Napiwek
Podczas pracy z procesem refaktoryzacji dobrze jest upewnić się, że plik Bicep jest prawidłowy i że nie zostały przypadkowo wprowadzone żadne błędy. Rozszerzenie Bicep dla programu Visual Studio Code pomaga w tym celu. Zwróć uwagę na czerwone lub żółte linie poniżej kodu, ponieważ wskazują one błąd lub ostrzeżenie. Możesz również wyświetlić listę problemów w pliku, wybierając pozycję Wyświetl>problemy.
Aktualizowanie parametrów
Niektóre parametry w szablonie nie są jasne. Rozważmy na przykład następujące parametry:
@allowed([ 'F1' 'D1' 'B1' 'B2' 'B3' 'S1' 'S2' 'S3' 'P1' 'P2' 'P3' 'P4' ]) param skuName string = 'F1' @minValue(1) param skuCapacity int = 1
Do czego służą?
Napiwek
Jeśli masz parametr, który próbujesz zrozumieć, program Visual Studio Code może pomóc. Wybierz i przytrzymaj (lub kliknij prawym przyciskiem myszy) nazwę parametru w dowolnym miejscu w pliku i wybierz pozycję Znajdź wszystkie odwołania.
Czy szablon musi określać listę dozwolonych wartości parametru
skuName
? Jakie zasoby mają wpływ na wybór różnych wartości dla tych parametrów? Czy istnieją lepsze nazwy, które można nadać parametrom?Napiwek
Podczas zmieniania nazw identyfikatorów pamiętaj, aby stale zmieniać ich nazwy we wszystkich częściach szablonu. Jest to szczególnie ważne w przypadku parametrów, zmiennych i zasobów, które odwołujesz się do całego szablonu.
Program Visual Studio Code oferuje wygodny sposób zmiany nazwy symboli: wybierz identyfikator, którego nazwę chcesz zmienić, wybierz F2, wprowadź nową nazwę, a następnie wybierz Enter:
Te kroki powodują zmianę nazwy identyfikatora i automatyczne aktualizowanie wszystkich odwołań do niego.
Parametr
managedIdentityName
nie ma wartości domyślnej. Czy można rozwiązać ten problem lub, jeszcze lepiej, utworzyć nazwę automatycznie w szablonie?Przyjrzyj się definicji parametru
roleDefinitionId
:param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
Dlaczego istnieje wartość domyślna
b24988ac-6180-42a0-ab88-20f7382dd24c
? Co oznacza ten długi identyfikator? Jak ktoś inny wiedziałby, czy użyć wartości domyślnej, czy zastąpić ją? Co można zrobić, aby ulepszyć identyfikator? Czy ma to nawet sens jako parametr?Napiwek
Ten identyfikator to identyfikator definicji roli Współautor dla platformy Azure. Jak można użyć tych informacji, aby ulepszyć szablon?
Kiedy ktoś wdroży szablon, w jaki sposób będzie wiedział, dla którego parametru jest dany parametr? Czy możesz dodać opisy, aby pomóc użytkownikom szablonu?
Dodawanie zestawu konfiguracji
Rozmawiasz ze współpracownikami i decydujesz się na użycie określonych jednostek SKU dla każdego zasobu, w zależności od wdrażanego środowiska. Decydujesz się na te jednostki SKU dla każdego z zasobów:
Zasób Jednostka SKU dla środowiska produkcyjnego Jednostka SKU dla nieprodukcyjnej Plan usługi App Service S1, dwa wystąpienia F1, jedno wystąpienie Konto magazynu GRS LRS Baza danych SQL S1 Podstawowy Czy można użyć zestawu konfiguracji, aby uprościć definicje parametrów?
Aktualizowanie nazw symbolicznych
Przyjrzyj się nazwam symbolicznym zasobów w szablonie. Co można zrobić, aby je poprawić?
Szablon Bicep zawiera zasoby z różnymi stylami wielkich liter dla ich nazw symbolicznych, takich jak:
storageAccount
iwebSite
, które używają wielkiej litery camelCase.roleassignment
isqlserver
, które używają płaskiej wielkości liter.sqlserverName_databaseName
iAppInsights_webSiteName
, które używają wielkości liter węża.
Czy można je naprawić, aby używać jednego stylu spójnie?
Spójrz na ten zasób przypisania roli:
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 } }
Czy nazwa symboliczna jest wystarczająco opisowa, aby pomóc komuś innemu pracować z tym szablonem?
Napiwek
Przyczyną, dla którego tożsamość wymaga przypisania roli, jest to, że aplikacja internetowa używa swojej tożsamości zarządzanej do nawiązywania połączenia z serwerem bazy danych. Czy pomaga to wyjaśnić w szablonie?
Kilka zasobów ma symboliczne nazwy, które nie odzwierciedlają bieżących nazw zasobów platformy Azure:
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' = { // ... }
Tożsamości zarządzane używane do wywoływaniu msI, planów usługi App Service używanych do wywoływanych planów hostingu i aplikacji usługi App Service używanych do wywoływanych witryn internetowych.
Czy można je zaktualizować do najnowszych nazw, aby uniknąć nieporozumień w przyszłości?
Uproszczenie definicji kontenera obiektów blob
Zobacz, jak są zdefiniowane kontenery obiektów blob:
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}' }
Jeden z nich używa
parent
właściwości , a drugi nie. Czy można rozwiązać te problemy, aby były spójne?Nazwy kontenerów obiektów blob nie zmienią się między środowiskami. Czy uważasz, że nazwy muszą być określone przy użyciu parametrów?
Istnieją dwa kontenery obiektów blob. Czy można je wdrożyć przy użyciu pętli?
Aktualizowanie nazw zasobów
Istnieją pewne parametry, które jawnie ustawiają nazwy zasobów:
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'
Czy jest inny sposób, w jaki można to zrobić?
Uwaga
Pamiętaj, że nie można zmienić nazwy zasobów po ich wdrożeniu. Podczas modyfikowania szablonów, które są już używane, należy zachować ostrożność podczas zmiany sposobu tworzenia nazw zasobów przez szablon. Jeśli szablon zostanie wdrożony ponownie, a zasób ma nową nazwę, platforma Azure utworzy kolejny zasób. Może nawet usunąć stary zasób, jeśli wdrożysz go w trybie ukończonym .
Nie musisz się tym martwić, ponieważ jest to tylko przykład.
Nazwa zasobu serwera logicznego SQL jest ustawiana przy użyciu zmiennej, mimo że potrzebuje globalnie unikatowej nazwy:
var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
Jak można to poprawić?
Aktualizowanie zależności i zasobów podrzędnych
Oto jeden z zasobów, który zawiera
dependsOn
właściwość. Czy to naprawdę potrzebuje?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 ] }
Zwróć uwagę, jak te zasoby podrzędne są deklarowane w szablonie:
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 ] }
Jak można zmodyfikować sposób deklarowanego tych zasobów? Czy istnieją inne zasoby w szablonie, które również powinny zostać zaktualizowane?
Aktualizowanie wartości właściwości
Przyjrzyj się właściwościom zasobu bazy danych SQL:
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 } }
Czy ma to sens, aby trwale zakodować wartość właściwości jednostki SKU
name
? A co to są te dziwne wartości dlacollation
właściwości imaxSizeBytes
?Napiwek
Właściwości
collation
imaxSizeBytes
są ustawione na wartości domyślne. Jeśli nie określisz wartości samodzielnie, zostaną użyte wartości domyślne. Czy to pomaga zdecydować, co z nimi zrobić?Czy można zmienić sposób ustawiania parametry połączenia magazynu tak, aby wyrażenie złożone nie było zdefiniowane w tekście z zasobem?
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}': {} } } }
Kolejność elementów
Czy jesteś zadowolony z kolejności elementów w pliku? Jak można poprawić czytelność pliku, przenosząc elementy wokół?
Przyjrzyj się zmiennej
databaseName
. Czy należy on do miejsca, w którym jest teraz?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 } }
Czy zauważono zasób z komentarzem?
webSiteConnectionStrings
Czy uważasz, że musi znajdować się w pliku?
Dodawanie komentarzy, tagów i innych metadanych
Zastanów się nad wszystkimi elementami w szablonie, które mogą nie być oczywiste lub które wymagają dodatkowego wyjaśnienia. Czy możesz dodać komentarze, aby ułatwić innym osobom otwarcie pliku w przyszłości?
Przyjrzyj
webSite
się właściwości zasobuidentity
: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}': {} } } }
Ta składnia jest dziwna, prawda? Czy uważasz, że wymaga to komentarza, aby go wyjaśnić?
Spójrz na zasób przypisania roli:
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 } }
Nazwa zasobu używa
guid()
funkcji . Czy pomogłoby to wyjaśnić, dlaczego?Czy można dodać opis do przypisania roli?
Czy można dodać zestaw tagów do każdego zasobu?
Sugerowane rozwiązanie
Oto przykład refaktoryzacji szablonu. Szablon może nie wyglądać dokładnie tak, ponieważ twój styl może być inny.
@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'
}
}
Napiwek
Jeśli pracujesz ze współpracownikami przy użyciu repozytoriów GitHub lub Azure Repos, warto przesłać żądanie ściągnięcia w celu zintegrowania zmian z gałęzią główną. Dobrym pomysłem jest przesyłanie żądań ściągnięcia po wykonaniu refaktoryzacji.