Ćwiczenie — przygotowanie konta magazynu i bazy danych
Zaktualizowano pipeline w celu skompilowania i wdrożenia aplikacji serwisu internetowego w aplikacji usługi Azure App Service zdefiniowanej w pliku Bicep. Jednak etap testu dymnego kończy się niepowodzeniem, ponieważ baza danych nie działa jeszcze. W tej lekcji wdrożysz nowy serwer logiczny i bazę danych usługi Azure SQL oraz skonfigurujesz potok w celu skompilowania i wdrożenia schematu bazy danych. Zaktualizujesz również potok, aby dodać przykładowe dane produktów dla środowiska testowego, żeby zespół mógł przetestować stronę internetową.
W tym procesie wykonujesz następujące zadania:
- Dodaj kontener obiektów blob do konta usługi Azure Storage.
- Dodaj serwer logiczny i bazę danych usługi Azure SQL.
- Zaktualizuj etap kompilacji, aby skompilować projekt bazy danych w pliku DACPAC.
- Dodaj nowe zmienne do grupy zmiennych dla serwera logicznego i bazy danych azure SQL.
- Zaktualizuj etapy wdrażania, aby użyć nowych zmiennych jako wartości parametrów.
- Dodaj nowe etapy procesu, aby wdrożyć plik DACPAC.
- Uruchom rurę i wyświetl witrynę internetową.
Dodaj pojemnik magazynowy
Plik Bicep definiuje już konto magazynowe, ale nie definiuje kontenera blobów. W tym miejscu dodasz kontener obiektów blob do pliku Bicep. Podaj również nazwę konta magazynu i kontenera blob w ustawieniach konfiguracji aplikacji. Dzięki temu aplikacja wie, do którego konta magazynu ma uzyskać dostęp.
W programie Visual Studio Code otwórz plik main.bicep w folderze deploy.
Poniżej zmiennych definiujących nazwy zasobów (w pobliżu wiersza 27) dodaj nową definicję zmiennej dla nazwy kontenera magazynu obiektów blob:
var storageAccountImagesBlobContainerName = 'toyimages'
Zaktualizuj zasób
storageAccount
, aby zdefiniować kontener 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' } } } }
Zaktualizuj właściwość
appSettings
aplikacji, aby dodać trzy nowe ustawienia aplikacji dla nazwy konta magazynu, punktu końcowego blob i nazwy kontenera 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 } ] } } }
Na końcu pliku dodaj nowe dane wyjściowe, aby uwidocznić nazwy konta magazynu i kontenera obiektów blob:
output storageAccountName string = storageAccount.name output storageAccountImagesBlobContainerName string = storageAccount::blobService::storageAccountImagesBlobContainer.name
Zapisz zmiany w pliku.
Zatwierdź swoje zmiany do repozytorium Git, ale jeszcze ich nie przesyłaj. W terminalu programu Visual Studio Code uruchom następujące polecenia:
git add . git commit -m "Add storage container"
Dodawanie serwera logicznego i bazy danych azure SQL
Plik Bicep nie wdraża obecnie serwera logicznego ani bazy danych Azure SQL. W tej sekcji dodasz te zasoby do pliku Bicep.
W pliku main.bicep dodaj dwa nowe parametry poniżej parametru
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
Poniżej zmiennych definiujących nazwy zasobów dodaj nowe zmienne, aby zdefiniować nazwy serwera logicznego i bazy danych Azure SQL:
var sqlServerName = 'toy-website-${resourceNameSuffix}' var sqlDatabaseName = 'Toys'
Poniżej właśnie dodanych zmiennych zdefiniuj nową zmienną, która tworzy parametry połączenia dla aplikacji w celu uzyskania dostępu do bazy danych:
// 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;'
Notatka
Dla uproszczenia aplikacja używa identyfikatora logowania administratora i hasła w celu uzyskania dostępu do bazy danych. Nie jest to jednak dobre rozwiązanie dla rozwiązania produkcyjnego. Lepiej użyć tożsamości zarządzanej usługi App Service w celu uzyskania dostępu do bazy danych i przyznać tożsamości zarządzanej minimalne uprawnienia wymagane przez aplikację. Link do dodatkowych informacji na stronie Podsumowanie.
Na końcu pliku powyżej danych wyjściowych dodaj zasoby serwera logicznego i bazy danych Azure SQL:
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 }
Zaktualizuj zmienną
environmentConfigurationMap
, aby zdefiniować wartościsku
do użycia dla bazy danych dla każdego środowiska: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' } } } }
Dodaj kolejne ustawienie aplikacji do aplikacji usługi App Service dla parametrów połączenia bazy danych:
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 } ] } } }
W dolnej części pliku dodaj dane wyjściowe, aby uwidocznić nazwę hosta serwera logicznego Azure SQL i nazwę bazy danych:
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
Zapisz zmiany w pliku.
Dodawanie nowych kroków kompilacji dla projektu bazy danych
Deweloperzy witryny internetowej przygotowali projekt bazy danych programu Visual Studio, który wdraża i konfiguruje tabelę bazy danych witryny internetowej. W tym miejscu zaktualizujesz potok , aby w etapie kompilacji skompilować projekt bazy danych do pliku DACPAC i opublikować go jako artefakt potoku.
Otwórz plik build.yml w folderze deploy/pipeline-templates.
Aby skompilować projekt bazy danych w Visual Studio, skopiuj wygenerowany plik DACPAC do folderu przejściowego, a następnie opublikuj go jako artefakt potoku, wykonaj następujące kroki:
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'
Zapisz zmiany w pliku.
Dodawanie wartości do grup zmiennych
W przeglądarce przejdź do Pipelines>Library.
Wybierz grupę zmiennych ToyWebsiteProduction.
Dodaj następujące zmienne do grupy zmiennych:
Nazwa Wartość SqlServerAdministratorLogin ToyCompanyAdmin SqlServerAdministratorLoginPassword SecurePassword!111 Wybierz ikonę kłódki obok zmiennej SqlServerAdministratorLoginPassword. Ta funkcja informuje usługę Azure Pipelines o bezpiecznym traktowaniu wartości zmiennej.
Zapisz grupę zmiennych.
Powtórz proces, aby dodać następujące zmienne do grupy zmiennych ToyWebsiteTest:
Nazwa Wartość SqlServerAdministratorLogin TestToyCompanyAdmin SqlServerAdministratorLoginPassword SecurePassword!999 Pamiętaj, aby wybrać ikonę kłódki obok SqlServerAdministratorLoginPassword zmiennej i zapisać grupę zmiennych.
Dodawanie wartości parametrów do etapów weryfikacji i podglądu
Plik Bicep ma teraz dwa nowe obowiązkowe parametry: sqlServerAdministratorLogin
i sqlServerAdministratorLoginPassword
. W tym miejscu propagujesz te wartości parametrów z grupy zmiennych dla etapów Validate i Preview.
W programie Visual Studio Code otwórz plik deploy.yml w folderze deploy/pipeline-templates.
Zaktualizuj krok RunPreflightValidation w etapie Validate, dodając nowe parametry.
- 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)
Zaktualizuj etap Preview, krok RunWhatIf, dodając nowe parametry.
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)
Ważny
Pamiętaj, aby dodać znak ukośnika odwrotnego (
\
) na końcu wiersza, który ustawia wartość parametrureviewApiKey
i w kolejnym wierszu. Znak\
wskazuje, że istnieją kolejne wiersze, które są częścią tego samego polecenia Azure CLI.
Dodawanie wartości parametrów do etapu Wdrażanie
Zaktualizuj krok Deploy stage's DeployBicepFile , dodając nowe parametry:
- 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
Utwórz zmienne potoku zawierające wartości danych wyjściowych Bicep, które zostały ostatnio dodane dla konta magazynu i zasobów usługi Azure SQL:
- 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)
Dodawanie kroków wdrażania bazy danych
W tej sekcji zdefiniujesz kroki wymagane do wdrożenia składników bazy danych witryny internetowej. Najpierw należy dodać krok wdrożenia pliku DACPAC, który został wcześniej utworzony przez potok. Następnie dodasz przykładowe dane do bazy danych i konta magazynu, ale tylko dla środowisk nieprodukcyjnych.
Poniżej kroku DeployWebsiteApp na etapie Deploy dodaj nowy krok w celu wdrożenia pliku 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'
Poniżej właśnie dodanego kroku ustal krok zasiania bazy danych przykładowymi danymi.
- ${{ 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'
Zwróć uwagę, że ten krok ma zastosowany warunek, aby był uruchamiany tylko dla środowisk nieprodukcyjnych.
Poniżej kroku, który właśnie dodałeś, i nadal w ramach wcześniej określonego warunku, dodaj krok, aby przesłać przykładowe obrazy zabawek do kontenera obiektów blob, korzystając z interfejsu wiersza polecenia platformy 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'
Weryfikowanie plików i zatwierdzanie zmian
Sprawdź, czy plik main.bicep wygląda następująco:
@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
Jeśli tak nie jest, zaktualizuj ją tak, aby była zgodna z zawartością pliku.
Sprawdź, czy plik deploy.yml wygląda następująco:
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'
Jeśli tak nie jest, zaktualizuj ją tak, aby była zgodna z zawartością pliku.
Zapisz zmiany w pliku.
Zatwierdź i wypchnij zmiany do repozytorium Git. W terminalu programu Visual Studio Code uruchom następujące polecenia:
git add . git commit -m "Add SQL database" git push
Uruchamianie potoku
W przeglądarce przejdź do Pipeline'y.
Wybierz najnowszy przebieg pipeliny.
Poczekaj, aż wszystkie etapy środowiska testowego zakończą się pomyślnie. Zwróć uwagę, że test dymny teraz również zakończył się pomyślnie.
Poczekaj, aż potok ponownie się zatrzyma przed etapem Preview ( środowisko produkcyjne), ponieważ tym razem potrzebuje uprawnień do innej grupy zmiennych.
Wybierz pozycję View, a następnie wybierz pozycję Permit>Permit.
Etap Preview (Środowisko Produkcyjne) kończy się pomyślnie.
Monitoruj rurociąg, gdy zakończy ostatnie etapy.
Etap Wdrożenie (Środowisko produkcyjne) kończy się pomyślnie, a etap Test dymny (Środowisko produkcyjne) również kończy się pomyślnie.
Wyświetlanie witryny internetowej
Wybierz etap Deploy (Środowisko testowe), aby otworzyć dziennik potoku.
Wybierz krok Wdróż witrynę internetową.
Przytrzymaj wciśnięty Ctrl (⌘ w systemie macOS) i wybierz adres URL aplikacji usługi App Service, otwierając go na nowej karcie przeglądarki.
Wybierz pozycję Toys.
Zwróć uwagę, że przykładowe dane są wyświetlane w środowisku testowym.
Powtórz ten proces dla etapu aplikacji Deploy (Środowisko produkcyjne).
Zwróć uwagę, że w środowisku produkcyjnym nie są wyświetlane żadne przykładowe dane.
Czyszczenie zasobów
Teraz, gdy ćwiczenie zostało zakończone, należy usunąć zasoby, aby uniknąć naliczania opłat.
W terminalu programu Visual Studio Code uruchom następujące polecenia:
az group delete --resource-group ToyWebsiteTest --yes --no-wait
az group delete --resource-group ToyWebsiteProduction --yes --no-wait
Grupa zasobów jest usuwana w tle.
Remove-AzResourceGroup -Name ToyWebsiteTest -Force
Remove-AzResourceGroup -Name ToyWebsiteProduction -Force