Ü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, um einen Rauchtest auszuführen, der überprüft, ob die Anforderung des Sicherheitsteams erfüllt ist.

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

Zuerst fügen Sie ein Testskript hinzu, um zu überprüfen, ob auf die Website zugegriffen werden kann, wenn HTTPS verwendet wird und nicht darauf zugegriffen werden kann, wenn das nicht unsichere HTTP-Protokoll verwendet wird.

  1. Erstellen Sie in Visual Studio Code eine neue Datei im Bereitstellungsordner . Benennen Sie die Datei Website.Tests.ps1.

    Screenshot des Visual Studio Code-Explorers. Der Bereitstellungsordner und die Testdatei werden angezeigt.

  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 besteht, wenn der Server mit einem HTTP-Antwortstatuscode antwortet, der zwischen 200 und 299 liegt und eine erfolgreiche Verbindung anzeigt.
    • 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 müssen Sie die Details der Testdatei und deren Funktionsweise nicht verstehen. Wenn Sie interessiert sind, können Sie mehr erfahren, indem Sie sich die in der Modulzusammenfassung aufgeführte Ressource auschecken.

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

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

    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 jetzt 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:

      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. Der Wert wird anschließend 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

Sie können jetzt eine Smoke-Test-Phase hinzufügen, in der Ihre Tests ausgeführt werden.

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

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

    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:

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

    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 wichtig, da jeder fehlgeschlagene Test dazu führt, dass der Testschritt fehlschlägt, und normalerweise wird die Pipeline nach einem fehlgeschlagenen Schritt beendet.

  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: 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'
    

    Wenn Die Datei nicht gleich aussieht, aktualisieren Sie sie entsprechend diesem Beispiel, und speichern Sie sie dann.

  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 Azure DevOps 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 neuesten Status aktualisiert, ist es ratsam, die Seite gelegentlich zu 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 der Azure DevOps-Schnittstelle, die die Pipelineausführungsphasen zeigt. Die SmokeTest-Phase meldet einen Fehler.

  5. Wählen Sie die Registerkarte Tests aus.

    Screenshot der Azure DevOps-Schnittstelle, die die Pipelineausführung zeigt. Die Registerkarte

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

    Screenshot der Azure DevOps-Schnittstelle, die die Testergebnisse der Pipelineausführung zeigt. Der fehlgeschlagene Test ist hervorgehoben.

    Dieser Text gibt an, dass die Website nicht ordnungsgemäß konfiguriert ist, um die Anforderung 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 deploy.

  2. Suchen Sie die Definition für die Azure App Service-App, und aktualisieren Sie sie so, dass sie die httpsOnly Eigenschaft in den properties Abschnitt einschließt:

    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 neuesten Status aktualisiert, ist es ratsam, die Seite gelegentlich zu 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/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. Wechseln Sie zurück zur Ausführung der Pipeline.

  5. Wählen Sie die Schaltfläche " Überprüfen" und dann " Genehmigen" aus.

    Warten Sie, bis die Pipelineausführung abgeschlossen ist.

  6. Sie sehen, dass die gesamte Pipeline erfolgreich abgeschlossen wurde, einschließlich der Phase SmokeTest. Dieses Ergebnis gibt an, dass beide Tests bestanden wurden.

    Screenshot der Azure DevOps-Schnittstelle, die eine erfolgreiche Pipelineausführung zeigt.

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