Exercício - Refatore seu arquivo Bicep
Depois de revisar seu modelo com seus colegas, você decide refatorar o arquivo para facilitar o trabalho com eles. Neste exercício, você aplica as melhores práticas aprendidas nas unidades anteriores.
A sua tarefa
Revise o modelo Bicep que você salvou anteriormente. Pense nos conselhos que leu sobre como estruturar os seus modelos. Tente atualizar seu modelo para facilitar a compreensão de seus colegas.
Nas próximas seções, há algumas dicas para partes específicas do modelo e algumas dicas sobre coisas que você pode querer alterar. Nós fornecemos uma solução sugerida, mas seu modelo pode parecer diferente, o que é perfeitamente OK!
Gorjeta
À medida que você trabalha no processo de refatoração, é bom garantir que seu arquivo Bicep seja válido e que você não tenha introduzido erros acidentalmente. A extensão Bicep para Visual Studio Code ajuda com isso. Cuidado com quaisquer linhas onduladas vermelhas ou amarelas abaixo do seu código, porque elas indicam um erro ou um aviso. Você também pode exibir uma lista dos problemas em seu arquivo selecionando Exibir>problemas.
Atualizar os parâmetros
Alguns parâmetros no seu 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 são utilizados?
Gorjeta
Se você tiver um parâmetro que está tentando entender, o Visual Studio Code pode ajudar. Selecione e segure (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
skuName
parâmetro? Quais recursos são afetados pela escolha de valores diferentes para esses parâmetros? Existem nomes melhores que você pode dar os parâmetros?Gorjeta
Ao renomear identificadores, certifique-se de renomeá-los consistentemente em todas as partes do modelo. Isso é especialmente importante para parâmetros, variáveis e recursos aos quais você se refere em todo o modelo.
O Visual Studio Code oferece uma maneira conveniente de renomear símbolos: selecione o identificador que você 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
managedIdentityName
parâmetro não tem um valor padrão. Você poderia corrigir isso ou, melhor ainda, criar o nome automaticamente dentro do modelo?Veja a definição do
roleDefinitionId
parâmetro:param roleDefinitionId string = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
Por que há um valor padrão de
b24988ac-6180-42a0-ab88-20f7382dd24c
? O que significa esse identificador longo? Como alguém saberia se deveria usar o valor padrão ou substituí-lo? O que você poderia fazer para melhorar o identificador? Faz mesmo sentido ter isso como parâmetro?Gorjeta
Esse identificador é a ID de definição de função de Colaborador para o Azure. Como você pode usar essas informações para melhorar o modelo?
Quando alguém implanta o modelo, como saberá para que serve cada parâmetro? Você pode adicionar algumas descrições para ajudar os usuários do seu modelo?
Adicionar um conjunto de configurações
Você fala com seus colegas e decide usar SKUs específicas para cada recurso, dependendo do ambiente que está sendo implantado. Você decide sobre estes SKUs para cada um dos seus recursos:
Recurso SKU para produção SKU para não produção Plano do Serviço de Aplicações S1, duas instâncias F1, uma instância Conta de armazenamento GRS LRS Base de dados SQL S1 Básica Você pode usar um conjunto de configurações para simplificar as definições de parâmetro?
Atualizar os nomes simbólicos
Dê uma olhada nos nomes simbólicos para os recursos no modelo. O que você poderia fazer para melhorá-los?
Seu modelo Bicep contém recursos com uma variedade de estilos de maiúsculas para seus nomes simbólicos, como:
storageAccount
ewebSite
, que usam maiúsculas camelCase.roleassignment
esqlserver
, que usam maiúsculas e minúsculas simples.sqlserverName_databaseName
eAppInsights_webSiteName
, que usam maiúsculas e minúsculas.
Você pode corrigi-los para usar um estilo de forma 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 este modelo?
Gorjeta
A razão pela qual a identidade precisa de uma atribuição de função é que o aplicativo Web usa sua identidade gerenciada 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?
Simplifique as definições do contêiner de blob
Veja como os contêineres de blob 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 o
parent
imóvel e o outro não. Você pode corrigi-los para ser consistente?Os nomes dos contêineres de blob não serão alterados entre ambientes. Você acha que os nomes precisam ser especificados usando parâmetros?
Existem dois recipientes de blob. Eles poderiam ser implantados usando um loop?
Atualizar os nomes dos recursos
Existem alguns parâmetros que definem explicitamente os nomes dos 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'
Existe outra maneira de fazer isso?
Atenção
Lembre-se de que os recursos não podem ser renomeados depois de implantados. Ao modificar 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 pode até excluir o recurso antigo se você implantá-lo no modo Completo .
Você não precisa se preocupar com isso aqui, porque é apenas um exemplo.
O nome do recurso do servidor lógico SQL é definido usando uma variável, mesmo que ele precise de um nome globalmente exclusivo:
var sqlserverName = 'toywebsite${uniqueString(resourceGroup().id)}'
Como você poderia melhorar isso?
Atualizar dependências e recursos filho
Aqui está um dos seus recursos, que inclui uma
dependsOn
propriedade. Será que ele realmente precisa disso?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 esses recursos filho são declarados em seu 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 modificar a forma como esses recursos são declarados? Existem outros recursos no modelo que também devem ser atualizados?
Atualizar valores de propriedade
Dê uma olhada nas propriedades do 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 codificar o valor da propriedade do
name
SKU? E quais são esses valores estranhos para ascollation
emaxSizeBytes
propriedades?Gorjeta
As
collation
propriedades emaxSizeBytes
são definidas com os valores padrão. Se você mesmo não especificar os valores, os valores padrão serão usados. Isso ajuda-o a decidir o que fazer com eles?Você pode 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
Está satisfeito com a ordem dos elementos no ficheiro? Como você poderia melhorar a legibilidade do arquivo movendo os elementos?
Dê uma olhada na
databaseName
variável. Pertence onde 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 } }
Reparou no recurso comentado,
webSiteConnectionStrings
? Você acha que isso precisa estar no arquivo?
Adicionar comentários, tags e outros metadados
Pense em qualquer coisa no modelo que possa não ser óbvia ou que precise de explicações adicionais. Você pode adicionar comentários para torná-lo mais claro para outras pessoas que podem abrir o arquivo no futuro?
Dê uma olhada na
webSite
propriedade doidentity
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}': {} } } }
Essa sintaxe é estranha, não é? Você acha que isso precisa de um comentário para ajudar a explicá-lo?
Observe 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
guid()
função. Ajudaria a explicar porquê?Você pode adicionar uma descrição à atribuição de função?
Você pode adicionar um conjunto de tags a cada recurso?
Solução sugerida
Aqui está 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'
}
}
Gorjeta
Se você estiver trabalhando com seus colegas usando o GitHub ou o Azure Repos, este seria um ótimo momento para enviar uma solicitação pull para integrar suas alterações na ramificação principal. É uma boa ideia enviar solicitações pull depois de fazer um trabalho de refatoração.