演習 - Bicep ファイルをリファクターする
あなたは同僚と一緒にテンプレートを確認した後、彼らがファイルを簡単に使用できるようにリファクターすることにしました。 この演習では、前のユニットで学んだベスト プラクティスを適用します。
タスク
前に保存した Bicep テンプレートを確認します。 テンプレートを構築する方法を説明したアドバイスについて考えてみましょう。 同僚が理解しやすいようにテンプレートを更新します。
この後のセクションでは、テンプレートの特定の部分へのポインターと、変更が必要な点に関するヒントをいくつか紹介します。 推奨されるソリューションを示しますが、ご自分のテンプレートは見た目が異なる場合があります。それは問題ありません。
ヒント
リファクタリング プロセスを進めながら、Bicep ファイルが有効であり、誤ってエラーが発生していないことを確認することをお勧めします。 これには Visual Studio Code の Bicep 拡張機能が役立ちます。 コードの下に赤や黄色の波線が表示されていたら、エラーや警告を示していますのでご注意ください。 また、[表示]>[問題]を選択して、ファイル内の問題の一覧を表示することもできます。
パラメーターを更新する
テンプレートの一部のパラメーターが明確ではありません。 たとえば、次のようなパラメーターを考えてみます。
@allowed([ 'F1' 'D1' 'B1' 'B2' 'B3' 'S1' 'S2' 'S3' 'P1' 'P2' 'P3' 'P4' ]) param skuName string = 'F1' @minValue(1) param skuCapacity int = 1
これらは何のために使用されていますか。
ヒント
理解しようとしているパラメーターがある場合は、Visual Studio Code が役に立ちます。 ファイル内の任意の場所でパラメーター名を選んで保持 (または右クリック) し、[すべての参照の検索] を選びます。
テンプレートで
skuName
パラメーターに使用できる値の一覧を指定する必要がありますか。 これらのパラメーターに異なる値を選択することによって影響を受けるリソースは何ですか。 パラメーターに付けることができる、より適切な名前はありますか。ヒント
識別子の名前を変更するときは、テンプレートのすべての部分で一貫して名前を変更してください。 これは、テンプレート全体で参照するパラメーター、変数、およびリソースの場合に特に重要です。
Visual Studio Code には、シンボルの名前を変更する便利な方法が用意されています。名前を変更する識別子を選択し、F2 キーを押して新しい名前を入力したら、Enter キーを押します。
これらのステップで、識別子の名前が変更され、それに対するすべての参照が自動的に更新されます。
managedIdentityName
パラメーターには既定値がありません。 これを修正できますか。または、より望ましい方法として、テンプレート内で自動的に名前を作成できますか。roleDefinitionId
パラメーター定義を確認します。param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
既定値が
b24988ac-6180-42a0-ab88-20f7382dd24c
になっているのはなぜですか。 その長い識別子は何を意味するのでしょうか。 既定値を使用するかオーバーライドするかを他のユーザーはどのようにして知ることができますか。 識別子を改善するにはどうすればよいでしょうか。 これをパラメーターとして使用することは適切ですか。ヒント
その識別子は、Azure の "共同作成者" ロールの定義 ID です。 その情報を使用してテンプレートを改善するにはどうすればよいですか。
だれかがテンプレートをデプロイするとき、各パラメーターの目的はどのようにしてわかりますか。 テンプレートのユーザーに役立つ説明を追加できますか。
構成セットを追加する
あなたは同僚と話し、デプロイする環境に応じて、リソースごとに特定の SKU を使用することを決定しました。 これらの SKU は、リソースごとに決定します。
リソース 運用環境の SKU 非運用環境の SKU App Service プラン S1、2 インスタンス F1、1 インスタンス ストレージ アカウント GRS LRS SQL データベース S1 Basic 構成セットを使用してパラメーター定義を簡略化できますか。
シンボリック名を更新する
テンプレート内のリソースのシンボリック名を見てみましょう。 改善のために何ができるでしょうか。
Bicep テンプレートには、次のような、シンボリック名に大文字と小文字のさまざまなスタイルを持つリソースが含まれています。
- camelCase の大文字化を使用する
storageAccount
とwebSite
。 - フラット ケースの大文字化を使用する
roleassignment
とsqlserver
。 - スネーク ケースの大文字化を使用する
sqlserverName_databaseName
とAppInsights_webSiteName
。
これらを 1 つのスタイルを一貫して使用するように修正できますか。
- camelCase の大文字化を使用する
このロールの割り当てリソースを確認します。
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 } }
シンボリック名は、他のユーザーがこのテンプレートを使用できるようにするのに十分な説明ですか。
ヒント
ID にロールの割り当てが必要な理由は、Web アプリからデータベース サーバーへの接続にマネージド ID が使用されることです。 テンプレートでこれを明確にすることはできますか。
いくつかのリソースには、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' = { // ... }
マネージド ID は、以前は MSI と呼ばれていました。App Service プランは、以前は "ホスティング プラン"、App Service アプリは以前 "Web サイト" と呼ばれていました。
将来の混乱を避けるために、これらを最新の名前に更新することはできますか?
BLOB コンテナーの定義を簡略化する
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}' }
そのうちの 1 つは
parent
プロパティを使用し、もう一方は使用していません。 これらを修正して一貫性を確保できますか。BLOB コンテナーの名前は環境によって変わりません。 パラメーターを使用して名前を指定する必要があると考えられますか。
BLOB コンテナーが 2 つあります。 ループを使用してそれらをデプロイできますか?
リソース名を更新する
リソース名を明示的に設定するパラメーターがいくつかあります。
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'
これを行う別の方法はありますか。
注意
デプロイ後にリソースの名前を変更できないことに注意してください。 既に使用されているテンプレートを変更する場合、テンプレートでのリソース名の作成方法を変更するときに注意が必要です。 テンプレートを再デプロイした場合、リソースに新しい名前が指定されていると、Azure によって別のリソースが作成されます。 "完全" モードでデプロイした場合は、古いリソースが削除されることもあります。
ここでは、これについて心配する必要はありません。これは単なる例です。
SQL 論理サーバーのリソース名は、グローバルに一意の名前を必要としますが、変数を使用して設定されます。
var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
これを改善するにはどうすればよいでしょうか。
依存関係と子リソースを更新する
次にリソースの 1 つを示します。これには
dependsOn
プロパティが含まれています。 それは本当に必要でしょうか。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 ] }
これらの子リソースがテンプレートでどのように宣言されているかに注目してください。
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 ] }
これらのリソースの宣言方法を変更するにはどうすればよいですか。 更新する必要があるその他のリソースがテンプレートにありますか。
プロパティ値を更新する
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 } }
SKU の
name
プロパティ値をハードコーディングすることは適切ですか。 また、collation
およびmaxSizeBytes
プロパティの奇妙に見える値は何でしょうか。ヒント
collation
およびmaxSizeBytes
プロパティは既定値に設定されています。 値を自分で指定しない場合は、既定値が使用されます。 これは、どのような処理を行うかを決定するのに役立ちますか。リソースで複合式がインラインで定義されないように、ストレージ接続文字列の設定方法を変更できますか。
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}': {} } } }
要素の順序
ファイル内の要素の順序に満足していますか。 要素を移動して、ファイルの読みやすさを向上させるにはどうすればよいですか?
databaseName
変数を見てみましょう。 それは適切な場所に配置されていますか。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 } }
コメントアウトされたリソース
webSiteConnectionStrings
に気付きましたか。 それはファイル内に存在する必要があると思いますか。
コメント、タグ、その他のメタデータを追加する
テンプレートで、明確でない可能性があるものや、追加の説明が必要なものがないか考えます。 将来ファイルを開く可能性がある他のユーザーに対して、コメントを追加してわかりやすくすることができますか。
webSite
リソースのidentity
プロパティを見てみましょう。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}': {} } } }
その構文はおかしくないですか。 これについて説明するためにコメントが必要だと思いますか。
ロールの割り当てリソースを確認します。
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 } }
リソース名は、
guid()
関数を使います。 理由を説明すると役に立ちますか。ロールの割り当てに説明を追加できますか。
各リソースにタグのセットを追加できますか。
推奨されている解決方法
テンプレートをリファクターする方法の例を次に示します。 テンプレートは、スタイルが異なる可能性があるため、正確にこのとおりには表示されない場合があります。
@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'
}
}
ヒント
GitHub または Azure Repos を使用して同僚と共同作業している場合、この時点で "プル要求" を送信して変更をメイン ブランチに統合できます。 リファクタリング作業を行った後で、pull request を送信することをお勧めします。