Ć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 weryfikacyjny kompilacji sprawdzający wymagania zespołu ds. zabezpieczeń.

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

W tym miejscu 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 wdrażania o nazwie Website.Tests.ps1.

    Zrzut ekranu eksploratora programu Visual Studio Code z wyświetlonym folderem deploy i plikiem testowym.

  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 jest ważne, aby zrozumieć szczegóły pliku testowego i sposób jego działania. W podsumowaniu udostępnimy linki, aby dowiedzieć się więcej, jeśli cię interesują.

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:

    - 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
    

    Teraz proces wdrażania nadal używa tego samego zadania, co wcześniej, ale dane wyjściowe z wdrożeń są 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:

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

    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. Następnie wartość jest publikowana w zmiennej wyjściowej etapu o takiej samej nazwie jak dane wyjściowe 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 weryfikacyjnego kompilacji, który uruchamia testy.

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

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

    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 :

    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
    

    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 :

    - task: PublishTestResults@2
      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. Wkrótce zobaczysz, jak są wyświetlane wyniki.

    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 test kończący się niepowodzeniem spowoduje niepowodzenie kroku testu, a zwykle potok przestaje działać po kroku niepowodzenia.

  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:
      vmImage: ubuntu-latest
    
    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 tak nie jest, zaktualizuj go tak, 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 przeglądarce przejdź do potoku.

  2. Wybierz najnowszy przebieg potoku.

    Poczekaj, aż potok 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. Niepowodzenie etapu SmokeTest.

  5. Wybierz kartę Testy .

    Zrzut ekranu przedstawiający interfejs usługi Azure DevOps przedstawiający przebieg potoku z wyróżnioną kartą Testy.

  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 z wyróżnionym testem, który zakończył się niepowodzeniem.

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

Aktualizowanie pliku Bicep

Po zidentyfikowaniu, że definicja Bicep nie spełnia wymagań 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 aplikacja systemu Azure Service i zaktualizuj ją, aby uwzględnić httpsOnly właściwość w swoim properties obszarze:

    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ż potok 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/f0750bbe-ea75-4ae5-b24d-a92ca601da2c/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 potok zostanie pomyślnie zakończony, w tym etap SmokeTest . Ten sukces wskazuje, że oba testy zakończyły się pomyślnie.

    Zrzut ekranu przedstawiający interfejs usługi Azure DevOps przedstawiają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