Exercice - Ajouter une phase de test à votre pipeline

Effectué

L’équipe de sécurité de votre entreprise de jouets vous a demandé de vérifier que votre site web est accessible uniquement via le protocole HTTPS. Dans cet exercice, vous allez configurer votre pipeline pour exécuter un test de détection de fumée qui vérifie l’exigence de l’équipe de sécurité.

Pendant ce processus, vous allez :

  • Ajouter un script de test à votre dépôt.
  • Mettre à jour votre définition de pipeline pour y ajouter une phase de test.
  • Exécuter le pipeline et observer l’échec du test.
  • Corriger le fichier Bicep et observer la réussite de l’exécution du pipeline.

Ajouter un script de test

Ici, vous ajouterez un script de test pour vérifier que le site web est accessible quand le protocole HTTPS est utilisé et qu’il n’est pas accessible quand le protocole HTTP non sécurisé est utilisé.

  1. Dans Visual Studio Code, créez un fichier dans le dossier deploy, nommé Website.Tests.ps1.

    Screenshot of Visual Studio Code Explorer, with the deploy folder and the test file shown.

  2. Collez le code de test suivant dans le fichier :

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

    Ce code est un fichier de test Pester. Il nécessite un paramètre nommé $HostName. Il exécute deux tests sur le nom d’hôte :

    • Essayer de se connecter au site web via HTTPS. Le test réussit si le serveur répond avec un code d’état de réponse HTTP compris entre 200 et 299, ce qui indique une connexion réussie.
    • Essayer de se connecter au site web via HTTP. Le test réussit si le serveur répond avec un code d’état de réponse HTTP de 300 ou plus.

    Dans le cadre de cet exercice, il n’est pas important de comprendre les détails du fichier de test et son fonctionnement. Nous proposerons des liens dans le résumé pour vous permettre d’en savoir plus si vous êtes intéressé.

Publier la sortie de votre fichier Bicep comme variable de sortie de phase

Le script de test que vous avez créé dans les étapes précédentes a besoin d’un nom d’hôte à tester. Votre fichier Bicep contient déjà une sortie, mais avant de pouvoir l’utiliser dans vos tests de détection de fumée, vous devez le publier en tant que variable de sortie de phase.

  1. Dans Visual Studio Code, ouvrez le fichier azure-pipelines.yml dans le dossier deploy.

  2. Dans l’étape Déploiement, mettez à jour l’étape de déploiement pour publier les sorties dans une variable :

    - 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
    

    À présent, votre processus de déploiement utilise toujours la même tâche que précédemment, mais les sorties des déploiements sont stockées dans une variable de pipeline nommée deploymentOutputs. La variable de sortie est mise en forme au format JSON.

  3. Pour convertir les sorties au format JSON en variables de pipeline, ajoutez l’étape de script suivante sous l’étape de déploiement :

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

    Une fois le déploiement réussi, le script accède à la valeur de chaque sortie du déploiement Bicep. Le script utilise l’outil jq pour accéder à la partie pertinente de la sortie JSON. Ensuite, la valeur est publiée dans une variable de sortie d’une phase portant le même nom que la sortie du déploiement Bicep.

    Notes

    Pester et jq sont tous deux préinstallés sur les agents hébergés par Microsoft pour Azure Pipelines. Vous n’avez rien de spécial à faire pour les utiliser dans une étape de script.

  4. Enregistrez le fichier .

Ajouter une phase de test de détection de fumée à votre pipeline

Vous pouvez maintenant ajouter une phase de test de détection de fumée qui exécute vos tests.

  1. Dans le bas du fichier, ajoutez la définition suivante pour la phase SmokeTest :

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

    Ce code définit la phase et un travail. Il crée également une variable dans le travail nommé appServiceAppHostName. Cette variable prend sa valeur de la variable de sortie que vous avez créée dans la section précédente.

  2. Dans le bas du fichier, ajoutez la définition d’étape suivante à la phase 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
    

    Cette étape exécute un script PowerShell pour exécuter le script de test que vous avez écrit précédemment en utilisant l’outil de test Pester.

  3. Dans le bas du fichier, ajoutez la définition d’étape suivante à la phase SmokeTest :

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

    Cette étape prend le fichier de résultats des tests que Pester crée et publie en tant que résultats des tests du pipeline. Vous verrez comment les résultats sont affichés sous peu.

    Notez que la définition de l’étape inclut condition: always(). Cette condition indique à Azure Pipelines qu’il doit toujours publier les résultats des tests, même si l’étape précédente échoue. Cette condition importante, car un test qui échoue va entraîner l’échec de l’étape de test et normalement, le pipeline cesse de s’exécuter après une étape qui a échoué.

  4. Enregistrez le fichier .

Vérifier et commiter votre définition de pipeline

  1. Vérifiez que votre fichier azure-pipelines.yml est similaire au code ci-dessous :

    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'
    

    Si ce n’est pas le cas, modifiez-le d’après cet exemple, puis enregistrez-le.

  2. Commitez et poussez (push) vos modifications à votre dépôt Git en exécutant les commandes suivantes dans le terminal Visual Studio Code :

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

Exécuter le pipeline et passer en revue le résultat des tests

  1. Dans votre navigateur, accédez à votre pipeline.

  2. Sélectionnez l’exécution la plus récente de votre pipeline.

    Attendez que le pipeline termine les étapes Vérification lint, Validation et Prévisualisation. Azure Pipelines met automatiquement à jour la page avec l’état le plus récent, mais il est toutefois conseillé d’actualiser régulièrement cette page.

  3. Sélectionnez Passer en revue, puis Approuver.

    Attendez la fin de l’exécution du pipeline.

  4. Notez que l’exécution de la phase Deploy s’est terminée avec succès. En revanche, la phase SmokeTest s’est terminée avec une erreur.

    Screenshot of the Azure DevOps interface that shows the pipeline run stages. The SmokeTest stage reports failure.

  5. Sélectionnez l’onglet Tests.

    Screenshot of the Azure DevOps interface that shows the pipeline run, with the Tests tab highlighted.

  6. Notez que le récapitulatif des tests indique que deux tests ont été exécutés. L’un a réussi et l’autre a échoué. Le test qui a échoué apparaît comme ceci : Toy Website.Does not serve pages over HTTP (Site web Toy. Ne délivre pas de pages via HTTP).

    Screenshot of the Azure DevOps interface that shows the pipeline run's test results, with the failed test highlighted.

    Ce texte indique que le site web n’a pas été configuré correctement pour satisfaire à l’exigence de votre équipe de sécurité.

Mettre à jour le fichier Bicep

Maintenant que vous avez identifié que votre définition Bicep ne répond pas à l’exigence de votre équipe de sécurité, vous allez la corriger.

  1. Dans Visual Studio Code, ouvrez le fichier main.bicep dans le dossier deploy.

  2. Recherchez la définition de l’application Azure App Service et mettez-la à jour en incluant la propriété httpsOnly dans sa section properties :

    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. Enregistrez le fichier .

  4. Commitez et poussez (push) vos modifications à votre dépôt Git en exécutant les commandes suivantes dans le terminal Visual Studio Code :

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

Réexécuter le pipeline

  1. Dans votre navigateur, accédez à votre pipeline.

  2. Sélectionnez la dernière exécution.

    Attendez que le pipeline termine les étapes Vérification lint, Validation et Prévisualisation. Azure Pipelines met automatiquement à jour la page avec l’état le plus récent, mais il est toutefois conseillé d’actualiser régulièrement cette page.

  3. Sélectionnez la phase Preview, puis examinez à nouveau les résultats de la simulation.

    Notez que la commande de simulation a détecté la modification de la valeur de la propriété httpsOnly :

    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. Revenez à l’exécution du pipeline.

  5. Sélectionnez Passer en revue, puis Approuver.

    Attendez la fin de l’exécution du pipeline.

  6. Notez que l’intégralité du pipeline s’est terminée avec succès, y compris la phase SmokeTest. Ce succès indique que les deux tests ont réussi.

    Screenshot of the Azure DevOps interface that shows a successful pipeline run.

Nettoyer les ressources

Maintenant que vous avez terminé l’exercice, vous pouvez supprimer les ressources afin de ne pas avoir à payer pour.

Dans le terminal Visual Studio Code, exécutez la commande suivante :

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

Le groupe de ressources est supprimé en arrière-plan.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force