Exercice – Refactoriser votre fichier Bicep
Après avez passé en revue votre modèle avec vos collègues, vous décidez de refactoriser le fichier pour leur en faciliter l’utilisation. Dans cet exercice, vous allez appliquer les bonnes pratiques que vous avez apprises dans les unités précédentes.
Votre tâche
Passez en revue le modèle Bicep que vous avez enregistré précédemment. Rappelez-vous du conseil que vous avez lu concernant la structuration des modèles. Essayez de mettre à jour votre modèle pour permettre à vos collègues de mieux le comprendre.
Dans les sections suivantes, vous trouverez des éléments d’information sur certaines parties du modèle ainsi que des indications sur ce que vous pouvez modifier. Nous proposons une solution suggérée, mais votre modèle peut se présenter différemment, ce qui est tout à fait concevable !
Conseil
À mesure que vous progresserez dans le processus de refactorisation, vous avez tout intérêt à vérifier que votre fichier Bicep est valide et que vous n’avez pas introduit d’erreurs par inadvertance. À cet effet, l’extension Bicep pour Visual Studio Code peut vous être utile. La présence de lignes ondulées rouges ou jaunes en dessous de votre code indique la présence d’une erreur ou d’un avertissement. Vous pouvez également examiner la liste des problèmes dans votre fichier en sélectionnant Affichage>Problèmes.
Mettre à jour les paramètres
Certains paramètres de votre modèle ne sont pas clairs. Par exemple, examinez les paramètres suivants :
@allowed([ 'F1' 'D1' 'B1' 'B2' 'B3' 'S1' 'S2' 'S3' 'P1' 'P2' 'P3' 'P4' ]) param skuName string = 'F1' @minValue(1) param skuCapacity int = 1
À quoi servent-ils ?
Conseil
Si vous ne comprenez pas la fonction d’un paramètre, Visual Studio Code peut vous aider. Sélectionnez un nom de paramètre en maintenant le bouton enfoncé (ou cliquez dessus avec le bouton droit) dans m’importe quel emplacement de votre fichier, puis sélectionnez Rechercher toutes les références.
Le modèle doit-il indiquer la liste des valeurs autorisées pour le paramètre
skuName
? Quelles ressources sont affectées par le choix de valeurs différentes pour ces paramètres ? Pourriez-vous donner de meilleurs noms aux paramètres ?Conseil
Quand vous renommez des identificateurs, veillez à le faire de façon cohérente dans toutes les parties de votre modèle. Ceci est particulièrement important pour les paramètres, les variables et les ressources auxquels vous faites référence dans votre modèle.
Visual Studio Code offre un moyen pratique de renommer les symboles : sélectionnez l’identificateur que vous souhaitez renommer, appuyez sur F2, entrez un nouveau nom, puis appuyez sur Entrée :
Ces étapes renomment l’identificateur et mettent automatiquement à jour toutes les références à ce dernier.
Le paramètre
managedIdentityName
n’a pas de valeur par défaut. Pourriez-vous corriger cela ou, mieux encore, pourriez-vous créer le nom automatiquement dans le modèle ?Examinez la définition du paramètre
roleDefinitionId
:param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
Pourquoi la valeur par défaut
b24988ac-6180-42a0-ab88-20f7382dd24c
existe-t-elle ? Que signifie la présence de cet identificateur long ? Comment un utilisateur sait-il si la valeur par défaut doit être utilisée ou si elle doit être remplacée ? Que pourriez-vous faire pour améliorer l’identificateur ? Est-il même utile de faire de cela un paramètre ?Conseil
Cet identificateur est l’ID de définition du rôle Contributeur pour Azure. Comment pouvez-vous utiliser ces informations pour améliorer le modèle ?
Quand le modèle sera déployé, comment les utilisateurs sauront-ils à quoi sert chaque paramètre ? Pouvez-vous ajouter des descriptions pour aider les utilisateurs de votre modèle ?
Ajouter un jeu de configuration
Suite à la discussion que vous avez eue avec vos collègues, vous décidez d’utiliser des références SKU spécifiques pour chaque ressource, en fonction de l’environnement déployé. Voici les références SKU que vous choisissez pour chacune de vos ressources :
Ressource Référence SKU pour la production Référence SKU pour la non-production Plan App Service S1, deux instances F1, une instance Compte de stockage GRS LRS Base de données SQL S1 De base Pouvez-vous utiliser un jeu de configuration pour simplifier les définitions de paramètres ?
Mettre à jour les noms symboliques
Examinez les noms symboliques des ressources présentes dans le modèle. Que pourriez-vous faire pour les améliorer ?
Votre modèle Bicep contient des ressources dont les noms symboliques utilisent divers styles typographiques, à savoir :
storageAccount
etwebSite
, qui utilisent les règles typographiques « camelCase ».roleassignment
etsqlserver
, qui utilisent les règles typographiques « flat case ».sqlserverName_databaseName
etAppInsights_webSiteName
, qui utilisent les règles typographiques « snake case ».
Pouvez-vous les corriger de façon à utiliser un seul et même style ?
Examinez cette ressource d’attribution de rôle :
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 } }
Le nom symbolique est-il suffisamment descriptif pour faciliter l’utilisation de ce modèle ?
Conseil
Si l’identité a besoin d’une attribution de rôle, c’est parce que l’application web utilise son identité managée pour se connecter au serveur de base de données. Cela vous aide-t-il à clarifier cela dans le modèle ?
Certaines ressources ont des noms symboliques qui ne reflètent pas les noms actuels de ressources 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' = { // ... }
Auparavant, les identités managées étaient appelées MSI, les plans App Service étaient des plans d’hébergement et les applications App Service étaient des sites web.
Pour éviter toute confusion à l’avenir, pouvez-vous les mettre à jour en leur donnant les noms les plus récents ?
Simplifier les définitions de conteneurs d’objets blob
Examinez la façon dont les conteneurs d’objets blob sont définis :
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}' }
L’un d’entre eux utilise la propriété
parent
, mais pas l’autre. Pouvez-vous les harmoniser ?Les noms de conteneurs blob ne changent pas d’un environnement à l’autre. Pensez-vous que les noms doivent être spécifiés à l’aide de paramètres ?
Il existe deux conteneurs d’objets blob. Pourraient-ils être déployés en utilisant une boucle ?
Mettre à jour les noms de ressources
Certains paramètres définissent explicitement les noms de ressources :
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'
Existe-t-il une autre façon de procéder ?
Attention
N’oubliez pas que les ressources ne peuvent pas être renommées une fois déployées. Quand vous modifiez un modèle qui est déjà utilisé, faites attention lorsque vous changez la façon dont il crée les noms de ressources. Si le modèle est redéployé et que la ressource a un nouveau nom, Azure créera une autre ressource. L’ancienne ressource risque même d’être supprimée si vous la déployez en mode Complet.
Il est inutile de s’en inquiéter ici, car il ne s’agit que d’un exemple.
Le nom de ressource de votre serveur logique SQL est défini à l’aide d’une variable, même s’il a besoin d’un nom global unique :
var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
Comment pourriez-vous améliorer cela ?
Mettre à jour les dépendances et les ressources enfants
Voici une de vos ressources, qui comprend une propriété
dependsOn
. En a-t-elle vraiment besoin ?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 ] }
Remarquez comment ces ressources enfants sont déclarées dans votre modèle :
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 ] }
Comment pourriez-vous modifier la façon dont ces ressources sont déclarées ? Y a-t-il d’autres ressources dans le modèle qui doivent aussi être mises à jour ?
Mettre à jour les valeurs des propriétés
Examinez les propriétés de la ressource de base de données 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 } }
Est-il judicieux de coder de manière irréversible la valeur de la propriété
name
de la référence SKU ? Et quelles sont ces valeurs inhabituelles pour les propriétéscollation
etmaxSizeBytes
?Conseil
Les propriétés
collation
etmaxSizeBytes
sont définies avec les valeurs par défaut. Si vous ne spécifiez pas vous-même les valeurs, les valeurs par défaut sont utilisées. Cela vous aide-t-il à prendre une décision les concernant ?Pouvez-vous changer la façon dont la chaîne de connexion de stockage est définie de sorte que l’expression complexe ne soit pas définie en fonction de la ressource ?
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}': {} } } }
Ordre des éléments
Êtes-vous satisfait de l’ordre des éléments dans le fichier ? Comment pourriez-vous améliorer la lisibilité du fichier en déplaçant des éléments ?
Examinez la variable
databaseName
. Est-elle à sa place ?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 } }
Avez-vous repéré la ressource commentée,
webSiteConnectionStrings
? Selon vous, est-il nécessaire que cela figure dans le fichier ?
Ajouter des commentaires, des étiquettes et d’autres métadonnées
Réfléchissez aux éléments du modèle qui ne sont pas forcément évidents et qui auraient besoin d’être davantage expliqués. Pouvez-vous ajouter des commentaires pour permettre aux personnes qui ouvriront le fichier de mieux les comprendre ?
Examinez la propriété
identity
de la ressourcewebSite
: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}': {} } } }
Cette syntaxe vous interpelle, n’est-ce pas ? Vous pensez qu’il est nécessaire de l’expliquer au moyen d’un commentaire ?
Examinez la ressource d’attribution de rôle :
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 } }
Le nom de la ressource utilise la fonction
guid()
. Serait-il utile de l’expliquer ? Pourquoi ?Pouvez-vous ajouter une description à l’attribution de rôle ?
Pouvez-vous ajouter un ensemble d’étiquettes à chaque ressource ?
Solution suggérée
Voici par exemple comment vous pourriez refactoriser le modèle. Il est possible que votre modèle n’ait pas exactement le même aspect, peut-être parce que votre style est différent.
@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'
}
}
Conseil
Si vous travaillez avec vos collègues en utilisant GitHub ou Azure Repos, ce serait le moment idéal de soumettre une demande de tirage (pull request) pour intégrer vos modifications dans la branche primaire. Il est judicieux de soumettre des demandes de tirage (pull request) de tirage après une opération de refactorisation.