Exercício – Refatorar o arquivo Bicep
Depois de examinar o modelo com seus colegas, você decide refatorar o arquivo para facilitar o uso dele. Neste exercício, você aplicará as melhores práticas aprendidas nas unidades anteriores.
Sua tarefa
Examine o modelo Bicep salvo anteriormente. Pense na recomendação que você leu sobre como estruturar os modelos. Tente atualizar seu modelo para facilitar a compreensão dos seus colegas.
Nas próximas seções, haverá alguns ponteiros para partes específicas do modelo e algumas dicas sobre os itens cuja alteração pode ser útil. Forneceremos uma solução sugerida, mas seu modelo poderá parecer diferente, o que é perfeitamente aceitável.
Dica
À medida que você realiza o processo de refatoração, é bom garantir que o arquivo Bicep seja válido e que você não tenha introduzido nenhum erro acidentalmente. A extensão do Bicep para Visual Studio Code ajuda nessa tarefa. Fique atento a qualquer linha ondulada vermelha ou amarela abaixo do código, pois ela indica um erro ou um aviso. Veja também uma lista dos problemas do arquivo selecionando Exibir>Problemas.
Atualizar os parâmetros
Alguns parâmetros do modelo não estão claros. Por exemplo, considere estes parâmetros:
@allowed([ 'F1' 'D1' 'B1' 'B2' 'B3' 'S1' 'S2' 'S3' 'P1' 'P2' 'P3' 'P4' ]) param skuName string = 'F1' @minValue(1) param skuCapacity int = 1
Para que eles são usados?
Dica
Se você tiver um parâmetro que está tentando entender, o Visual Studio Code poderá ser útil. Selecione e mantenha pressionado (ou clique com o botão direito do mouse) um nome de parâmetro em qualquer lugar do arquivo e selecione Localizar todas as referências.
O modelo precisa especificar a lista de valores permitidos para o parâmetro
skuName
? Quais recursos são afetados pela escolha de valores diferentes para esses parâmetros? Há nomes melhores que você pode dar aos parâmetros?Dica
Ao renomear os identificadores, não se esqueça de renomeá-los de maneira consistente em todas as partes do modelo. Isso é especialmente importante para os parâmetros, as variáveis e os recursos que são referenciados no modelo.
O Visual Studio Code oferece uma forma conveniente de renomear os símbolos: escolha o identificador que deseja renomear, selecione F2, insira um novo nome e selecione ENTER:
Essas etapas renomeiam o identificador e atualizam automaticamente todas as referências a ele.
O parâmetro
managedIdentityName
não tem um valor padrão. Você pode corrigir isso ou, melhor ainda, criar o nome automaticamente dentro do modelo?Examine a definição de parâmetro
roleDefinitionId
:param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
Por que há um valor padrão igual a
b24988ac-6180-42a0-ab88-20f7382dd24c
? O que o identificador longo significa? Como alguém saberá se o valor padrão deve ser usado ou substituído? O que você pode fazer para aprimorar o identificador? Ainda faz sentido ter isso como parâmetro?Dica
Esse identificador é a ID da definição de função Colaborador do Azure. Como você pode usar essas informações para aprimorar o modelo?
Quando alguém implantar o modelo, como ele saberá qual é a função de cada parâmetro? Você pode adicionar algumas descrições para ajudar os usuários do modelo?
Adicionar um conjunto de configuração
Você conversa com seus colegas e decide usar SKUs específicos para cada recurso, dependendo do ambiente que está sendo implantado. Você decide usar estes SKUs para cada um dos recursos:
Recurso SKU para produção SKU para não produção Plano do Serviço de Aplicativo S1, duas instâncias F1, uma instância Conta de armazenamento GRS LRS Banco de dados SQL S1 Basic É possível usar um conjunto de configuração para simplificar as definições de parâmetros?
Atualizar os nomes simbólicos
Dê uma olhada nos nomes simbólicos dos recursos no modelo. O que você pode fazer para aprimorá-los?
O modelo Bicep contém recursos com uma variedade de estilos de uso de maiúsculas para os nomes simbólicos, como:
storageAccount
ewebSite
, que usam o estilo camelCase.roleassignment
esqlserver
, que usam o estilo Flat Case.sqlserverName_databaseName
eAppInsights_webSiteName
, que usam o estilo Snake Case.
É possível corrigir esses erros para usar um estilo de maneira consistente?
Veja este recurso de atribuição de função:
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 } }
O nome simbólico é descritivo o suficiente para ajudar outra pessoa a trabalhar com esse modelo?
Dica
O motivo pelo qual a identidade precisa de uma atribuição de função é que o aplicativo Web usa a identidade gerenciada dela para se conectar ao servidor de banco de dados. Isso ajuda você a esclarecer isso no modelo?
Alguns recursos têm nomes simbólicos que não refletem os nomes atuais dos recursos do 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' = { // ... }
As identidades gerenciadas costumavam ser chamadas de MSIs, os Planos do Serviço de Aplicativo costumavam ser chamados de planos de hospedagem, e os aplicativos do Serviço de Aplicativo costumavam ser chamados de sites.
Você pode atualizá-los para os nomes mais recentes para evitar confusão no futuro?
Simplificar as definições de contêiner de blobs
Veja como os contêineres de blobs são definidos:
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}' }
Um deles usa a propriedade
parent
e o outro não. É possível corrigir isso para que ele seja consistente?Os nomes de contêiner de blob não serão alterados entre ambientes. Você acha que os nomes precisam ser especificados por meio de parâmetros?
Há dois contêineres de blobs. Eles podem ser implantados por meio de um loop?
Atualizar os nomes de recursos
Há alguns parâmetros que configuram explicitamente os nomes de recursos:
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'
Há outra maneira de fazer isso?
Cuidado
Lembre-se de que os recursos não podem ser renomeados depois de implantados. Ao modificar os modelos que já estão em uso, tenha cuidado ao alterar a maneira como o modelo cria nomes de recursos. Se o modelo for reimplantado e o recurso tiver um novo nome, o Azure criará outro recurso. Ele poderá, até mesmo, excluir o recurso antigo se você implantá-lo no modo Completo.
Você não precisa se preocupar com isso aqui, porque ele é apenas um exemplo.
O nome de recurso do servidor lógico SQL é definido por meio de uma variável, mesmo que ele precise de um nome globalmente exclusivo:
var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
Como você pode aprimorar isso?
Atualizar as dependências e os recursos filho
Aqui está um dos seus recursos, que inclui uma propriedade
dependsOn
. Ele realmente precisa dela?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 ] }
Observe como estes recursos filho são declarados no modelo:
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 ] }
Como você pode modificar a forma como esses recursos são declarados? Há outros recursos no modelo que também devem ser atualizados?
Atualizar os valores das propriedades
Dê uma olhada nas propriedades de recurso do banco de dados 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 } }
Faz sentido em embutir em código o valor da propriedade
name
do SKU? E quais são estes valores estranhos para as propriedadescollation
emaxSizeBytes
?Dica
As propriedades
collation
emaxSizeBytes
são definidas com os valores padrão. Se você não especificar os valores por conta própria, os valores padrão serão usados. Isso ajuda você a decidir o que fazer com eles?É possível alterar a maneira como a cadeia de conexão de armazenamento é definida para que a expressão complexa não seja definida em linha com o recurso?
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}': {} } } }
Ordem dos elementos
Você está satisfeito com a ordem dos elementos no arquivo? Como você pode melhorar a legibilidade do arquivo movendo os elementos?
Dê uma olhada na variável
databaseName
. Ela pertence ao local em que está agora?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 } }
Você percebeu o recurso removido da marca de comentário,
webSiteConnectionStrings
? Você acha que ele precisa estar no arquivo?
Adicionar comentários, marcas e outros metadados
Pense em qualquer coisa no modelo que possa não ser óbvia ou que precise de explicações adicionais. É possível adicionar comentários para deixar tudo mais claro para outras pessoas que possam abrir o arquivo no futuro?
Dê uma olhada na propriedade
identity
do recursowebSite
: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}': {} } } }
Essa sintaxe é estranha, não é? Você acha que isso precisa de um comentário para ajudar a explicá-la?
Veja o recurso de atribuição de função:
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 } }
O nome do recurso usa a função
guid()
. É útil explicar o motivo?É possível adicionar uma descrição à atribuição de função?
É possível adicionar um conjunto de marcas a cada recurso?
Solução sugerida
Este é um exemplo de como você pode refatorar o modelo. Seu modelo pode não ser exatamente assim, porque seu estilo pode ser diferente.
@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'
}
}
Dica
Se você estiver trabalhando com seus colegas usando o GitHub ou o Azure Repos, esse será um ótimo momento para enviar uma solicitação de pull para integrar suas alterações à ramificação principal. É uma boa ideia enviar solicitações de pull depois de fazer uma parte do trabalho de refatoração.