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 comprobación de la compilación que compruebe los requisitos 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

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

  1. En Visual Studio Code, cree un archivo denominado Website.Tests.ps1 en la carpeta deploy.

    Captura de pantalla del explorador de Visual Studio Code, en el que se muestran la carpeta de implementación 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 se supera si el servidor responde con un código de estado de respuesta HTTP entre 200 y 299, lo 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 importante que comprenda los detalles del archivo de prueba y cómo funciona. En el resumen se proporcionarán vínculos para que pueda obtener más información si está interesado.

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 Implementación, actualice el paso de implementación para publicar las salidas en una 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
    

    Ahora el proceso de implementación sigue usando la misma tarea que antes, pero las salidas de las implementaciones 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:

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

    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. A continuación, 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 comprobación de la compilación que ejecute las pruebas.

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

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

    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, agregue la definición de paso siguiente a la fase 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
    

    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, agregue la definición de paso siguiente a la fase SmokeTest:

    - task: PublishTestResults@2
      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 en breve.

    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 errores provocará un error en el paso de prueba y normalmente la canalización deja de ejecutarse después de un paso con errores.

  4. Guarde el archivo.

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

  1. Compruebe que el archivo azure-pipelines.yml es similar al código siguiente:

    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 no es así, actualícelo para que coincida con este ejemplo y después 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 el explorador, 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 (Validación) y Preview (Vista previa). Aunque Azure Pipelines actualiza de forma automática la página con el estado más reciente, es recomendable actualizarla ocasionalmente.

  3. Seleccione el botón Review (Revisar) y después Approve (Aprobar).

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

  4. Observe que la fase Deploy (Implementación) finaliza correctamente. La fase SmokeTest finaliza con un error.

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

  5. Seleccione la pestaña Pruebas.

    Captura de pantalla de la interfaz de Azure DevOps en la que se muestra la ejecución de canalización, con la pestaña Pruebas 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 (Toy Website. No proporciona páginas sobre HTTP).

    Captura de pantalla de la interfaz de Azure DevOps en la que se muestran los resultados de la prueba de ejecución de canalización, con la prueba con errores resaltada.

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

Actualización del archivo de Bicep

Ahora que ha identificado 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 de la carpeta deploy.

  2. Busque la definición de la aplicación de Azure App Service y actualícela para incluir la propiedad httpsOnly en su área 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. 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 (Validación) y Preview (Vista previa). Aunque Azure Pipelines actualiza de forma automática la página con el estado más reciente, es recomendable actualizarla 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/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. Vuelva a la ejecución de canalización.

  5. Seleccione el botón Revisar y después 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 éxito indica que ambas pruebas se han superado.

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

Limpiar 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