Ćwiczenie — inicjowanie konta magazynu i bazy danych
Zaktualizowano potok w celu skompilowania i wdrożenia aplikacji witryny internetowej w aplikacji usługi aplikacja systemu Azure Service zdefiniowanej w pliku Bicep. Jednak etap testu weryfikacyjnego kompilacji 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, aby zespół mógł wypróbować witrynę 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 kroki potoku, aby wdrożyć plik DACPAC.
- Uruchom potok i wyświetl witrynę internetową.
Dodawanie kontenera magazynu
Plik Bicep definiuje już konto magazynu, ale nie definiuje kontenera obiektów blob. W tym miejscu dodasz kontener obiektów blob do pliku Bicep. Należy również podać nazwę konta magazynu i kontenera obiektów blob do aplikacji przy użyciu jej ustawień konfiguracji. 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 obiektów 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ść aplikacji
appSettings
, aby dodać trzy nowe ustawienia aplikacji dla nazwy konta magazynu, punktu końcowego obiektu blob i nazwy kontenera obiektów 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ź zmiany w repozytorium Git, ale nie wypchnij ich jeszcze. 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;'
Uwaga
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ćsku
wartości używane 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 bazy danych parametry połączenia:
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 etap kompilacji potoku, aby skompilować projekt bazy danych w pliku DACPAC i opublikować go jako artefakt potoku.
Otwórz plik build.yml w folderze deploy/pipeline-templates.
Aby skompilować projekt bazy danych programu Visual Studio, skopiuj wygenerowany plik DACPAC do folderu przejściowego i opublikuj go jako artefakt potoku, dodaj 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 pozycji Biblioteka potoków>.
Wybierz grupę zmiennych ToyWebsiteProduction .
Dodaj do grupy zmiennych następujące zmienne:
Nazwa/nazwisko Wartość SqlServer Administracja istratorLogin ToyCompany Administracja SqlServer Administracja istratorLoginPassword SecurePassword!111 Wybierz ikonę kłódki obok zmiennej SqlServer Administracja istratorLoginPassword. 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/nazwisko Wartość SqlServer Administracja istratorLogin TestToyCompany Administracja SqlServer Administracja istratorLoginPassword SecurePassword!999 Pamiętaj, aby wybrać ikonę kłódki obok zmiennej SqlServer Administracja istratorLoginPassword 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 zarówno dla etapów Weryfikacji, jak i Podgląd.
W programie Visual Studio Code otwórz plik deploy.yml w folderze deploy/pipeline-templates .
Zaktualizuj krok RunPreflightValidation etapu Weryfikacji, 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 krok RunWhatIf etapu podglądu, 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żne
Pamiętaj, aby dodać znak ukośnika odwrotnego (
\
) na końcu wiersza, który ustawiareviewApiKey
wartość parametru i w kolejnym wierszu. Znak\
wskazuje, że istnieją kolejne wiersze, które są częścią tego samego polecenia interfejsu wiersza polecenia platformy Azure.
Dodawanie wartości parametrów do etapu Wdrażanie
Zaktualizuj krok DeployBicepFile etapu wdrażania, 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 wdrażania pliku DACPAC utworzonego wcześniej potoku. Następnie dodasz przykładowe dane do bazy danych i konta magazynu, ale tylko dla środowisk nieprodukcyjnych.
Poniżej kroku DeployWebsiteApp na etapie Wdrażania 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 zdefiniuj krok inicjowania bazy danych z 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 właśnie dodanego kroku i nadal w zakresie warunku dodaj krok, aby przekazać przykładowe obrazy do kontenera obiektów blob przy użyciu 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 pozycji Potoki.
Wybierz najnowszy przebieg potoku.
Poczekaj, aż wszystkie etapy środowiska testowego zakończą się pomyślnie. Zwróć uwagę, że test weryfikacyjny kompilacji również zakończy się pomyślnie.
Poczekaj na ponowne wstrzymanie potoku przed etapem Wersja zapoznawcza (środowisko produkcyjne), ponieważ tym razem potrzebuje uprawnień do innej grupy zmiennych.
Wybierz pozycję Widok, a następnie wybierz pozycję Zezwól na zezwolenie>.
Etap Wersja zapoznawcza (środowisko produkcyjne) kończy się pomyślnie.
Monitoruj potok w miarę kończe ostatnich etapów.
Etap Deploy (Production Environment) zakończy się pomyślnie, a etap Testu weryfikacyjnego kompilacji (środowisko produkcyjne) również zakończy się pomyślnie.
Wyświetlanie witryny internetowej
Wybierz etap Deploy (Test Environment), aby otworzyć dziennik potoku.
Wybierz krok Wdróż witrynę internetową .
Przytrzymaj wciśnięty klawisz Ctrl (} w systemie macOS) i wybierz adres URL aplikacji usługi App Service, otwierając go na nowej karcie przeglądarki.
Wybierz pozycję Zabawki.
Zwróć uwagę, że przykładowe dane są wyświetlane w środowisku testowym.
Powtórz poprzedni proces dla aplikacji etapu Wdrażanie (środowisko produkcyjne).
Zwróć uwagę, że w środowisku produkcyjnym nie są wyświetlane żadne przykładowe dane.
Czyszczenie zasobów
Po zakończeniu ćwiczenia należy usunąć zasoby, aby nie były naliczane opłaty.
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