연습 - 워크플로에 테스트 작업 추가

완료됨

장난감 회사의 보안 팀에서 HTTPS를 통해서만 웹 사이트에 액세스할 수 있는지 확인해 달라고 여러분에게 요청합니다. 이 연습에서는 보안 팀의 요구 사항을 확인하는 스모크 테스트를 실행하도록 워크플로를 구성합니다.

프로세스 중에 다음 작업을 수행합니다.

  • 리포지토리에 테스트 스크립트를 추가합니다.
  • 테스트 작업을 추가하도록 워크플로 정의를 업데이트합니다.
  • 워크플로를 실행하고 테스트가 실패하는지 확인합니다.
  • Bicep 파일을 수정하고 워크플로가 성공적으로 실행되는지 확인합니다.

테스트 스크립트 추가

여기서는 HTTPS를 사용하면 웹 사이트에 액세스할 수 있고, 안전하지 않은 HTTP 프로토콜을 사용하면 액세스할 수 없는지 확인하는 테스트 스크립트를 추가합니다.

  1. Visual Studio Code에서 deploy 폴더에 Website.Tests.ps1이라는 새 파일을 만듭니다.

    deploy 폴더와 테스트 파일이 표시된 Visual Studio Code 탐색기의 스크린샷

  2. 다음 테스트 코드를 복사하여 파일에 붙여넣습니다.

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

    결과로 Pester 테스트 파일이 만들어집니다. $HostName이라는 매개 변수가 필요합니다. 호스트 이름에 대해 두 개의 테스트를 실행합니다.

    • HTTPS를 통해 웹 사이트에 연결해 봅니다. 서버가 연결 성공을 나타내는 200~299 사이의 HTTP 응답 상태 코드로 응답하면 테스트를 통과합니다.
    • HTTP를 통해 웹 사이트에 연결해 봅니다. 서버가 300 이상의 HTTP 응답 상태 코드로 응답하면 테스트를 통과합니다.

    이 연습에서는 테스트 파일의 세부 정보와 작동 방식을 꼭 이해할 필요는 없습니다. 관심이 있는 분들을 위해 요약 단원에 링크가 제공됩니다.

Bicep 파일의 출력을 작업 출력으로 게시

이전 단계에서 만든 테스트 스크립트에는 테스트할 호스트 이름이 필요합니다. Bicep 파일에는 이미 출력이 포함되어 있지만, 스모크 테스트에 사용하려면 출력을 작업 출력 변수로 게시해야 합니다.

  1. Visual Studio Code에서 .github/workflows 폴더의 workflow.yml 파일을 엽니다.

  2. 배포 작업에서 Deploy website 단계에 id를 추가하여 해당 단계를 참조할 수 있도록 합니다. 또한 배포 단계의 appServiceAppHostName 출력을 복사하는 작업 출력을 추가합니다.

    deploy:
      runs-on: ubuntu-latest
      environment: Website
      needs: preview
      outputs:
        appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
      steps:
      - uses: actions/checkout@v3
      - uses: azure/login@v1
        name: Sign in to Azure
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - uses: azure/arm-deploy@v1
        id: deploy
        name: Deploy website
        with:
          failOnStdErr: false
          deploymentName: ${{ github.run_number }}
          resourceGroupName: ${{ env.AZURE_RESOURCEGROUP_NAME }}
          template: ./deploy/main.bicep
          parameters: environmentType=${{ env.ENVIRONMENT_TYPE }}
    
  3. 파일을 저장합니다.

워크플로에 스모크 테스트 작업 추가

이제 테스트를 실행하는 스모크 테스트 작업을 추가할 수 있습니다.

  1. 파일 맨 아래에 smoke-test 작업에 대한 다음 정의를 추가합니다.

    smoke-test:
      runs-on: ubuntu-latest
      needs: deploy
      steps:
      - uses: actions/checkout@v3
      - run: |
          $container = New-PesterContainer `
            -Path 'deploy/Website.Tests.ps1' `
            -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' }
          Invoke-Pester `
            -Container $container `
            -CI
        name: Run smoke tests
        shell: pwsh
    

    이 코드는 작업을 정의합니다. 작업은 코드를 체크 아웃하는 단계와 Pester를 사용하여 테스트를 실행하는 단계를 포함합니다.

    작업 정의는 needs 속성을 사용하여 배포 작업에 대한 종속성을 정의합니다. 이 종속성에 따라 작업이 원하는 순서대로 실행됩니다. 또한 스모크 테스트를 실행할 때 배포 작업의 출력을 사용할 수 있습니다.

    참고

    PowerShell 및 Pester는 GitHub 호스팅 실행기에 미리 설치되어 있습니다. 스크립트 단계에서 사용하기 위해 특별한 작업을 수행할 필요가 없습니다.

  2. 파일을 저장합니다.

워크플로 정의 확인 및 커밋

  1. ‘workflow.yml’ 파일이 다음 코드와 같이 표시되는지 확인합니다.

    name: deploy-toy-website-test
    concurrency: toy-company
    
    on:
      push:
        branches:
          - main
    
    permissions:
      id-token: write
      contents: read
    
    env:
      AZURE_RESOURCEGROUP_NAME: ToyWebsiteTest
      ENVIRONMENT_TYPE: Test
    
    jobs:
      lint:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - name: Run Bicep linter
          run: az bicep build --file deploy/main.bicep
    
      validate:
        runs-on: ubuntu-latest
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/arm-deploy@v1
          name: Run preflight validation
          with:
            deploymentName: ${{ github.run_number }}
            resourceGroupName: ${{ env.AZURE_RESOURCEGROUP_NAME }}
            template: ./deploy/main.bicep
            parameters: environmentType=${{ env.ENVIRONMENT_TYPE }}
            deploymentMode: Validate
    
      preview:
        runs-on: ubuntu-latest
        needs: [lint, validate]
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/arm-deploy@v1
          name: Run what-if
          with:
            failOnStdErr: false
            resourceGroupName: ${{ env.AZURE_RESOURCEGROUP_NAME }}
            template: deploy/main.bicep
            parameters: >
              environmentType=${{ env.ENVIRONMENT_TYPE }}
            additionalArguments: --what-if
    
      deploy:
        runs-on: ubuntu-latest
        environment: Website
        needs: preview
        outputs:
          appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
        steps:
        - uses: actions/checkout@v3
        - uses: azure/login@v1
          name: Sign in to Azure
          with:
            client-id: ${{ secrets.AZURE_CLIENT_ID }}
            tenant-id: ${{ secrets.AZURE_TENANT_ID }}
            subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
        - uses: azure/arm-deploy@v1
          id: deploy
          name: Deploy website
          with:
            failOnStdErr: false
            deploymentName: ${{ github.run_number }}
            resourceGroupName: ${{ env.AZURE_RESOURCEGROUP_NAME }}
            template: ./deploy/main.bicep
            parameters: environmentType=${{ env.ENVIRONMENT_TYPE }}
    
      smoke-test:
        runs-on: ubuntu-latest
        needs: deploy
        steps:
        - uses: actions/checkout@v3
        - run: |
            $container = New-PesterContainer `
              -Path 'deploy/Website.Tests.ps1' `
              -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' }
            Invoke-Pester `
              -Container $container `
              -CI
          name: Run smoke tests
          shell: pwsh
    

    파일이 달라 보이면 다음 예제와 일치하도록 업데이트한 후 저장합니다.

  2. Visual Studio Code 터미널에서 다음 명령을 실행하여 변경 내용을 커밋하고 Git 리포지토리에 푸시합니다.

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

워크플로 실행 및 테스트 결과 검토

  1. 브라우저에서 워크플로로 이동합니다.

  2. 가장 최근에 실행한 워크플로를 선택합니다.

    워크플로가 린팅, 유효성 검사미리 보기 작업을 완료할 때까지 기다립니다. GitHub Actions는 자동으로 페이지를 최신 상태로 업데이트하지만 가끔 페이지를 새로 고치는 것이 좋습니다.

  3. 배포 검토 단추를 선택하고 Website 환경을 선택한 후 승인 및 배포를 선택합니다.

    워크플로 실행이 완료될 때까지 기다립니다.

  4. 배포 작업이 성공적으로 완료됩니다. smoke-test 작업이 오류를 나타내며 완료됩니다.

    워크플로 실행 작업을 보여주는 GitHub 인터페이스의 스크린샷. 스모크 테스트 작업에서 오류를 보고합니다.

  5. 세부 정보를 보려면 smoke-test 작업을 선택합니다.

  6. smoke-test 출력에서는 두 개의 테스트가 실행된 것을 볼 수 있습니다. 하나는 통과하고 하나는 실패했습니다. 실패한 테스트는 Toy Website.Does not serve pages over HTTP로 표시됩니다.

    실패한 테스트가 강조 표시된 워크플로 실행 테스트 결과를 보여주는 GitHub 인터페이스의 스크린샷

    이 텍스트는 웹 사이트가 보안 팀의 요구 사항을 충족하도록 올바르게 구성되지 않았음을 나타냅니다.

Bicep 파일 업데이트

Bicep 정의가 보안 팀의 요구 사항을 충족하지 않는다는 것을 확인했으므로 문제를 해결할 수 있습니다.

  1. Visual Studio Code의 deploy 폴더에서 main.bicep 파일을 엽니다.

  2. Azure App Service 앱에 대한 정의를 찾아 properties 영역에 httpsOnly 속성을 포함하도록 업데이트합니다.

    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. 파일을 저장합니다.

  4. Visual Studio Code 터미널에서 다음 명령을 실행하여 변경 내용을 커밋하고 Git 리포지토리에 푸시합니다.

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

워크플로 다시 실행

  1. 브라우저에서 워크플로 실행으로 이동합니다.

  2. 가장 최근 실행을 선택합니다.

    워크플로가 린팅, 유효성 검사미리 보기 작업을 완료할 때까지 기다립니다. GitHub는 자동으로 페이지를 최신 상태로 업데이트하지만 가끔 페이지를 새로 고치는 것이 좋습니다.

  3. 미리 보기 작업을 선택하고 what-if 결과를 다시 검토합니다.

    가상 명령이 다음과 같은 httpsOnly 속성 값의 변경 내용을 탐지했습니다.

    Resource and property changes are indicated with these symbols:
      - Delete
      + Create
      ~ Modify
      = Nochange
      * Ignore
    
    The deployment will update the following scope:
    
    Scope: /subscriptions/***/resourceGroups/ToyWebsiteTest
    
      ~ Microsoft.OperationalInsights/workspaces/workspace-abcdefghijklm [2022-10-01]
        - properties.retentionInDays: 30
        - properties.sku:
    
            name: "pergb2018"
    
        - properties.workspaceCapping:
    
            dailyQuotaGb: -1.0
    
      ~ Microsoft.Web/sites/toy-website-abcdefghijklm [2022-03-01]
        + properties.siteConfig.localMySqlEnabled:   false
        + properties.siteConfig.netFrameworkVersion: "v4.6"
        ~ properties.httpsOnly:                      false => true
    
      = Microsoft.Insights/components/toywebsite [2020-02-02]
      = Microsoft.Storage/storageAccounts/mystorageabcdefghijklm [2022-09-01]
      = Microsoft.Web/serverfarms/toy-website [2022-03-01]
      * microsoft.alertsmanagement/smartDetectorAlertRules/Failure Anomalies - toywebsite
    
    Resource changes: 2 to modify, 3 no change, 1 to ignore.
    
  4. 워크플로 실행으로 돌아갑니다.

  5. 배포 검토 단추를 선택하고 Website 환경을 선택한 후 승인 및 배포를 선택합니다.

    워크플로 실행이 완료될 때까지 기다립니다.

  6. smoke-test 작업을 포함하여 전체 워크플로가 성공적으로 완료됩니다. 성공이란 두 테스트를 모두 통과했다는 뜻입니다.

    성공한 워크플로 실행을 보여주는 GitHub 인터페이스의 스크린샷

리소스 정리

연습을 완료하고 나면 리소스에 대한 요금이 청구되지 않도록 리소스를 제거하면 됩니다.

Visual Studio Code 터미널에서 다음 명령을 실행합니다.

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

리소스 그룹은 백그라운드에서 삭제됩니다.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force