Ćwiczenie — dodawanie etapu testu do potoku

Ukończone

Zespół ds. zabezpieczeń twojej firmy z prośbą o sprawdzenie, czy witryna internetowa jest dostępna tylko za pośrednictwem protokołu HTTPS. W tym ćwiczeniu skonfigurujesz potok, aby uruchomić test dymny, który sprawdza, czy wymagania zespołu ds. zabezpieczeń zostały spełnione.

Podczas tego procesu wykonasz następujące czynności:

  • Dodaj skrypt testowy do repozytorium.
  • Zaktualizuj definicję potoku, aby dodać etap testu.
  • Uruchom potok i obserwuj niepowodzenie testu.
  • Napraw plik Bicep i sprawdź przebieg potoku pomyślnie.

Dodawanie skryptu testowego

Najpierw dodasz skrypt testowy, aby sprawdzić, czy witryna internetowa jest dostępna, gdy jest używany protokół HTTPS i nie jest dostępny, gdy jest używany niezabezpieczony protokół HTTP.

  1. W programie Visual Studio Code utwórz nowy plik w folderze deploy . Nadaj plikowi nazwę Website.Tests.ps1.

    Zrzut ekranu przedstawiający Eksploratora programu Visual Studio Code. Zostanie wyświetlony folder deploy i plik testowy.

  2. Wklej następujący kod testowy do pliku:

    param(
      [Parameter(Mandatory)]
      [ValidateNotNullOrEmpty()]
      [string] $HostName
    )
    
    Describe 'Toy Website' {
    
        It 'Serves pages over HTTPS' {
          $request = [System.Net.WebRequest]::Create("https://$HostName/")
          $request.AllowAutoRedirect = $false
          $request.GetResponse().StatusCode |
            Should -Be 200 -Because "the website requires HTTPS"
        }
    
        It 'Does not serves pages over HTTP' {
          $request = [System.Net.WebRequest]::Create("http://$HostName/")
          $request.AllowAutoRedirect = $false
          $request.GetResponse().StatusCode | 
            Should -BeGreaterOrEqual 300 -Because "HTTP is not secure"
        }
    
    }
    

    Ten kod jest plikiem testowym Pester. Wymaga parametru o nazwie $HostName. Uruchamia dwa testy względem nazwy hosta:

    • Spróbuj nawiązać połączenie z witryną internetową za pośrednictwem protokołu HTTPS. Test zakończy się pomyślnie, jeśli serwer odpowie kodem stanu odpowiedzi HTTP z zakresu od 200 do 299, co oznacza pomyślne połączenie.
    • Spróbuj nawiązać połączenie z witryną internetową za pośrednictwem protokołu HTTP. Test zakończy się pomyślnie, jeśli serwer odpowie kodem stanu odpowiedzi HTTP 300 lub wyższym.

    Na potrzeby tego ćwiczenia nie musisz rozumieć szczegółów pliku testowego i sposobu jego działania. Jeśli chcesz, możesz dowiedzieć się więcej, przeglądając zasób wymieniony w podsumowaniu modułu.

Publikowanie danych wyjściowych pliku Bicep jako zmiennej wyjściowej etapu

Skrypt testowy utworzony w poprzednich krokach wymaga przetestowania nazwy hosta. Plik Bicep zawiera już dane wyjściowe, ale zanim będzie można go użyć w testach weryfikacyjnych kompilacji, musisz opublikować go jako zmienną wyjściową etapu.

  1. W programie Visual Studio Code otwórz plik azure-pipelines.yml w folderze deploy .

  2. Na etapie Wdrażania zaktualizuj krok wdrażania, aby opublikować dane wyjściowe w zmiennej:

    name: DeployBicepFile
    displayName: Deploy Bicep file
    inputs:
      connectedServiceName: $(ServiceConnectionName)
      deploymentName: $(Build.BuildNumber)
      location: $(deploymentDefaultLocation)
      resourceGroupName: $(ResourceGroupName)
      csmFile: deploy/main.bicep
      overrideParameters: >
        -environmentType $(EnvironmentType)
      deploymentOutputs: deploymentOutputs
    

    Proces wdrażania nadal używa tego samego zadania, co wcześniej, ale dane wyjściowe z wdrożeń są teraz przechowywane w zmiennej potoku o nazwie deploymentOutputs. Zmienna wyjściowa jest formatowana jako JSON.

  3. Aby przekonwertować dane wyjściowe sformatowane w formacie JSON na zmienne potoku, dodaj następujący krok skryptu poniżej kroku wdrażania:

      echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
    name: SaveDeploymentOutputs
    displayName: Save deployment outputs into variables
    env:
      DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    

    Jeśli wdrożenie zakończy się pomyślnie, skrypt uzyskuje dostęp do wartości poszczególnych danych wyjściowych z wdrożenia Bicep. Skrypt używa jq narzędzia do uzyskiwania dostępu do odpowiedniej części danych wyjściowych JSON. Wartość jest następnie publikowana w zmiennej wyjściowej etapu, która ma taką samą nazwę jak wynik wdrożenia Bicep.

    Uwaga

    Oprogramowanie Pester i jq są wstępnie zainstalowane na agentach hostowanych przez firmę Microsoft dla usługi Azure Pipelines. Nie musisz wykonywać żadnych specjalnych czynności, aby używać ich w kroku skryptu.

  4. Zapisz plik.

Dodawanie etapu testu weryfikacyjnego kompilacji do potoku

Teraz możesz dodać etap testu wstępnego, który uruchamia testy.

  1. W dolnej części pliku dodaj następującą definicję etapu SmokeTest :

    jobs:
    - job: SmokeTest
      displayName: Smoke test
      variables:
        appServiceAppHostName: $[ stageDependencies.Deploy.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
      steps:
    

    Ten kod definiuje etap i zadanie. Tworzy również zmienną w zadaniu o nazwie appServiceAppHostName. Ta zmienna pobiera jego wartość ze zmiennej wyjściowej utworzonej w poprzedniej sekcji.

  2. W dolnej części pliku dodaj następującą definicję kroku do etapu SmokeTest :

    - 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
    

    Ten krok uruchamia skrypt programu PowerShell, aby uruchomić napisany wcześniej skrypt testowy przy użyciu narzędzia do testowania Pester.

  3. W dolnej części pliku dodaj następującą definicję kroku do etapu SmokeTest :

    name: PublishTestResults
    displayName: Publish test results
    condition: always()
    inputs:
      testResultsFormat: NUnit
      testResultsFiles: 'testResults.xml'
    

    Ten krok wykonuje plik wyników testu, który Pester tworzy i publikuje jako wyniki testu potoku. Zobaczysz, jak wyniki są wyświetlane wkrótce.

    Zwróć uwagę, że definicja kroku zawiera condition: always()element . Ten warunek wskazuje usłudze Azure Pipelines, że powinien zawsze publikować wyniki testu, nawet jeśli poprzedni krok zakończy się niepowodzeniem. Ten warunek jest ważny, ponieważ każdy nieudany test spowoduje jego niepowodzenie, a zwykle proces przestaje działać po nieudanym etapie.

  4. Zapisz plik.

Weryfikowanie i zatwierdzanie definicji potoku

  1. Sprawdź, czy plik azure-pipelines.yml wygląda następująco:

    trigger:
      batch: true
      branches:
        include:
        - main
    
    pool: Dafault
    
    variables:
      - name: deploymentDefaultLocation
        value: westus3
    
    stages:
    
    - stage: Lint
      jobs:
      - job: LintCode
        displayName: Lint code
        steps:
          - script: |
              az bicep build --file deploy/main.bicep
            name: LintBicepCode
            displayName: Run Bicep linter
    
    - stage: Validate
      jobs:
      - job: ValidateBicepCode
        displayName: Validate Bicep code
        steps:
          - task: AzureResourceManagerTemplateDeployment@3
            name: RunPreflightValidation
            displayName: Run preflight validation
            inputs:
              connectedServiceName: $(ServiceConnectionName)
              location: $(deploymentDefaultLocation)
              deploymentMode: Validation
              resourceGroupName: $(ResourceGroupName)
              csmFile: deploy/main.bicep
              overrideParameters: >
                -environmentType $(EnvironmentType)
    
    - stage: Preview
      jobs:
      - job: PreviewAzureChanges
        displayName: Preview Azure changes
        steps:
          - task: AzureCLI@2
            name: RunWhatIf
            displayName: Run what-if
            inputs:
              azureSubscription: $(ServiceConnectionName)
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                az deployment group what-if \
                  --resource-group $(ResourceGroupName) \
                  --template-file deploy/main.bicep \
                  --parameters environmentType=$(EnvironmentType)
    
    - stage: Deploy
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: Website
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: $(ServiceConnectionName)
                    deploymentName: $(Build.BuildNumber)
                    location: $(deploymentDefaultLocation)
                    resourceGroupName: $(ResourceGroupName)
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType $(EnvironmentType)
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
                  name: SaveDeploymentOutputs
                  displayName: Save deployment outputs into variables
                  env:
                    DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    
    - stage: SmokeTest
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy.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 plik nie wygląda tak samo, zaktualizuj go, aby był zgodny z tym przykładem, a następnie zapisz go.

  2. Zatwierdź i wypchnij zmiany do repozytorium Git, uruchamiając następujące polecenia w terminalu programu Visual Studio Code:

    git add .
    git commit -m "Add test stage"
    git push
    

Uruchamianie potoku i przeglądanie wyniku testu

  1. W usłudze Azure DevOps przejdź do potoku.

  2. Wybierz najnowszy przebieg potoku.

    Poczekaj, aż pipeline zakończy etapy Lint, Validate i Preview. Chociaż usługa Azure Pipelines automatycznie aktualizuje stronę o najnowszym stanie, warto odświeżyć stronę od czasu do czasu.

  3. Wybierz przycisk Przejrzyj , a następnie wybierz pozycję Zatwierdź.

    Poczekaj na zakończenie przebiegu potoku.

  4. Zwróć uwagę, że etap Deploy (Wdrażanie ) kończy się pomyślnie. Etap SmokeTest kończy się błędem.

    Zrzut ekranu przedstawiający interfejs usługi Azure DevOps przedstawiający etapy uruchamiania potoku. Etap SmokeTest zgłasza błąd.

  5. Wybierz kartę Testy .

    Zrzut ekranu interfejsu usługi Azure DevOps pokazujący przebieg potoku. Karta Testy jest wyróżniona.

  6. Zwróć uwagę, że podsumowanie testu pokazuje, że uruchomiono dwa testy. Jeden przeszedł, a jeden nie powiódł się. Test, który zakończył się niepowodzeniem, jest wyświetlany jako witryna internetowa toy.Nie obsługuje stron za pośrednictwem protokołu HTTP.

    Zrzut ekranu przedstawiający interfejs usługi Azure DevOps przedstawiający wyniki testu przebiegu potoku. Test, który zakończył się niepowodzeniem, został wyróżniony.

    Ten tekst wskazuje, że witryna internetowa nie jest poprawnie skonfigurowana w celu spełnienia wymagań zespołu ds. zabezpieczeń.

Aktualizowanie pliku Bicep

Teraz, gdy wiesz, że definicja Bicep nie spełnia wymagań twojego zespołu ds. zabezpieczeń, naprawisz ją.

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

  2. Znajdź definicję aplikacji usługi Azure App Service i zaktualizuj ją, aby uwzględnić httpsOnly właściwość w jej properties sekcji.

    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
            }
          ]
        }
      }
    }
    
  3. Zapisz plik.

  4. Zatwierdź i wypchnij zmiany do repozytorium Git, uruchamiając następujące polecenia w terminalu programu Visual Studio Code:

    git add .
    git commit -m "Configure HTTPS on website"
    git push
    

Ponownie uruchom potok

  1. W przeglądarce przejdź do potoku.

  2. Wybierz najnowszy przebieg.

    Poczekaj, aż pipeline zakończy etapy Lint, Validate i Preview. Chociaż usługa Azure Pipelines automatycznie aktualizuje stronę o najnowszym stanie, warto odświeżyć stronę od czasu do czasu.

  3. Wybierz etap Podgląd i ponownie przejrzyj wyniki analizy co-jeżeli.

    Zwróć uwagę, że polecenie analizy co-jeżeli wykryło zmianę wartości httpsOnly właściwości:

    Resource and property changes are indicated with these symbols:
      + Create
      ~ Modify
      = Nochange
    
    The deployment will update the following scope:
    
    Scope: /subscriptions/aaaa0a0a-bb1b-cc2c-dd3d-eeeeee4e4e4e/resourceGroups/ToyWebsiteTest
    
      ~ Microsoft.Web/sites/toy-website-nbfnedv766snk [2021-01-15]
        + properties.siteConfig.localMySqlEnabled:   false
        + properties.siteConfig.netFrameworkVersion: "v4.6"
        ~ properties.httpsOnly:                      false => true
    
      = Microsoft.Insights/components/toywebsite [2020-02-02]
      = Microsoft.Storage/storageAccounts/mystoragenbfnedv766snk [2021-04-01]
      = Microsoft.Web/serverfarms/toy-website [2021-01-15]
    
    Resource changes: 1 to modify, 3 no change.
    
  4. Wróć do uruchomienia potoku.

  5. Wybierz przycisk Przejrzyj , a następnie wybierz pozycję Zatwierdź.

    Poczekaj na zakończenie przebiegu potoku.

  6. Zwróć uwagę, że cały proces przetwarzania został pomyślnie zakończony, w tym także etap SmokeTest. Ten wynik wskazuje, że oba testy zakończyły się pomyślnie.

    Zrzut ekranu interfejsu Azure DevOps pokazujący pomyślne uruchomienie potoku.

Oczyszczanie zasobów

Po ukończeniu ćwiczenia możesz usunąć zasoby, aby nie były naliczane opłaty.

W terminalu programu Visual Studio Code uruchom następujące polecenie:

az group delete --resource-group ToyWebsiteTest --yes --no-wait

Grupa zasobów jest usuwana w tle.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force