Ejercicio: Adición de una fase de prueba a la canalización

Completado

El equipo de seguridad de la empresa de juguetes le ha pedido que compruebe que el sitio web solo es accesible mediante HTTPS. En este ejercicio, configurará la canalización para ejecutar una prueba de humo que compruebe si se cumple el requisito del equipo de seguridad.

Durante el proceso, hará lo siguiente:

  • Agregar un script de prueba al repositorio.
  • Actualizar la definición de canalización para agregar una fase de prueba.
  • Ejecutar la canalización y observar el error de prueba.
  • Corregir el archivo de Bicep y observar que la canalización se ejecuta correctamente.

Adición de un script de prueba

En primer lugar, agregará un script de prueba para comprobar que el sitio web es accesible cuando se usa HTTPS y no es accesible cuando se usa el protocolo HTTP no seguro.

  1. En Visual Studio Code, cree un nuevo archivo en la carpeta deploy . Asigne al archivo el nombreWebsite.Tests.ps1.

    Captura de pantalla del Explorador de Visual Studio Code. Se muestran la carpeta deploy y el archivo de prueba.

  2. Pegue el código de prueba siguiente en el archivo:

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

    Es código es un archivo de prueba de Pester. Requiere un parámetro denominado $HostName. Ejecuta dos pruebas en el nombre de host:

    • Intente conectarse al sitio web mediante HTTPS. La prueba pasa si el servidor responde con un código de estado de respuesta HTTP comprendido entre 200 y 299, que indica una conexión correcta.
    • Intente conectarse al sitio web mediante HTTP. La prueba se supera si el servidor responde con un código de estado de respuesta HTTP de 300 o superior.

    Para los fines de este ejercicio, no es necesario comprender los detalles del archivo de prueba y cómo funciona. Si le interesa, puede obtener más información comprobando el recurso que aparece en el resumen del módulo.

Publicación de la salida del archivo de Bicep como una variable de salida de fase

En el script de prueba que ha creado en los pasos anteriores es necesario probar un nombre de host. El archivo de Bicep ya incluye una salida, pero, antes de poder usarla en las pruebas de comprobación de la compilación, debe publicarla como una variable de salida de fase.

  1. En Visual Studio Code, abra el archivo azure-pipelines.yml en la carpeta deploy .

  2. En la fase Implementar , actualice el paso de implementación para publicar las salidas en una variable:

    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
    

    El proceso de implementación sigue usando la misma tarea que anteriormente, pero las salidas de las implementaciones ahora se almacenan en una variable de canalización denominada deploymentOutputs. La variable de salida tiene el formato JSON.

  3. Para convertir las salidas con formato JSON en variables de canalización, agregue el siguiente paso de script debajo del paso de implementación:

      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)
    

    Si la implementación se completa correctamente, el script accede al valor de cada salida de la implementación de Bicep. El script usa la herramienta jq a fin de acceder a la parte pertinente de la salida JSON. Después, el valor se publica en una variable de salida de la fase con el mismo nombre que la salida de implementación de Bicep.

    Nota

    Pester y jq están preinstalados en agentes hospedados por Microsoft para Azure Pipelines. No es necesario hacer nada especial para usarlos en un paso de script.

  4. Guarde el archivo.

Adición de una fase de prueba de comprobación de la compilación a la canalización

Ahora, puede agregar una fase de prueba de humo que ejecute las pruebas.

  1. En la parte inferior del archivo, agregue la siguiente definición para la fase SmokeTest :

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

    Este código define la fase y un trabajo. También crea una variable denominada appServiceAppHostName en el trabajo. Esta variable toma su valor de la variable de salida que ha creado en la sección anterior.

  2. En la parte inferior del archivo, añada la siguiente definición de paso a la etapa 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
    

    Este paso ejecuta un script de PowerShell para ejecutar el script de prueba que ha escrito antes mediante la herramienta de pruebas Pester.

  3. En la parte inferior del archivo, añada la siguiente definición de paso a la etapa SmokeTest:

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

    Este paso toma el archivo de resultados de la prueba que crea Pester y lo publica como los resultados de la prueba de canalización. Verá cómo se muestran los resultados pronto.

    Observe que la definición del paso incluye condition: always(). Esta condición indica a Azure Pipelines que siempre debe publicar los resultados de la prueba, incluso si se produce un error en el paso anterior. Esta condición es importante porque cualquier prueba con error hará que se produzca un error en el paso de prueba y, normalmente, la canalización deja de ejecutarse después de un paso con error.

  4. Guarde el archivo.

Comprobación y confirmación de la definición de canalización

  1. Compruebe que el archivo azure-pipelines.yml tiene el siguiente aspecto:

    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'
    

    Si el archivo no tiene el mismo aspecto, actualícelo para que coincida con este ejemplo y guárdelo.

  2. Confirme e inserte los cambios en el repositorio de Git mediante la ejecución de los comandos siguientes en el terminal de Visual Studio Code:

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

Ejecución de la canalización y revisión del resultado de la prueba

  1. En Azure DevOps, vaya a la canalización.

  2. Seleccione la ejecución más reciente de la canalización.

    Espere hasta que la canalización complete las fases Lint, Validate y Preview . Aunque Azure Pipelines actualiza automáticamente la página con el estado más reciente, es recomendable actualizar la página ocasionalmente.

  3. Seleccione el botón Revisar y, a continuación, seleccione Aprobar.

    Espere a que finalice la ejecución de la canalización.

  4. Observe que la fase Implementar finaliza correctamente. La fase SmokeTest finaliza con un error.

    Recorte de pantalla de la interfaz de Azure DevOps en el que se muestra las fases de ejecución de la canalización. La fase SmokeTest notifica un error.

  5. Seleccione la pestaña Pruebas .

    Captura de pantalla de la interfaz de Azure DevOps que muestra la ejecución de la canalización. La pestaña Pruebas está resaltada.

  6. Observe que en el resumen de la prueba se muestra que se han ejecutado dos pruebas. La primera se realizó correctamente y la otra produjo un error. La prueba con error aparece como Toy Website.Does not serve pages over HTTP.

    Captura de pantalla de la interfaz de Azure DevOps que muestra los resultados de las pruebas de la ejecución del flujo de trabajo. La prueba con errores está destacada.

    Este texto indica que el sitio web no está configurado correctamente para satisfacer los requisitos del equipo de seguridad.

Actualización del archivo de Bicep

Ahora que ha sabe que la definición de Bicep no cumple los requisitos del equipo de seguridad, lo corregirá.

  1. En Visual Studio Code, abra el archivo main.bicep en la carpeta deploy .

  2. Busque la definición de la aplicación de Azure App Service y actualícela para incluir la httpsOnly propiedad en su properties sección:

    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. Guarde el archivo.

  4. Confirme e inserte los cambios en el repositorio de Git mediante la ejecución de los comandos siguientes en el terminal de Visual Studio Code:

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

Repetición de la ejecución de la canalización

  1. En el explorador, vaya a la canalización.

  2. Seleccione la ejecución más reciente.

    Espere hasta que la canalización complete las fases Lint, Validate y Preview . Aunque Azure Pipelines actualiza automáticamente la página con el estado más reciente, es recomendable actualizar la página ocasionalmente.

  3. Seleccione la fase Vista previa y vuelva a revisar los resultados hipotéticos.

    Observe que el comando hipotético ha detectado el cambio en el valor de la propiedad httpsOnly:

    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. Vuelva a la ejecución de canalización.

  5. Seleccione el botón Revisar y, a continuación, seleccione Aprobar.

    Espere a que finalice la ejecución de la canalización.

  6. Observe que toda la canalización finaliza correctamente, incluida la fase SmokeTest. Este resultado indica que se han superado ambas pruebas.

    Captura de pantalla de la interfaz de Azure DevOps que muestra una ejecución de canalización correcta.

Limpieza de los recursos

Ahora que ha completado el ejercicio, puede quitar los recursos para que no se le facturen.

En el terminal de Visual Studio Code, ejecute el comando siguiente:

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

El grupo de recursos se elimina en segundo plano.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force