Ćwiczenie — inicjowanie konta magazynu i bazy danych

Ukończone

Zaktualizowano przepływ pracy w celu skompilowania i wdrożenia aplikacji witryny internetowej w aplikacji usługi aplikacja systemu Azure Service zdefiniowanej w pliku Bicep, ale zadanie 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. Skonfigurujesz przepływ pracy w celu skompilowania i wdrożenia schematu bazy danych. Zaktualizujesz również przepływ pracy, aby dodać przykładowe dane produktów dla środowiska testowego, aby twój zespół mógł wypróbować witrynę internetową.

W tym procesie wykonasz następujące czynności:

  • Dodaj kontener obiektów blob do konta usługi Azure Storage.
  • Dodaj serwer logiczny i bazę danych usługi Azure SQL.
  • Zaktualizuj zadanie kompilacji, aby skompilować projekt bazy danych w pliku DACPAC.
  • Dodaj nowe zmienne i wpisy tajne dla serwera logicznego i bazy danych azure SQL.
  • Zaktualizuj przepływ pracy, aby używał nowych zmiennych i wpisów tajnych.
  • Dodaj nowe kroki przepływu pracy, aby wdrożyć plik DACPAC.
  • Uruchom przepływ pracy 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.

  1. W programie Visual Studio Code otwórz plik main.bicep w folderze deploy .

  2. 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'
    
  3. 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'
          }
        }
      }
    }
    
  4. Zaktualizuj właściwość aplikacji appSettings , aby dodać dwa nowe ustawienia aplikacji: jedną dla nazwy konta magazynu i jedną dla 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
            }
          ]
        }
      }
    }
    
  5. Na końcu zawartości 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
    
  6. Zapisz zmiany w pliku.

  7. 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.

  1. W pliku main.bicep dodaj dwa nowe parametry poniżej reviewApiKey parametru w górnej części pliku:

    @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
    
  2. 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'
    
  3. 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 znajduje się na stronie Podsumowanie tego modułu.

  4. Na końcu zawartości 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
    }
    
  5. Zaktualizuj zmienną environmentConfigurationMap , aby zdefiniować jednostki SKU 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'
          }
        }
      }
    }
    
  6. 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
            }
          ]
        }
      }
    }
    
  7. 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
    
  8. 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 kompilację przepływu pracy o nazwie workflow, aby skompilować projekt bazy danych w pliku DACPAC i przekazać go jako artefakt przepływu pracy.

  1. Otwórz plik build.yml w folderze .github/workflows.

  2. Aby skompilować projekt bazy danych programu Visual Studio i przekazać wygenerowany plik DACPAC jako artefakt przepływu pracy, dodaj zadanie kompilacji bazy danych :

    name: build-website
    
    on:
      workflow_call:
    
    jobs:
      build-application:
        name: Build application
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
    
        - name: Install .NET Core
          uses: actions/setup-dotnet@v3
          with:
            dotnet-version: 3.1
    
        - name: Build publishable website
          run: |
            dotnet publish --configuration Release
          working-directory: ./src/ToyCompany/ToyCompany.Website
    
        - name: Zip publishable website
          run: |
            zip -r publish.zip .
          working-directory: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish
    
        - name: Upload website as workflow artifact
          uses: actions/upload-artifact@v3
          with:
            name: website
            path: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish/publish.zip
    
      build-database:
        name: Build database
        runs-on: windows-latest
        steps:
        - uses: actions/checkout@v3
    
        - name: Prepare MSBuild
          uses: microsoft/setup-msbuild@v1.1
    
        - name: Build database project
          working-directory: ./src/ToyCompany/ToyCompany.Database
          run: MSBuild.exe ToyCompany.Database.sqlproj -property:Configuration=Release
    
        - name: Upload website as workflow artifact
          uses: actions/upload-artifact@v3
          with:
            name: database
            path: ./src/ToyCompany/ToyCompany.Database/bin/Release/ToyCompany.Database.dacpac
    

    Zadanie kompilacji bazy danych używa modułu uruchamiającego system Windows. Obecnie projekty baz danych programu Visual Studio muszą być tworzone w systemie operacyjnym Windows.

  3. Zapisz zmiany w pliku.

Definiowanie wpisów tajnych

Musisz bezpiecznie przechowywać hasło administratora serwera logicznego usługi Azure SQL dla każdego środowiska. Decydujesz się używać wpisów tajnych usługi GitHub do ochrony informacji.

  1. W przeglądarce przejdź do pozycji Ustawienia>Wpisy tajne i zmienne>Akcje.

    Zrzut ekranu usługi GitHub przedstawiający element menu Wpisy tajne w kategorii Ustawienia.

  2. Wybierz przycisk Nowy wpis tajny repozytorium.

  3. Wprowadź SQL_SERVER_ADMINISTRATOR_LOGIN_PASSWORD_TEST jako nazwę wpisu tajnego i wartość SecurePassword!111 .

    Zrzut ekranu witryny GitHub przedstawiający nowy wpis tajny.

  4. Wybierz przycisk Add secret (Dodaj wpis tajny).

  5. Powtórz ten proces, aby dodać inny wpis tajny o nazwie SQL_SERVER_ADMINISTRATOR_LOGIN_PASSWORD_PRODUCTION jako nazwę wpisu tajnego i wartość SecurePassword!999 . Wybierz przycisk Add secret (Dodaj wpis tajny).

Dodawanie wpisów tajnych i danych wejściowych do przepływu pracy

  1. W programie Visual Studio Code otwórz plik deploy.yml w folderze .github/workflows .

  2. W górnej części pliku zdefiniuj nowe dane wejściowe o nazwie i nowy wpis tajny o nazwie sqlServerAdministratorLogin sqlServerAdministratorLoginPassword:

    name: deploy
    
    on:
      workflow_call:
        inputs:
          environmentType:
            required: true
            type: string
          resourceGroupName:
            required: true
            type: string
          reviewApiUrl:
            required: true
            type: string
          sqlServerAdministratorLogin:
            required: true
            type: string
        secrets:
          AZURE_CLIENT_ID:
            required: true
          AZURE_TENANT_ID:
            required: true
          AZURE_SUBSCRIPTION_ID:
            required: true
          reviewApiKey:
            required: true
          sqlServerAdministratorLoginPassword:
            required: true
    
  3. Zapisz zmiany w pliku.

  4. Otwórz plik workflow.yml.

  5. W definicji deploy-test zdefiniuj wartość danych sqlServerAdministratorLogin wejściowych i propaguj wartość wpisu tajnegosqlServerAdministratorLoginPassword:

    # Deploy to the test environment.
    deploy-test:
      uses: ./.github/workflows/deploy.yml
      needs: [build, lint]
      with:
        environmentType: Test
        resourceGroupName: ToyWebsiteTest
        reviewApiUrl: https://sandbox.contoso.com/reviews
        sqlServerAdministratorLogin: TestToyCompanyAdmin
      secrets:
        AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }}
        AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
        AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        reviewApiKey: ${{ secrets.REVIEW_API_KEY_TEST }}
        sqlServerAdministratorLoginPassword: ${{ secrets.SQL_SERVER_ADMINISTRATOR_LOGIN_PASSWORD_TEST }}
    
  6. Powtórz proces w definicji deploy-production z wartościami środowiska produkcyjnego:

    # Deploy to the production environment.
    deploy-production:
      uses: ./.github/workflows/deploy.yml
      needs:
      - lint
      - build
      - deploy-test
      with:
        environmentType: Production
        resourceGroupName: ToyWebsiteProduction
        reviewApiUrl: https://api.contoso.com/reviews
        sqlServerAdministratorLogin: ToyCompanyAdmin
      secrets:
        AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }}
        AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
        AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        reviewApiKey: ${{ secrets.REVIEW_API_KEY_PRODUCTION }}
        sqlServerAdministratorLoginPassword: ${{ secrets.SQL_SERVER_ADMINISTRATOR_LOGIN_PASSWORD_PRODUCTION }}
    
  7. Zapisz zmiany w pliku.

Dodawanie wartości parametrów i danych wyjściowych

Plik Bicep ma teraz dwa nowe obowiązkowe parametry: sqlServerAdministratorLogin i sqlServerAdministratorLoginPassword. W tym miejscu propagujesz te wartości parametrów z danych wejściowych i wpisów tajnych przepływu pracy dla zadań sprawdzania poprawności i wdrażania . Propagujesz również dane wyjściowe wdrożeń Bicep do danych wyjściowych zadania.

  1. Otwórz plik deploy.yml.

  2. Zaktualizuj krok weryfikacji uruchom wstępne zadanie, aby dodać nowe parametry:

    jobs:
      validate:
         runs-on: ubuntu-latest
         steps:
         - uses: actions/checkout@v3
         - uses: azure/login@v1
           name: Sign in to Azure
           with:
             client-id: ${{ secrets.AZURE_CLIENT_ID }}
             tenant-id: ${{ secrets.AZURE_TENANT_ID }}
             subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
         - if: inputs.environmentType != 'Production'
           uses: azure/arm-deploy@v1
           name: Run preflight validation
           with:
             deploymentName: ${{ github.run_number }}
             resourceGroupName: ${{ inputs.resourceGroupName }}
             template: ./deploy/main.bicep
             parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
               sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
               sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
             deploymentMode: Validate
    
  3. Zaktualizuj krok Uruchom co-jeżeli, aby dodać nowe parametry:

    - if: inputs.environmentType == 'Production'
      uses: azure/arm-deploy@v1
      name: Run what-if
      with:
        failOnStdErr: false
        resourceGroupName: ${{ inputs.resourceGroupName }}
        template: ./deploy/main.bicep
        parameters: >
          environmentType=${{ inputs.environmentType }}
          reviewApiUrl=${{ inputs.reviewApiUrl }}
          reviewApiKey=${{ secrets.reviewApiKey }}
          sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
          sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
        additionalArguments: --what-if
    
  4. Zaktualizuj krok Wdrażanie pliku Bicep zadania wdrażania, aby dodać nowe parametry:

    deploy:
      needs: validate
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      outputs:
        appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
        appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
      steps:
      - uses: actions/checkout@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - uses: azure/arm-deploy@v1
        id: deploy
        name: Deploy Bicep file
        with:
          failOnStdErr: false
          deploymentName: ${{ github.run_number }}
          resourceGroupName: ${{ inputs.resourceGroupName }}
          template: ./deploy/main.bicep
          parameters: >
             environmentType=${{ inputs.environmentType }}
             reviewApiUrl=${{ inputs.reviewApiUrl }}
             reviewApiKey=${{ secrets.reviewApiKey }}
             sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
             sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
    
  5. W definicji zadania wdrażania dodaj nowe dane wyjściowe dla danych wyjściowych pliku Bicep:

    deploy:
      needs: validate
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      outputs:
        appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
        appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
        storageAccountName: ${{ steps.deploy.outputs.storageAccountName }}
        storageAccountImagesBlobContainerName: ${{ steps.deploy.outputs.storageAccountImagesBlobContainerName }}
        sqlServerFullyQualifiedDomainName: ${{ steps.deploy.outputs.sqlServerFullyQualifiedDomainName }}
        sqlDatabaseName: ${{ steps.deploy.outputs.sqlDatabaseName }}
    

Dodawanie zadań inicjuj bazy danych i 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 przez przepływ pracy. Następnie dodasz przykładowe dane do bazy danych i konta magazynu, ale tylko dla środowisk nieprodukcyjnych.

  1. Poniżej zadania deploy-website dodaj nowe zadanie w celu wdrożenia pliku DACPAC:

    deploy-database:
      needs: deploy
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      steps:
      - uses: actions/download-artifact@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - uses: azure/sql-action@v1.2
        name: Deploy DACPAC to database
        with:
          server-name: ${{ needs.deploy.outputs.sqlServerFullyQualifiedDomainName }}
          connection-string: ${{ format('Server={0};Initial Catalog={1};User Id={2};Password={3};', needs.deploy.outputs.sqlServerFullyQualifiedDomainName, needs.deploy.outputs.sqlDatabaseName, inputs.sqlServerAdministratorLogin, secrets.sqlServerAdministratorLoginPassword) }}
          dacpac-package: database/ToyCompany.Database.dacpac
    
  2. Poniżej właśnie dodanego zadania i powyżej zadania testu weryfikacyjnego kompilacji zdefiniuj nowe zadanie rozmieszczania bazy danych z przykładowymi danymi.

    seed-database:
      needs:
      - deploy
      - deploy-database
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      steps:
      - uses: actions/checkout@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - if: inputs.environmentType != 'Production'
        uses: azure/sql-action@v1.2
        name: Add test data to database
        with:
          server-name: ${{ needs.deploy.outputs.sqlServerFullyQualifiedDomainName }}
          connection-string: ${{ format('Server={0};Initial Catalog={1};User Id={2};Password={3};', needs.deploy.outputs.sqlServerFullyQualifiedDomainName, needs.deploy.outputs.sqlDatabaseName, inputs.sqlServerAdministratorLogin, secrets.sqlServerAdministratorLoginPassword) }}
          sql-file: 'deploy/sample-data/Toys.sql'
    

    Zwróć uwagę, że krok Dodawanie danych testowych do bazy danych ma zastosowany warunek. Oznacza to, że działa tylko dla środowisk nieprodukcyjnych. Warunek jest stosowany do kroku, a nie do całego zadania, aby późniejsze zadania mogły zależeć od tego zadania niezależnie od typu środowiska.

  3. Poniżej właśnie dodanego zadania i powyżej zadania testu weryfikacyjnego kompilacji zdefiniuj inne zadanie przekazywania przykładowych obrazów do kontenera obiektów blob przy użyciu interfejsu wiersza polecenia platformy Azure:

    seed-storage-account:
      needs: deploy
      environment: ${{ inputs.environmentType }}
      runs-on: ubuntu-latest
      steps:
      - uses: actions/checkout@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - if: inputs.environmentType != 'Production'
        uses: azure/CLI@v1
        name: Upload sample images
        with:
          inlineScript: |
              az storage blob upload-batch \
                --account-name ${{ needs.deploy.outputs.storageAccountName }} \
                --destination ${{ needs.deploy.outputs.storageAccountImagesBlobContainerName }} \
                --source 'deploy/sample-data/toyimages'
    

    Zwróć uwagę, że to zadanie używa modułu uruchamiającego system Ubuntu, ponieważ azure/cli akcja wymaga uruchomienia systemu Linux, ale build-database zdefiniowane wcześniej zadanie używa modułu uruchamiającego system Windows do kompilowania projektu bazy danych. Ten przepływ pracy jest dobrym przykładem użycia różnych systemów operacyjnych w celu osiągnięcia wymagań.

Aktualizowanie zależności zadania testu kompilacji

  1. Zaktualizuj zależności zadania testu weryfikacyjnego kompilacji, aby upewnić się, że działa po zakończeniu wszystkich kroków wdrażania:

    smoke-test:
      runs-on: ubuntu-latest
      needs:
      - deploy
      - deploy-website
      - deploy-database
      - seed-database
      - seed-storage-account
      steps:
      - uses: actions/checkout@v3
      - run: |
          $container = New-PesterContainer `
            -Path 'deploy/Website.Tests.ps1' `
            -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' }
          Invoke-Pester `
            -Container $container `
            -CI
        name: Run smoke tests
        shell: pwsh
    
  2. Zapisz zmiany w pliku.

Weryfikowanie plików i zatwierdzanie zmian

  1. 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.

  2. Sprawdź, czy plik deploy.yml wygląda następująco:

    name: deploy
    
    on:
      workflow_call:
        inputs:
          environmentType:
            required: true
            type: string
          resourceGroupName:
            required: true
            type: string
          reviewApiUrl:
            required: true
            type: string
          sqlServerAdministratorLogin:
            required: true
            type: string
        secrets:
          AZURE_CLIENT_ID:
            required: true
          AZURE_TENANT_ID:
            required: true
          AZURE_SUBSCRIPTION_ID:
            required: true
          reviewApiKey:
            required: true
          sqlServerAdministratorLoginPassword:
            required: true
    
    jobs:
      validate:
         runs-on: ubuntu-latest
         steps:
         - uses: actions/checkout@v3
         - uses: azure/login@v1
           name: Sign in to Azure
           with:
             client-id: ${{ secrets.AZURE_CLIENT_ID }}
             tenant-id: ${{ secrets.AZURE_TENANT_ID }}
             subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
         - if: inputs.environmentType != 'Production'
           uses: azure/arm-deploy@v1
           name: Run preflight validation
           with:
             deploymentName: ${{ github.run_number }}
             resourceGroupName: ${{ inputs.resourceGroupName }}
             template: ./deploy/main.bicep
             parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
               sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
               sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
             deploymentMode: Validate
         - if: inputs.environmentType == 'Production'
           uses: azure/arm-deploy@v1
           name: Run what-if
           with:
             failOnStdErr: false
             resourceGroupName: ${{ inputs.resourceGroupName }}
             template: ./deploy/main.bicep
             parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
               sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
               sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
             additionalArguments: --what-if
    
      deploy:
        needs: validate
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        outputs:
          appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
          appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
          storageAccountName: ${{ steps.deploy.outputs.storageAccountName }}
          storageAccountImagesBlobContainerName: ${{ steps.deploy.outputs.storageAccountImagesBlobContainerName }}
          sqlServerFullyQualifiedDomainName: ${{ steps.deploy.outputs.sqlServerFullyQualifiedDomainName }}
          sqlDatabaseName: ${{ steps.deploy.outputs.sqlDatabaseName }}
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/arm-deploy@v1
          id: deploy
          name: Deploy Bicep file
          with:
            failOnStdErr: false
            deploymentName: ${{ github.run_number }}
            resourceGroupName: ${{ inputs.resourceGroupName }}
            template: ./deploy/main.bicep
            parameters: >
               environmentType=${{ inputs.environmentType }}
               reviewApiUrl=${{ inputs.reviewApiUrl }}
               reviewApiKey=${{ secrets.reviewApiKey }}
               sqlServerAdministratorLogin=${{ inputs.sqlServerAdministratorLogin }}
               sqlServerAdministratorLoginPassword=${{ secrets.sqlServerAdministratorLoginPassword }}
    
      deploy-website:
        needs: deploy
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        steps:
        - uses: actions/download-artifact@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/webapps-deploy@v2
          name: Deploy website
          with:
            app-name: ${{ needs.deploy.outputs.appServiceAppName }}
            package: website/publish.zip
    
      deploy-database:
        needs: deploy
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        steps:
        - uses: actions/download-artifact@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/sql-action@v1.2
          name: Deploy DACPAC to database
          with:
            server-name: ${{ needs.deploy.outputs.sqlServerFullyQualifiedDomainName }}
            connection-string: ${{ format('Server={0};Initial Catalog={1};User Id={2};Password={3};', needs.deploy.outputs.sqlServerFullyQualifiedDomainName, needs.deploy.outputs.sqlDatabaseName, inputs.sqlServerAdministratorLogin, secrets.sqlServerAdministratorLoginPassword) }}
            dacpac-package: database/ToyCompany.Database.dacpac
    
      seed-database:
        needs:
        - deploy
        - deploy-database
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - if: inputs.environmentType != 'Production'
          uses: azure/sql-action@v1.2
          name: Add test data to database
          with:
            server-name: ${{ needs.deploy.outputs.sqlServerFullyQualifiedDomainName }}
            connection-string: ${{ format('Server={0};Initial Catalog={1};User Id={2};Password={3};', needs.deploy.outputs.sqlServerFullyQualifiedDomainName, needs.deploy.outputs.sqlDatabaseName, inputs.sqlServerAdministratorLogin, secrets.sqlServerAdministratorLoginPassword) }}
            sql-file: 'deploy/sample-data/Toys.sql'
    
      seed-storage-account:
        needs: deploy
        environment: ${{ inputs.environmentType }}
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - if: inputs.environmentType != 'Production'
          uses: azure/CLI@v1
          name: Upload sample images
          with:
            inlineScript: |
                az storage blob upload-batch \
                  --account-name ${{ needs.deploy.outputs.storageAccountName }} \
                  --destination ${{ needs.deploy.outputs.storageAccountImagesBlobContainerName }} \
                  --source 'deploy/sample-data/toyimages'
    
      smoke-test:
        runs-on: ubuntu-latest
        needs:
        - deploy
        - deploy-website
        - deploy-database
        - seed-database
        - seed-storage-account
        steps:
        - uses: actions/checkout@v3
        - run: |
            $container = New-PesterContainer `
              -Path 'deploy/Website.Tests.ps1' `
              -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' }
            Invoke-Pester `
              -Container $container `
              -CI
          name: Run smoke tests
          shell: pwsh
    

    Jeśli tak nie jest, zaktualizuj ją tak, aby była zgodna z zawartością pliku.

  3. Zapisz zmiany w pliku.

  4. 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
    

Uruchom przepływ pracy

  1. W przeglądarce przejdź do przebiegów przepływu pracy.

  2. Wybierz najnowszy przebieg.

    Poczekaj, aż wszystkie zadania środowiska testowego zakończą się pomyślnie. Zwróć uwagę, że test weryfikacyjny kompilacji również zakończy się pomyślnie.

    Zrzut ekranu przedstawiający funkcję GitHub Actions przedstawiającą zadanie testu weryfikacyjnego kompilacji przebiegu przepływu pracy dla środowiska testowego. Stan pokazuje, że zadanie zakończyło się pomyślnie.

  3. Poczekaj na pomyślne zakończenie przepływu pracy, w tym wdrożenie produkcyjne.

Wyświetlanie witryny internetowej

  1. Wybierz zadanie deploy-test/deploy-website, aby otworzyć dziennik przepływu pracy.

  2. Wybierz krok Wdróż witrynę internetową .

    Przytrzymaj wciśnięty Ctrl (} w systemie macOS) i wybierz adres URL aplikacji usługi App Service, aby otworzyć go na nowej karcie przeglądarki.

    Zrzut ekranu przedstawiający funkcję GitHub Actions z dziennikem przepływu pracy dla zadania witryny internetowej deploy-website środowiska testowego. Adres URL aplikacji usługi App Service został wyróżniony.

  3. Wybierz pozycję Zabawki.

    Zrzut ekranu przedstawiający stronę główną witryny internetowej firmy zabawki z wyróżnionym linkiem Toys.

    Zwróć uwagę, że przykładowe dane są wyświetlane w środowisku testowym.

    Zrzut ekranu przedstawiający stronę zabawki witryny testowej z wyświetlonymi przykładowymi zabawkami.

  4. Powtórz poprzedni proces dla aplikacji zadania deploy-production/deploy-website .

    Zwróć uwagę, że w środowisku produkcyjnym nie są wyświetlane żadne przykładowe dane.

    Zrzut ekranu przedstawiający stronę zabawki witryny produkcyjnej bez wyświetlanych zabawek.

Czyszczenie zasobów

Po ukończeniu ćwiczenia należy usunąć zasoby platformy Azure, 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