Übung: Hinzufügen einer Testphase zu Ihrer Pipeline

Abgeschlossen

Das Sicherheitsteam Ihres Spielzeugunternehmens hat Sie gebeten, zu überprüfen, ob auf Ihre Website nur über HTTPS zugegriffen werden kann. In dieser Übung konfigurieren Sie Ihre Pipeline für die Ausführung einer Feuerprobe, welche die Anforderungen des Sicherheitsteams überprüft.

In dem Prozess gehen Sie wie folgt vor:

  • Fügen Sie ein Testskript zu Ihrer Repository hinzu.
  • Aktualisieren Sie Ihre Pipelinedefinition, um eine Testphase hinzuzufügen.
  • Führen Sie die Pipeline aus, und beobachten Sie, ob der Test fehlschlägt.
  • Korrigieren Sie die Bicep-Datei, und beobachten Sie, ob die Pipeline erfolgreich ausgeführt wurde.

Hinzufügen eines Testskripts

Hier fügen Sie ein Testskript hinzu, um zu überprüfen, ob die Website aufgerufen werden kann, wenn HTTPS verwendet wird, und nicht aufgerufen werden kann, wenn das weniger sichere HTTP verwendet wird.

  1. Erstellen Sie in Visual Studio Code eine neue Datei im Ordner Bereitstellen mit dem Namen Website.Tests.ps1.

    Screenshot: Visual Studio Code-Explorer mit Ordner „deploy“ und der Testdatei

  2. Fügen Sie den folgenden Code in die Datei ein:

    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"
        }
    
    }
    

    Dieser Code ist eine Pester-Testdatei. Hierfür ist ein Parameter mit dem Namen $HostName erforderlich. Es werden zwei Tests für den Hostnamen ausgeführt:

    • Versuchen Sie, über HTTPS eine Verbindung mit der Website herzustellen. Der Test ist erfolgreich, wenn der Server mit einem HTTP-Antwortstatuscode zwischen 200 und 299 antwortet, was auf eine erfolgreiche Verbindung hinweist.
    • Versuchen Sie, über HTTP eine Verbindung mit der Website herzustellen. Der Test ist erfolgreich, wenn der Server mit dem HTTP-Antwortstatuscode 300 oder höher antwortet.

    Für diese Übung ist es nicht wichtig, dass Sie die Details der Testdatei und ihre Funktionsweise verstehen. Über die Links in der Zusammenfassung erhalten Sie weitere Informationen.

Veröffentlichung der Ausgabe Ihrer Bicep-Datei als Phasenausgabevariable

Für das Testskript, das Sie in den vorherigen Schritten erstellt haben, muss ein Hostname getestet werden. Ihre Bicep-Datei enthält bereits eine Ausgabe, aber bevor Sie sie in Ihren Feuerproben verwenden können, müssen Sie sie als Ausgabevariable für die Phase veröffentlichen.

  1. Öffnen Sie in Visual Studio Code die Datei azure-pipelines.yml im Ordner Bereitstellen.

  2. Aktualisieren Sie in der Stage Bereitstellen den Bereitstellungsschritt, um die Ausgaben in einer Variable zu veröffentlichen:

    - 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
    

    Ihr Bereitstellungsprozess verwendet dieselben Aufgaben wie zuvor, aber die Ausgaben der Bereitstellungen werden in einer Pipelinevariable namens deploymentOutputs gespeichert. Die Ausgabevariable ist als JSON formatiert.

  3. Um die JSON-formatierten Ausgaben in Pipelinevariablen zu konvertieren, fügen Sie den folgenden Skriptschritt unterhalb des Bereitstellungsschritts hinzu:

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

    Wenn die Bereitstellung erfolgreich abgeschlossen wurde, greift das Skript auf den Wert jeder Ausgabe aus der Bicep-Bereitstellung zu. Das Skript verwendet das Tool jq, um auf den relevanten Teil der JSON-Ausgabe zuzugreifen. Anschließend wird der Wert in einer Stageausgabevariable mit demselben Namen wie die Bicep-Bereitstellungsausgabe veröffentlicht.

    Hinweis

    Pester und jq sind auf von Microsoft gehosteten Agents für Azure Pipelines vorinstalliert. Sie müssen keine besonderen Vorkehrungen treffen, um diese in einem Skriptschritt zu verwenden.

  4. Speichern Sie die Datei .

Hinzufügen einer Feuerprobenphase zu Ihrer Pipeline

Nun können Sie eine Feuerprobenphase hinzufügen, in der Ihre Tests ausgeführt werden.

  1. Fügen Sie am Ende der Datei die folgende Definition für Feuerprobe hinzu:

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

    Dieser Code definiert die Phase und einen Auftrag. Außerdem wird im Auftrag eine Variable mit dem Namen appServiceAppHostName erstellt. Diese Variable erhält ihren Wert aus der Ausgabevariablen, die Sie im vorherigen Abschnitt erstellt haben.

  2. Fügen Sie am Ende der Datei die folgende Schrittdefinition für die Phase Feuerprobe hinzu:

    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
    

    In diesem Schritt wird ein PowerShell-Skript ausgeführt, um das oben erstellte Testskript mit dem Testtool Pester auszuführen.

  3. Fügen Sie am Ende der Datei die folgende Schrittdefinition für die Phase Feuerprobe hinzu:

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

    In diesem Schritt wird die von Pester erstellte Testergebnisdatei als Pipeline-Testergebnis veröffentlicht. Sie sehen, wie die Ergebnisse in Kürze angezeigt werden.

    Beachten Sie, dass die Schrittdefinition condition: always() enthält. Mit dieser Bedingung wird Azure Pipelines mitgeteilt, dass die Testergebnisse immer veröffentlicht werden sollen, auch wenn der vorherige Schritt fehlschlägt. Diese Bedingung ist besonders wichtig, da jeder fehlgeschlagene Test dazu führt, dass der Testschritt scheitert, und die Pipeline normalerweise nach einem fehlgeschlagenen Schritt nicht mehr ausgeführt wird.

  4. Speichern Sie die Datei .

Überprüfen und Committen Ihrer Pipelinedefinition

  1. Vergewissern Sie sich, dass die Datei azure-pipelines.yml wie folgt aussieht:

    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'
    

    Falls nicht, aktualisieren Sie sie entsprechend diesem Beispiel, und speichern Sie sie.

  2. Committen und pushen Sie Ihre Änderungen in Ihr Git-Repository, indem Sie die folgenden Befehle im Visual Studio Code-Terminal ausführen:

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

Ausführen der Pipeline und Überprüfen des Testergebnisses

  1. Wechseln Sie in Ihrem Browser zu Ihrer Pipeline.

  2. Wählen Sie die letzte Ausführung Ihrer Pipeline aus.

    Warten Sie, bis die Pipeline die Phasen Lint, Überprüfen und Vorschau abgeschlossen hat. Obwohl Azure Pipelines die Seite automatisch mit dem aktuellen Status aktualisiert, sollten Sie die Seite gelegentlich auch manuell aktualisieren.

  3. Wählen Sie die Schaltfläche Bewertung und dann Genehmigen aus.

    Warten Sie, bis die Pipelineausführung abgeschlossen ist.

  4. Sie sehen, dass die Phase Deploy erfolgreich abgeschlossen wurde. Die Phase SmokeTest wird mit einem Fehler abgeschlossen.

    Screenshot: Azure DevOps-Benutzeroberfläche mit den Pipeline-Ausführungsphasen. Die SmokeTest-Phase meldet einen Fehler.

  5. Wählen Sie die Registerkarte Tests aus.

    Screenshot: Azure DevOps-Benutzeroberfläche mit der Pipelineausführung, wobei die Registerkarte „Tests“ hervorgehoben ist

  6. Die Testzusammenfassung gibt an, dass zwei Tests ausgeführt wurden. Einer war erfolgreich, und einer ist fehlgeschlagen. Der fehlgeschlagene Test wird als Toy Website.Does not serve pages over HTTP aufgeführt.

    Screenshot: Azure DevOps-Benutzeroberfläche mit den Ergebnissen aus der Ausführung der Pipelinetests, wobei der fehlgeschlagene Test hervorgehoben ist

    Dieser Text weist darauf hin, dass die Website nicht ordnungsgemäß konfiguriert wurde, um die Anforderungen Ihres Sicherheitsteams zu erfüllen.

Update der Bicep-Datei

Nachdem Sie nun ermittelt haben, dass Ihre Bicep-Definition die Anforderungen Ihres Sicherheitsteams nicht erfüllt, werden Sie diese beheben.

  1. Öffnen Sie in Visual Studio Code die Datei main.bicep im Ordner Bereitstellen.

  2. Suchen Sie die Definition für die App Service-App, und aktualisieren Sie sie so, dass sie die Eigenschaft httpsOnly im Bereich properties enthält:

    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. Speichern Sie die Datei .

  4. Committen und pushen Sie Ihre Änderungen in Ihr Git-Repository, indem Sie die folgenden Befehle im Visual Studio Code-Terminal ausführen:

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

Erneute Ausführung der Pipeline

  1. Wechseln Sie in Ihrem Browser zu Ihrer Pipeline.

  2. Wählen Sie die letzte Ausführung aus.

    Warten Sie, bis die Pipeline die Phasen Lint, Überprüfen und Vorschau abgeschlossen hat. Obwohl Azure Pipelines die Seite automatisch mit dem aktuellen Status aktualisiert, sollten Sie die Seite gelegentlich auch manuell aktualisieren.

  3. Wählen Sie die Phase Vorschau aus, und überprüfen Sie die Was-wäre-wenn-Ergebnisse erneut.

    Beachten Sie, dass der What-If-Befehl die Änderung des Eigenschaftswerts httpsOnly erkannt hat:

    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. Wechseln Sie zurück zur Ausführung der Pipeline.

  5. Klicken Sie auf die Schaltfläche Bewertung und dann auf Genehmigen.

    Warten Sie, bis die Pipelineausführung abgeschlossen ist.

  6. Sie sehen, dass die gesamte Pipeline erfolgreich abgeschlossen wurde, einschließlich der Phase SmokeTest. Das bedeutet, dass beide Tests erfolgreich waren.

    Screenshot: Azure DevOps-Benutzeroberfläche mit erfolgreicher Pipelineausführung

Bereinigen der Ressourcen

Nachdem Sie die Übung abgeschlossen haben, können Sie die Ressourcen entfernen, damit Ihnen dafür keine Gebühren berechnet werden.

Führen Sie im Visual Studio Code-Terminal den folgenden Befehl aus:

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

Die Ressourcengruppe wird im Hintergrund gelöscht.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force