Exercício – semear uma conta de armazenamento e um banco de dados
Você atualizou seu pipeline para compilar e implantar o aplicativo do seu site no aplicativo de Serviço de Aplicativo do Azure definido em seu arquivo do Bicep. Mas a fase de smoke test está falhando porque o banco de dados ainda não está funcionando. Nesta unidade, você implanta um novo servidor lógico e um banco de dados SQL do Azure e configura o pipeline para compilar e implantar o esquema do banco de dados. Você também atualiza o seu pipeline para adicionar alguns dados de produto de exemplo para seu ambiente de teste para que sua equipe possa experimentar o site.
No processo, você faz as seguintes tarefas:
- Adicione um contêiner de blob à conta de armazenamento do Azure.
- Adicione um servidor lógico e um banco de dados SQL do Azure.
- Atualize a fase de build para criar o projeto de banco de dados em um arquivo DACPAC.
- Adicione variáveis ao grupo de variáveis para o banco de dados e servidor lógico do SQL do Azure.
- Atualize suas fases de implantação para usar as novas variáveis como valores de parâmetro.
- Adicione etapas de pipeline para implantar o arquivo DACPAC.
- Execute o pipeline e veja o site.
Adicionar um contêiner de armazenamento
O arquivo Bicep já define uma conta de armazenamento, mas não define um contêiner de blob. Aqui, você adiciona um contêiner de blob ao arquivo Bicep. Você também fornece o nome da conta de armazenamento e o contêiner de blob para o aplicativo usando suas definições de configuração. Dessa forma, o aplicativo sabe qual conta de armazenamento acessar.
No Visual Studio Code, abra o arquivo main.bicep na pasta deploy.
Abaixo das variáveis que definem nomes de recursos (perto da linha 27), adicione uma nova definição de variável para o nome do contêiner de armazenamento de blobs:
var storageAccountImagesBlobContainerName = 'toyimages'
Atualize o recurso
storageAccount
para definir o contêiner de blob:resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { name: storageAccountName location: location kind: 'StorageV2' sku: environmentConfigurationMap[environmentType].storageAccount.sku resource blobService 'blobServices' = { name: 'default' resource storageAccountImagesBlobContainer 'containers' = { name: storageAccountImagesBlobContainerName properties: { publicAccess: 'Blob' } } } }
Atualize a propriedade do aplicativo
appSettings
para adicionar três novas configurações de aplicativo para o nome da conta de armazenamento, para o ponto de extremidade do blob e para o nome do contêiner de blob:resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = { name: appServiceAppName location: location properties: { serverFarmId: appServicePlan.id httpsOnly: true siteConfig: { appSettings: [ { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' value: applicationInsights.properties.InstrumentationKey } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.properties.ConnectionString } { name: 'ReviewApiUrl' value: reviewApiUrl } { name: 'ReviewApiKey' value: reviewApiKey } { name: 'StorageAccountName' value: storageAccount.name } { name: 'StorageAccountBlobEndpoint' value: storageAccount.properties.primaryEndpoints.blob } { name: 'StorageAccountImagesContainerName' value: storageAccount::blobService::storageAccountImagesBlobContainer.name } ] } } }
No final do arquivo, adicione novas saídas para expor os nomes da conta de armazenamento e do contêiner de blob:
output storageAccountName string = storageAccount.name output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name
Salve as alterações no arquivo.
Faça o commit de suas alterações no repositório do Git, mas não as envie por push ainda. No terminal do Visual Studio Code, execute os seguintes comandos:
git add . git commit -m "Add storage container"
Adicionar um servidor lógico e um banco de dados do SQL do Azure
No momento, o arquivo Bicep não implanta um banco de dados ou servidor lógico do SQL do Azure. Nesta seção, você vai adicionar esses recursos ao arquivo Bicep.
No arquivo main.bicep, adicione dois novos parâmetros abaixo do parâmetro
reviewApiKey
:@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
Abaixo das variáveis que definem nomes de recursos, adicione variáveis para definir os nomes do servidor lógico e banco de dados do SQL do Azure:
var sqlServerName = 'toy-website-${resourceNameSuffix}' var sqlDatabaseName = 'Toys'
Abaixo das variáveis que você acabou de adicionar, defina uma variável que cria uma cadeia de conexão para o aplicativo acessar o banco de dados:
// Define the connection string to access Azure SQL. var sqlDatabaseConnectionString = 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};Persist Security Info=False;User ID=${sqlServerAdministratorLogin};Password=${sqlServerAdministratorLoginPassword};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;'
Observação
Para simplificar, o aplicativo usa o logon de administrador e a senha para acessar o banco de dados. No entanto, essa não é uma boa prática para uma solução de produção. É melhor usar uma identidade gerenciada do Serviço de Aplicativo para acessar o banco de dados e conceder à identidade gerenciada as permissões mínimas necessárias para o aplicativo. Há um link para mais informações na página de Resumo.
Próximo ao final do arquivo, acima das saídas, adicione os recursos de banco de dados e servidor lógico do SQL do Azure:
resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { name: sqlServerName location: location properties: { administratorLogin: sqlServerAdministratorLogin administratorLoginPassword: sqlServerAdministratorLoginPassword } } resource sqlServerFirewallRule 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = { parent: sqlServer name: 'AllowAllWindowsAzureIps' properties: { endIpAddress: '0.0.0.0' startIpAddress: '0.0.0.0' } } resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = { parent: sqlServer name: sqlDatabaseName location: location sku: environmentConfigurationMap[environmentType].sqlDatabase.sku }
Atualize a variável
environmentConfigurationMap
para definir os valoressku
a serem usados para o banco de dados para cada ambiente:var environmentConfigurationMap = { Production: { appServicePlan: { sku: { name: 'S1' capacity: 1 } } storageAccount: { sku: { name: 'Standard_LRS' } } sqlDatabase: { sku: { name: 'Standard' tier: 'Standard' } } } Test: { appServicePlan: { sku: { name: 'F1' } } storageAccount: { sku: { name: 'Standard_GRS' } } sqlDatabase: { sku: { name: 'Standard' tier: 'Standard' } } } }
Adicione outra configuração de aplicativo ao aplicativo do Serviço de Aplicativo para a cadeia de conexão de banco de dados:
resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = { name: appServiceAppName location: location properties: { serverFarmId: appServicePlan.id httpsOnly: true siteConfig: { appSettings: [ { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' value: applicationInsights.properties.InstrumentationKey } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.properties.ConnectionString } { name: 'ReviewApiUrl' value: reviewApiUrl } { name: 'ReviewApiKey' value: reviewApiKey } { name: 'StorageAccountName' value: storageAccount.name } { name: 'StorageAccountBlobEndpoint' value: storageAccount.properties.primaryEndpoints.blob } { name: 'StorageAccountImagesContainerName' value: storageAccount::blobService::storageAccountImagesBlobContainer.name } { name: 'SqlDatabaseConnectionString' value: sqlDatabaseConnectionString } ] } } }
Na parte inferior do arquivo, adicione saídas para expor o nome do host do servidor lógico SQL do Azure e o nome do banco de dados:
output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName output storageAccountName string = storageAccount.name output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name output sqlServerFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName output sqlDatabaseName string = sqlDatabase.name
Salve as alterações no arquivo.
Adicionar etapas de build para o projeto de banco de dados
Os desenvolvedores do site prepararam um projeto de banco de dados do Visual Studio que implanta e configura a tabela de banco de dados do site. Aqui, você atualiza a fase de Build do pipeline para criar o projeto de banco de dados em um arquivo DACPAC e publicá-lo como um artefato de pipeline.
Abra o arquivo build.yml na pasta deploy/pipeline-templates.
Para criar o projeto de banco de dados Visual Studio, copie o arquivo DACPAC gerado para uma pasta de preparo, publique-o como um artefato de pipeline e adicione as seguintes etapas:
jobs: - job: Build displayName: Build application and database pool: vmImage: windows-latest steps: # Build, copy, and publish the website. - task: DotNetCoreCLI@2 displayName: Build publishable website inputs: command: 'publish' publishWebProjects: true - task: CopyFiles@2 displayName: Copy publishable website inputs: sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Website/bin' contents: '**/publish.zip' targetFolder: '$(Build.ArtifactStagingDirectory)/website' flattenFolders: true - task: PublishBuildArtifacts@1 displayName: Publish website as pipeline artifact inputs: pathToPublish: '$(Build.ArtifactStagingDirectory)/website' artifactName: 'website' # Build, copy, and publish the DACPAC file. - task: VSBuild@1 displayName: Build Visual Studio solution inputs: solution: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Database/ToyCompany.Database.sqlproj' - task: CopyFiles@2 displayName: Copy DACPAC inputs: sourceFolder: '$(Build.SourcesDirectory)/src/ToyCompany/ToyCompany.Database/bin' contents: '**/*.dacpac' targetFolder: '$(Build.ArtifactStagingDirectory)/database' flattenFolders: true - task: PublishBuildArtifacts@1 displayName: Publish DACPAC as pipeline artifact inputs: pathToPublish: '$(Build.ArtifactStagingDirectory)/database' artifactName: 'database'
Salve as alterações no arquivo.
Adicionar valores aos grupos de variáveis
No navegador, acesse Pipelines>Biblioteca.
Selecione o grupo de variáveis ToyWebsiteProduction.
Adicione as variáveis a seguir ao grupo de variáveis:
Nome Valor SqlServerAdministratorLogin ToyCompanyAdmin SqlServerAdministratorLoginPassword SecurePassword!111 Selecione o ícone de cadeado ao lado da variável SqlServerAdministratorLoginPassword. Esta funcionalidade diz para o Azure Pipelines tratar o valor da variável com segurança.
Salve o grupo de variáveis.
Repita o processo para adicionar as seguintes variáveis ao grupo de variáveis ToyWebsiteTest:
Nome Valor SqlServerAdministratorLogin TestToyCompanyAdmin SqlServerAdministratorLoginPassword SecurePassword!999 Lembre-se de selecionar o ícone de cadeado ao lado da variável SqlServerAdministratorLoginPassword e salvar o grupo de variáveis.
Adicionar valores de parâmetro às fases Validar e Visualizar
O arquivo Bicep agora tem dois novos parâmetros obrigatórios: sqlServerAdministratorLogin
e sqlServerAdministratorLoginPassword
. Aqui, você propaga esses valores de parâmetro de seu grupo de variáveis para as fases Validar e Visualizar.
No Visual Studio Code, abra o arquivo deploy.yml na pasta deploy/pipeline-templates.
Atualize a etapa RunPreflightValidation do estágio de validação adicionando os novos parâmetros.
- task: AzureResourceManagerTemplateDeployment@3 name: RunPreflightValidation displayName: Run preflight validation inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} location: ${{parameters.deploymentDefaultLocation}} deploymentMode: Validation resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) -sqlServerAdministratorLogin $(SqlServerAdministratorLogin) -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword)
Atualize a etapa RunWhatIf do estágio de pré-visualização adicionando os novos parâmetros.
inlineScript: | az deployment group what-if \ --resource-group $(ResourceGroupName) \ --template-file deploy/main.bicep \ --parameters environmentType=$(EnvironmentType) \ reviewApiUrl=$(ReviewApiUrl) \ reviewApiKey=$(ReviewApiKey) \ sqlServerAdministratorLogin=$(SqlServerAdministratorLogin) \ sqlServerAdministratorLoginPassword=$(SqlServerAdministratorLoginPassword)
Importante
Adicione o caractere de barra invertida (
\
) ao final da linha que define o valor do parâmetroreviewApiKey
e na linha subsequente. O caractere\
indica que há outras linhas que fazem parte do mesmo comando da CLI do Azure.
Adicionar valores de parâmetro à fase Implantar
Atualize a etapa DeployBicepFile do estágio de implantação adicionando os novos parâmetros:
- task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) -sqlServerAdministratorLogin $(SqlServerAdministratorLogin) -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword) deploymentOutputs: deploymentOutputs
Crie variáveis de pipeline que contêm os valores das saídas do Bicep adicionadas recentemente para a conta de armazenamento e os recursos de SQL do Azure:
- bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" echo "##vso[task.setvariable variable=storageAccountName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountName.value')" echo "##vso[task.setvariable variable=storageAccountImagesBlobContainerName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountImagesBlobContainerName.value')" echo "##vso[task.setvariable variable=sqlServerFullyQualifiedDomainName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlServerFullyQualifiedDomainName.value')" echo "##vso[task.setvariable variable=sqlDatabaseName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlDatabaseName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
Adicionar etapas de implantação de banco de dados
Nesta seção, você define as etapas necessárias para implantar os componentes de banco de dados do seu site. Primeiro, você adiciona uma etapa para implantar o arquivo DACPAC criado anteriormente pelo pipeline. Em seguida, você adiciona dados de exemplo ao banco de dados e à conta de armazenamento, mas apenas para ambientes de não produção.
Abaixo da etapa Implantar na fase DeployWebsiteApp, adicione uma etapa para implantar o arquivo DACPAC:
- task: SqlAzureDacpacDeployment@1 name: DeploySqlDatabaseDacpac displayName: Deploy DACPAC to database inputs: ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}} authenticationType: 'server' serverName: $(sqlServerFullyQualifiedDomainName) databaseName: $(sqlDatabaseName) sqlUsername: $(SqlServerAdministratorLogin) sqlPassword: $(SqlServerAdministratorLoginPassword) deployType: 'DacpacTask' deploymentAction: 'Publish' dacpacFile: '$(Pipeline.Workspace)/database/ToyCompany.Database.dacpac'
Abaixo da etapa que você acabou de adicionar, defina uma etapa para propagar o banco de dados com o exemplo.
- ${{ if ne(parameters.environmentType, 'Production') }}: - task: SqlAzureDacpacDeployment@1 name: AddTestDataToDatabase displayName: Add test data to database inputs: ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}} authenticationType: 'server' serverName: $(sqlServerFullyQualifiedDomainName) databaseName: $(sqlDatabaseName) sqlUsername: $(SqlServerAdministratorLogin) sqlPassword: $(SqlServerAdministratorLoginPassword) deployType: 'sqlTask' sqlFile: 'deploy/sample-data/Toys.sql'
Observe que esta etapa tem uma condição aplicada a ela para ser executada apenas para ambientes de não produção.
Abaixo da etapa que você acabou de adicionar e ainda dentro do escopo da condição, adicione uma etapa para carregar algumas imagens de brinquedos de exemplo no contêiner de blobs usando a CLI do Azure:
- task: AzureCLI@2 name: UploadSampleImages displayName: Upload sample images inputs: azureSubscription: ToyWebsite${{parameters.environmentType}} scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az storage blob upload-batch \ --account-name $(storageAccountName) \ --destination $(storageAccountImagesBlobContainerName) \ --source 'deploy/sample-data/toyimages'
Verificar arquivos e confirmar suas alterações
Verifique se o arquivo main.bicep tem esta aparência:
@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 URL to the product review API.') param reviewApiUrl string @secure() @description('The API key to use when accessing the product review API.') param reviewApiKey string @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 // Define the names for resources. var appServiceAppName = 'toy-website-${resourceNameSuffix}' var appServicePlanName = 'toy-website' var logAnalyticsWorkspaceName = 'workspace-${resourceNameSuffix}' var applicationInsightsName = 'toywebsite' var storageAccountName = 'mystorage${resourceNameSuffix}' var storageAccountImagesBlobContainerName = 'toyimages' var sqlServerName = 'toy-website-${resourceNameSuffix}' var sqlDatabaseName = 'Toys' // Define the connection string to access Azure SQL. var sqlDatabaseConnectionString = 'Server=tcp:${sqlServer.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqlDatabase.name};Persist Security Info=False;User ID=${sqlServerAdministratorLogin};Password=${sqlServerAdministratorLoginPassword};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;' // Define the SKUs for each component based on the environment type. var environmentConfigurationMap = { Production: { appServicePlan: { sku: { name: 'S1' capacity: 1 } } storageAccount: { sku: { name: 'Standard_LRS' } } sqlDatabase: { sku: { name: 'Standard' tier: 'Standard' } } } Test: { appServicePlan: { sku: { name: 'F1' } } storageAccount: { sku: { name: 'Standard_GRS' } } sqlDatabase: { sku: { name: 'Standard' tier: 'Standard' } } } } resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { name: appServicePlanName location: location sku: environmentConfigurationMap[environmentType].appServicePlan.sku } resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = { name: appServiceAppName location: location properties: { serverFarmId: appServicePlan.id httpsOnly: true siteConfig: { appSettings: [ { name: 'APPINSIGHTS_INSTRUMENTATIONKEY' value: applicationInsights.properties.InstrumentationKey } { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' value: applicationInsights.properties.ConnectionString } { name: 'ReviewApiUrl' value: reviewApiUrl } { name: 'ReviewApiKey' value: reviewApiKey } { name: 'StorageAccountName' value: storageAccount.name } { name: 'StorageAccountBlobEndpoint' value: storageAccount.properties.primaryEndpoints.blob } { name: 'StorageAccountImagesContainerName' value: storageAccount::blobService::storageAccountImagesBlobContainer.name } { name: 'SqlDatabaseConnectionString' value: sqlDatabaseConnectionString } ] } } } resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { name: logAnalyticsWorkspaceName location: location } resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { name: applicationInsightsName location: location kind: 'web' properties: { Application_Type: 'web' Request_Source: 'rest' Flow_Type: 'Bluefield' WorkspaceResourceId: logAnalyticsWorkspace.id } } resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { name: storageAccountName location: location kind: 'StorageV2' sku: environmentConfigurationMap[environmentType].storageAccount.sku resource blobService 'blobServices' = { name: 'default' resource storageAccountImagesBlobContainer 'containers' = { name: storageAccountImagesBlobContainerName properties: { publicAccess: 'Blob' } } } } resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { name: sqlServerName location: location properties: { administratorLogin: sqlServerAdministratorLogin administratorLoginPassword: sqlServerAdministratorLoginPassword } } resource sqlServerFirewallRule 'Microsoft.Sql/servers/firewallRules@2022-05-01-preview' = { parent: sqlServer name: 'AllowAllWindowsAzureIps' properties: { endIpAddress: '0.0.0.0' startIpAddress: '0.0.0.0' } } resource sqlDatabase 'Microsoft.Sql/servers/databases@2022-05-01-preview' = { parent: sqlServer name: sqlDatabaseName location: location sku: environmentConfigurationMap[environmentType].sqlDatabase.sku } output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName output storageAccountName string = storageAccount.name output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name output sqlServerFullyQualifiedDomainName string = sqlServer.properties.fullyQualifiedDomainName output sqlDatabaseName string = sqlDatabase.name
Se não, atualize-o para corresponder ao conteúdo do arquivo.
Verifique se o arquivo deploy.yml tem esta aparência:
parameters: - name: environmentType type: string - name: deploymentDefaultLocation type: string default: westus3 stages: - ${{ if ne(parameters.environmentType, 'Production') }}: - stage: Validate_${{parameters.environmentType}} displayName: Validate (${{parameters.environmentType}} Environment) jobs: - job: ValidateBicepCode displayName: Validate Bicep code variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureResourceManagerTemplateDeployment@3 name: RunPreflightValidation displayName: Run preflight validation inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} location: ${{parameters.deploymentDefaultLocation}} deploymentMode: Validation resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) -sqlServerAdministratorLogin $(SqlServerAdministratorLogin) -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword) - ${{ if eq(parameters.environmentType, 'Production') }}: - stage: Preview_${{parameters.environmentType}} displayName: Preview (${{parameters.environmentType}} Environment) jobs: - job: PreviewAzureChanges displayName: Preview Azure changes variables: - group: ToyWebsite${{parameters.environmentType}} steps: - task: AzureCLI@2 name: RunWhatIf displayName: Run what-if inputs: azureSubscription: ToyWebsite${{parameters.environmentType}} scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az deployment group what-if \ --resource-group $(ResourceGroupName) \ --template-file deploy/main.bicep \ --parameters environmentType=$(EnvironmentType) \ reviewApiUrl=$(ReviewApiUrl) \ reviewApiKey=$(ReviewApiKey) \ sqlServerAdministratorLogin=$(SqlServerAdministratorLogin) \ sqlServerAdministratorLoginPassword=$(SqlServerAdministratorLoginPassword) - stage: Deploy_${{parameters.environmentType}} displayName: Deploy (${{parameters.environmentType}} Environment) jobs: - deployment: DeployWebsite displayName: Deploy website pool: vmImage: windows-latest variables: - group: ToyWebsite${{parameters.environmentType}} environment: ${{parameters.environmentType}} strategy: runOnce: deploy: steps: - checkout: self - task: AzureResourceManagerTemplateDeployment@3 name: DeployBicepFile displayName: Deploy Bicep file inputs: connectedServiceName: ToyWebsite${{parameters.environmentType}} deploymentName: $(Build.BuildNumber) location: ${{parameters.deploymentDefaultLocation}} resourceGroupName: $(ResourceGroupName) csmFile: deploy/main.bicep overrideParameters: > -environmentType $(EnvironmentType) -reviewApiUrl $(ReviewApiUrl) -reviewApiKey $(ReviewApiKey) -sqlServerAdministratorLogin $(SqlServerAdministratorLogin) -sqlServerAdministratorLoginPassword $(SqlServerAdministratorLoginPassword) deploymentOutputs: deploymentOutputs - bash: | echo "##vso[task.setvariable variable=appServiceAppName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppName.value')" echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')" echo "##vso[task.setvariable variable=storageAccountName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountName.value')" echo "##vso[task.setvariable variable=storageAccountImagesBlobContainerName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.storageAccountImagesBlobContainerName.value')" echo "##vso[task.setvariable variable=sqlServerFullyQualifiedDomainName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlServerFullyQualifiedDomainName.value')" echo "##vso[task.setvariable variable=sqlDatabaseName]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.sqlDatabaseName.value')" name: SaveDeploymentOutputs displayName: Save deployment outputs into variables env: DEPLOYMENT_OUTPUTS: $(deploymentOutputs) - task: AzureRmWebAppDeployment@4 name: DeployWebsiteApp displayName: Deploy website inputs: appType: webApp ConnectionType: AzureRM azureSubscription: ToyWebsite${{parameters.environmentType}} ResourceGroupName: $(ResourceGroupName) WebAppName: $(appServiceAppName) Package: '$(Pipeline.Workspace)/website/publish.zip' - task: SqlAzureDacpacDeployment@1 name: DeploySqlDatabaseDacpac displayName: Deploy DACPAC to database inputs: ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}} authenticationType: 'server' serverName: $(sqlServerFullyQualifiedDomainName) databaseName: $(sqlDatabaseName) sqlUsername: $(SqlServerAdministratorLogin) sqlPassword: $(SqlServerAdministratorLoginPassword) deployType: 'DacpacTask' deploymentAction: 'Publish' dacpacFile: '$(Pipeline.Workspace)/database/ToyCompany.Database.dacpac' - ${{ if ne(parameters.environmentType, 'Production') }}: - task: SqlAzureDacpacDeployment@1 name: AddTestDataToDatabase displayName: Add test data to database inputs: ConnectedServiceNameARM: ToyWebsite${{parameters.environmentType}} authenticationType: 'server' serverName: $(sqlServerFullyQualifiedDomainName) databaseName: $(sqlDatabaseName) sqlUsername: $(SqlServerAdministratorLogin) sqlPassword: $(SqlServerAdministratorLoginPassword) deployType: 'sqlTask' sqlFile: 'deploy/sample-data/Toys.sql' - task: AzureCLI@2 name: UploadSampleImages displayName: Upload sample images inputs: azureSubscription: ToyWebsite${{parameters.environmentType}} scriptType: 'bash' scriptLocation: 'inlineScript' inlineScript: | az storage blob upload-batch \ --account-name $(storageAccountName) \ --destination $(storageAccountImagesBlobContainerName) \ --source 'deploy/sample-data/toyimages' - stage: SmokeTest_${{parameters.environmentType}} displayName: Smoke Test (${{parameters.environmentType}} Environment) jobs: - job: SmokeTest displayName: Smoke test variables: appServiceAppHostName: $[ stageDependencies.Deploy_${{parameters.environmentType}}.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ] steps: - task: PowerShell@2 name: RunSmokeTests displayName: Run smoke tests inputs: targetType: inline script: | $container = New-PesterContainer ` -Path 'deploy/Website.Tests.ps1' ` -Data @{ HostName = '$(appServiceAppHostName)' } Invoke-Pester ` -Container $container ` -CI - task: PublishTestResults@2 name: PublishTestResults displayName: Publish test results condition: always() inputs: testResultsFormat: NUnit testResultsFiles: 'testResults.xml'
Se não, atualize-o para corresponder ao conteúdo do arquivo.
Salve as alterações no arquivo.
Faça o commit e o push de suas alterações para o repositório Git. No terminal do Visual Studio Code, execute os seguintes comandos:
git add . git commit -m "Add SQL database" git push
Executar o pipeline
No navegador, acesse Pipelines.
Selecione a execução mais recente do pipeline.
Aguarde até que todas as fases do ambiente de teste terminem com êxito. Observe que o smoke test agora também é bem-sucedido.
Aguarde até que o pipeline pause novamente antes da fase Visualizar (Ambiente de Produção), pois ele precisa de permissão para um grupo de variáveis diferente desta vez.
Selecione Exibir e Permitir>Permitir.
A fase Visualizar (Ambiente de Produção) é finalizada com êxito.
Monitore o pipeline à medida que ele conclui os estágios finais.
A fase Implantar (Ambiente de Produção) é finalizada com êxito e a fase Smoke Test (Ambiente de Produção) também é finalizado com êxito.
Exibir o site
Selecione a fase Implantar (Ambiente de Teste) para abrir o log do pipeline.
Selecione a etapa Implantar site.
Mantenha pressionada a tecla Ctrl (⌘ no macOS) e selecione a URL do aplicativo do Serviço de Aplicativo, abrindo-a em uma nova guia do navegador.
Selecione Brinquedos.
Observe que os dados de exemplo são exibidos no ambiente de teste.
Repita o processo anterior para o aplicativo da fase Implantar (Ambiente de Produção).
Observe que nenhum dado de exemplo é exibido no ambiente de produção.
Limpar os recursos
Agora que o exercício foi concluído, você deve remover os recursos para não ser cobrado por eles.
No terminal do Visual Studio Code, execute os seguintes comandos:
az group delete --resource-group ToyWebsiteTest --yes --no-wait
az group delete --resource-group ToyWebsiteProduction --yes --no-wait
O grupo de recursos é excluído em segundo plano.
Remove-AzResourceGroup -Name ToyWebsiteTest -Force
Remove-AzResourceGroup -Name ToyWebsiteProduction -Force