name: $(BuildID)_$(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)

# https://docs.microsoft.com/azure/devops/pipelines/process/runtime-parameters
parameters:
- name: maxParallelJobs
  displayName: Maximum jobs to run in parallel. Change maxParallel to 1 make OS builds run in serial rather than in parallel.
  type: number
  # Reasonable target to avoid throttling and contention with several PRs running.
  default: 4
- name: jobTimeoutInMinutes
  displayName: Timeout for each job
  type: number
  default: 150
- name: testTargets
  displayName: The .NET test targets to build and run. 'all' and 'min-matrix' (net6.0) are aggregates of the remaining values.
  type: string
  values:
    - default
    - all
    - min-matrix
    - net8.0
    - net6.0
    - net4.7.2
  default: default

variables:
# Variables for the buid/test matrix based on the 'testTargets' that turn on/off various test targets specifically or based on the 2 aggregates.
  ${{ if and(eq(parameters['testTargets'], 'default'), eq(variables['Build.Reason'], 'PullRequest')) }}:
    allTestTargets: 'False'
    minMatrix: 'True'
  ${{ elseif eq(parameters['testTargets'], 'default') }}:
    allTestTargets: 'True'
    minMatrix: 'True'
  ${{ else }}:
    allTestTargets: ${{ contains(parameters['testTargets'], 'all') }}
    # We wish to run the minimum test matrix targets if 1) directly specified, 2) if all targets was chosen.
    minMatrix: ${{ or(eq(parameters['testTargets'], 'min-matrix'), eq(variables['allTestTargets'], 'True')) }}

# The minimum matrix build/test targets.
  testNet60: ${{ or(eq(variables['minMatrix'], 'True'), contains(parameters['testTargets'], 'net6.0')) }}

# The remaining build/test targets.
  testNet80: ${{ or(eq(variables['allTestTargets'], 'True'), contains(parameters['testTargets'], 'net8.0')) }}
  testNet472: ${{ or(eq(variables['allTestTargets'], 'True'), contains(parameters['testTargets'], 'net4.7.2')) }}

trigger:
  batch: true
  branches:
    include:
    - main
  paths:
    exclude:
    - docs/*
    - tools/CaptureLogs/*
    - iothub/device/devdoc/*
    - iothub/service/devdoc/*

resources:
  - repo: self
    clean: true

jobs:
  ### Linux build ###
  - job: LINUX
    displayName: Linux
    condition: succeeded()
    timeoutInMinutes: ${{ parameters.jobTimeoutInMinutes }}
    strategy:
      maxParallel: ${{ parameters.maxParallelJobs }}
      matrix:
        .NET 6.0:
          FRAMEWORK: net6.0
          # Can't find a good way to skip matrix elements so for now we set an environment variable that the script will use to bail out.
          SHOULD_RUN: ${{ eq(variables['testNet60'], 'True') }}
        .NET 8.0:
          FRAMEWORK: net8.0
          SHOULD_RUN: ${{ eq(variables['testNet80'], 'True') }}
    pool:
      # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date.
      name: 'sdk-net--ubuntu-20'
    steps:
      - task: PowerShell@2
        displayName: 'Print vars'
        inputs:
          targetType: inline
          script: |
            Write-Host "Build.Reason: ${{ variables['Build.Reason'] }}"
            Write-Host "jobTimeoutInMinutes: ${{ parameters.jobTimeoutInMinutes }}"
            Write-Host "maxParallelJobs: ${{ parameters.maxParallelJobs }}"
            Write-Host "minMatrix: ${{ variables.minMatrix }}"
            Write-Host "testNet60: ${{ variables.testNet60 }}"
            Write-Host "testNet80: ${{ variables.testNet80 }}"
            Write-Host "testNetcore31: ${{ variables.testNetcore31 }}"
            Write-Host "testNetcore21: ${{ variables.testNetcore21 }}"
            Write-Host "testNet472: ${{ variables.testNet472 }}"

      # https://docs.microsoft.com/azure/devops/pipelines/tasks/tool/dotnet-core-tool-installer?view=azure-devops

      # Linux requires .NET 8.0 install for tests to run, no matter which framework target is being tested
      - task: UseDotNet@2
        displayName: 'Use .NET SDK 8.0'
        inputs:
         packageType: sdk
         version: 8.x
         performMultiLevelLookup: true
         installationPath: $(Agent.ToolsDirectory)/dotnet

      - ${{ if eq(variables['testNet60'], 'True') }}:
        - task: UseDotNet@2
          displayName: 'Use .NET SDK 6.0'
          inputs:
            packageType: sdk
            version: 6.x
            performMultiLevelLookup: true
            installationPath: $(Agent.ToolsDirectory)/dotnet

      - task: Docker@2
        displayName: "Login to ACR"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          command: "login"
          containerRegistry: "Azure IoT ACR"

      - task: PowerShell@2
        displayName: 'Cert Validation - Setup Certificate Proxy'
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          targetType: 'inline'
          script: |
            Write-Host "============"
            Write-Host "check docker"
            Write-Host "============"
            docker images
            docker ps -a
            gci -Path $(Build.SourcesDirectory)/e2e/test/docker -Recurse -Force -Name
            Write-Host "==========================="
            Write-Host "uncompress self-signed cert"
            Write-Host "==========================="
            gzip -d -N -S ".bin" $(Build.SourcesDirectory)/e2e/test/docker/haproxy/haproxy.bin
            gci -Path $(Build.SourcesDirectory)/e2e/test/docker -Recurse -Force -Name
            Write-Host "====================="
            Write-Host "Docker run containers"
            Write-Host "====================="
            docker run -h invalidcertgde1.westus.cloudapp.azure.com --name invalid-gde --expose=443 --expose=5671 --expose=8883 -v $(Build.SourcesDirectory)/e2e/test/docker/haproxy:/usr/local/etc/haproxy:ro -d aziotacr.azurecr.io/haproxy haproxy -f /usr/local/etc/haproxy/haproxygde.cfg
            docker run -h invalidcertdps1.westus.cloudapp.azure.com --name invalid-dps --expose=443 --expose=5671 --expose=8883 -v $(Build.SourcesDirectory)/e2e/test/docker/haproxy:/usr/local/etc/haproxy:ro -d aziotacr.azurecr.io/haproxy haproxy -f /usr/local/etc/haproxy/haproxydps.cfg
            docker run -h invalidcertiothub1.westus.cloudapp.azure.com --name invalid-hub --expose=443 --expose=5671 --expose=8883 -v $(Build.SourcesDirectory)/e2e/test/docker/haproxy:/usr/local/etc/haproxy:ro -d aziotacr.azurecr.io/haproxy haproxy -f /usr/local/etc/haproxy/haproxyhub.cfg
            docker ps -a

      - task: Bash@3
        displayName: 'Cert Validation - Setup local hostname'
        name: CVTEST_NET
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          targetType: 'inline'
          script: |
            echo "==============="
            echo "Inspect network"
            echo "==============="
            ip -4 addr
            export CVTEST_HOST_IP=$(ip -4 addr show eth0 | grep -Po 'inet \K[\d.]+')
            export CVTEST_HOST_NETWORK=$(ip -4 addr show eth0 | grep -Po 'inet \K[\d.]+/*\d.')
            export CVTEST_HOST_SUBNET=$(ip -4 addr show eth0 | grep -Po 'inet \K[\d.]+' | grep -Po '[\d]{1,3}.[\d]{1,3}.[\d]{1,3}')
            export CVTEST_HOST_SUBNET_MASK=$(ip -4 addr show eth0 | grep -Po 'inet \K[\d.]+/*\d.' | grep -Po '/[\d]{1,2}')
            export CVTEST_CONTAINER_IP=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+')
            echo "HOST=$CVTEST_HOST_IP"
            echo "HOST NETWORK=$CVTEST_HOST_NETWORK"
            echo "HOST SUBNET=$CVTEST_HOST_SUBNET"
            echo "HOST SUBNET MASK=$CVTEST_HOST_SUBNET_MASK"
            echo "CONTAINER=$CVTEST_CONTAINER_IP"
            #echo "##vso[task.setvariable variable=AGENT_HOST;isoutput=true;]$CVTEST_HOST_IP"
            #echo "##vso[task.setvariable variable=AGENT_NETWORK;isoutput=true;]$CVTEST_HOST_NETWORK"
            #echo "##vso[task.setvariable variable=AGENT_SUBNET;isoutput=true;]$CVTEST_HOST_SUBNET"
            #echo "##vso[task.setvariable variable=AGENT_SUBNET_MASK;isoutput=true;]$CVTEST_HOST_SUBNET_MASK"
            #echo "##vso[task.setvariable variable=AGENT_CONTAINER;isoutput=true;]$CVTEST_CONTAINER_IP"
            export CVTEST_GDE_IP=$(docker inspect invalid-gde | grep -Po -m 1 '"IPAddress": "\K[\d.]+')
            export CVTEST_DPS_IP=$(docker inspect invalid-dps | grep -Po -m 1 '"IPAddress": "\K[\d.]+')
            export CVTEST_HUB_IP=$(docker inspect invalid-hub | grep -Po -m 1 '"IPAddress": "\K[\d.]+')
            echo "invalid-gde=$CVTEST_GDE_IP"
            echo "invalid-dps=$CVTEST_DPS_IP"
            echo "invalid-hub=$CVTEST_HUB_IP"
            #echo "##vso[task.setvariable variable=AGENT_GDE_IP;isoutput=true;]$CVTEST_GDE_IP"
            #echo "##vso[task.setvariable variable=AGENT_DPS_IP;isoutput=true;]$CVTEST_DPS_IP"
            #echo "##vso[task.setvariable variable=AGENT_HUB_IP;isoutput=true;]$CVTEST_HUB_IP"
            echo "================="
            echo "Update hosts file"
            echo "================="
            sudo bash -c 'mv /etc/hosts /etc/hosts.org'
            sudo bash -c 'cp /etc/hosts.org /etc/hosts'
            echo "$CVTEST_GDE_IP invalidcertgde1.westus.cloudapp.azure.com"
            echo "$CVTEST_DPS_IP invalidcertdps1.westus.cloudapp.azure.com"
            echo "$CVTEST_HUB_IP invalidcertiothub1.westus.cloudapp.azure.com"
            echo "" >> /tmp/hosts.cvtest
            echo "# Local host for invalid cert test" >> /tmp/hosts.cvtest
            echo "$CVTEST_GDE_IP invalidcertgde1.westus.cloudapp.azure.com" >> /tmp/hosts.cvtest
            echo "$CVTEST_DPS_IP invalidcertdps1.westus.cloudapp.azure.com" >> /tmp/hosts.cvtest
            echo "$CVTEST_HUB_IP invalidcertiothub1.westus.cloudapp.azure.com" >> /tmp/hosts.cvtest
            sudo bash -c 'cat /tmp/hosts.cvtest >> /etc/hosts'
            cat /etc/hosts
            echo "====================="
            echo "Ping containers (URL)"
            echo "====================="
            docker ps -a
            route
            ping -c 2 invalidcertgde1.westus.cloudapp.azure.com
            ping -c 2 invalidcertdps1.westus.cloudapp.azure.com
            ping -c 2 invalidcertiothub1.westus.cloudapp.azure.com

      - task: Docker@1
        displayName: "Start TPM Simulator"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          containerregistrytype: "Container Registry"
          command: "Run an image"
          azureContainerRegistry: aziotacr.azurecr.io
          imageName: aziotacr.azurecr.io/aziotbld/testtpm
          containerName: "testtpm-instance"
          ports: |
            127.0.0.1:2321:2321
            127.0.0.1:2322:2322
          restartPolicy: unlessStopped

      - task: Docker@1
        displayName: "Start Test Proxy"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          containerregistrytype: "Container Registry"
          command: "Run an image"
          azureContainerRegistry: aziotacr.azurecr.io
          imageName: aziotacr.azurecr.io/aziotbld/testproxy
          containerName: "testproxy-instance"
          ports: "127.0.0.1:8888:8888"
          restartPolicy: unlessStopped

      - powershell: ./vsts/gatedBuild.ps1
        displayName: build
        condition: eq(variables.SHOULD_RUN, 'True')
        env:
          # Environment variables for IoT Hub E2E tests
          IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING)
          IOTHUB_DEVICE_CONN_STRING: $(IOTHUB-DEVICE-CONN-STRING)
          IOTHUB_X509_DEVICE_PFX_CERTIFICATE: $(IOTHUB-X509-DEVICE-PFX-CERTIFICATE)
          IOTHUB_X509_DEVICE_PFX_THUMBPRINT: $(IOTHUB-X509-DEVICE-PFX-THUMBPRINT)

          IOTHUB_X509_CHAIN_DEVICE_NAME: $(IOTHUB-X509-CHAIN-DEVICE-NAME)
          IOTHUB_X509_CHAIN_DEVICE_PFX_CERTIFICATE: $(IOTHUB-X509-CHAIN-DEVICE-PFX-CERTIFICATE)
          IOTHUB_USER_ASSIGNED_MSI_RESOURCE_ID: $(IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID)
          IOT_HUB_SAS_KEY: $(IOTHUB-SAS-KEY)
          IOT_HUB_SAS_KEY_NAME: $(IOTHUB-SAS-KEY-NAME)

          # Environment variables for DPS E2E tests
          DPS_IDSCOPE: $(DPS-IDSCOPE)
          PROVISIONING_CONNECTION_STRING: $(PROVISIONING-CONNECTION-STRING)
          DPS_GLOBALDEVICEENDPOINT: $(DPS-GLOBALDEVICEENDPOINT)
          DPS_X509_PFX_CERTIFICATE_PASSWORD: $(DPS-X509-PFX-CERTIFICATE-PASSWORD)
          DPS_X509_GROUP_ENROLLMENT_NAME: $(DPS-X509-GROUP-ENROLLMENT-NAME)

          # Environment variables for Azure resources used for E2E tests (common)
          X509_CHAIN_ROOT_CA_CERTIFICATE: $(X509-CHAIN-ROOT-CA-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE1_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE1-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE2_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE2-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE2_PFX_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE2-PFX-CERTIFICATE)
          STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING)
          MSFT_TENANT_ID: $(MSFT-TENANT-ID)
          E2E_TEST_AAD_APP_CLIENT_ID: $(E2E-TEST-AAD-APP-CLIENT-ID)
          E2E_TEST_AAD_APP_CLIENT_SECRET: $(E2E-TEST-AAD-APP-CLIENT-SECRET)
          E2E_IKEY: $(E2E-IKEY)

          # Environment variables for the DevOps pipeline
          PROXY_SERVER_ADDRESS: 127.0.0.1:8888
          TARGET_BRANCH: $(System.PullRequest.TargetBranch)
          FRAMEWORK: $(FRAMEWORK)

          # Environment variables for invalid certificate tests
          IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT)
          IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT)
          DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT)
          PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT)

          # Environment variables used by Log Analytics Workspace for Azure Security Center
          LA_AAD_APP_ID: $(LA-AAD-APP-ID)
          LA_AAD_APP_CERT_BASE64: $(LA-AAD-APP-CERT-BASE64)
          LA_WORKSPACE_ID: $(LA-WORKSPACE-ID)

          # Environment variable for running samples/cleanups
          PATH_TO_DEVICE_PREFIX_FOR_DELETION_FILE: $(PATH-TO-DEVICE-PREFIX-FOR-DELETION-FILE)
          PNP_TC_DEVICE_CONN_STRING: $(PNP-TC-DEVICE-CONN-STRING)
          PNP_THERMOSTAT_DEVICE_CONN_STRING: $(PNP-THERMOSTAT-DEVICE-CONN-STRING)
          DPS_SYMMETRIC_KEY_INDIVIDUAL_ENROLLMENT_REGISTRATION_ID: $(DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLMENT-REGISTRATION-ID)
          DPS_SYMMETRIC_KEY_INDIVIDUAL_ENROLLEMNT_PRIMARY_KEY: $(DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLEMNT-PRIMARY-KEY)

      - task: CopyFiles@2
        displayName: "Copy files to the artifacts folder"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          SourceFolder: "$(Build.SourcesDirectory)"
          Contents: "**/*.trx"
          TargetFolder: "$(Build.ArtifactStagingDirectory)"

      - task: PublishBuildArtifacts@1
        displayName: "Publish Artifact: testresults_linux_$(FRAMEWORK)"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          ArtifactName: testresults_linux_$(FRAMEWORK)

      - task: PublishTestResults@2
        displayName: "Publish Test Results **/*.trx"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          testRunner: VSTest
          testRunTitle: "Linux Tests ($(FRAMEWORK)) (Attempt $(System.JobAttempt))"
          testResultsFiles: "**/*.trx"
        
      - task: ComponentGovernanceComponentDetection@0
        displayName: Component Governance Detection
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          scanType: 'Register'
          verbosity: 'Verbose'
          alertWarningLevel: 'Low' # The task will present a warning, but will not cause the build to fail

 ### Windows build ###
  - job: WINDOWS
    displayName: Windows
    condition: succeeded()
    timeoutInMinutes: ${{ parameters.jobTimeoutInMinutes }}
    strategy:
      maxParallel: ${{ parameters.maxParallelJobs }}
      matrix:
        .NET 6.0:
          FRAMEWORK: net6.0
          SHOULD_RUN: ${{ eq(variables['testNet60'], 'True') }}
        .NET 8.0:
          FRAMEWORK: net8.0
          SHOULD_RUN: ${{ eq(variables['testNet80'], 'True') }}
        .NET 4.7.2:
          FRAMEWORK: net472
          SHOULD_RUN: ${{ eq(variables['testNet472'], 'True') }}
    pool:
      # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date.
      name: 'sdk-net--win-vs2022'
    steps:
      - task: PowerShell@2
        displayName: 'Print vars'
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          targetType: inline
          script: |
            Write-Host "Build.Reason: ${{ variables['Build.Reason'] }}"
            Write-Host "jobTimeoutInMinutes: ${{ parameters.jobTimeoutInMinutes }}"
            Write-Host "maxParallelJobs: ${{ parameters.maxParallelJobs }}"
            Write-Host "minMatrix: ${{ variables.minMatrix }}"
            Write-Host "testNet60: ${{ variables.testNet60 }}"
            Write-Host "testNet80: ${{ variables.testNet80 }}"
            Write-Host "testNetcore31: ${{ variables.testNetcore31 }}"
            Write-Host "testNetcore21: ${{ variables.testNetcore21 }}"
            Write-Host "testNet472: ${{ variables.testNet472 }}"

      - ${{ if eq(variables['testNet60'], 'True') }}:
        - task: UseDotNet@2
          displayName: 'Use .NET SDK 6.0'
          inputs:
            packageType: sdk
            version: 6.x
            performMultiLevelLookup: true
            installationPath: $(Agent.ToolsDirectory)/dotnet

      - ${{ if eq(variables['testNet80'], 'True') }}:
        - task: UseDotNet@2
          displayName: 'Use .NET SDK 8.0'
          inputs:
            packageType: sdk
            version: 8.x
            performMultiLevelLookup: true
            installationPath: $(Agent.ToolsDirectory)/dotnet

      - ${{ if eq(variables['testNet472'], 'True') }}:
        - task: CmdLine@2
          displayName: 'Install .NET 4.7.2'
          inputs:
            script: 'choco install -y netfx-4.7.2-devpack'

      - script: |
          choco install -y squid

        displayName: "Install Squid"
        condition: eq(variables.SHOULD_RUN, 'True')

      - powershell: ./vsts/start_tpm_windows.ps1
        displayName: "Start TPM Simulator"
        condition: eq(variables.SHOULD_RUN, 'True')

      - powershell: ./vsts/gatedBuild.ps1
        displayName: build
        condition: eq(variables.SHOULD_RUN, 'True')
        env:
          # Environment variables for IoT Hub E2E tests
          IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING)
          IOTHUB_DEVICE_CONN_STRING: $(IOTHUB-DEVICE-CONN-STRING)
          IOTHUB_X509_DEVICE_PFX_CERTIFICATE: $(IOTHUB-X509-DEVICE-PFX-CERTIFICATE)
          IOTHUB_X509_DEVICE_PFX_THUMBPRINT: $(IOTHUB-X509-DEVICE-PFX-THUMBPRINT)

          IOTHUB_X509_CHAIN_DEVICE_NAME: $(IOTHUB-X509-CHAIN-DEVICE-NAME)
          IOTHUB_X509_CHAIN_DEVICE_PFX_CERTIFICATE: $(IOTHUB-X509-CHAIN-DEVICE-PFX-CERTIFICATE)
          IOTHUB_USER_ASSIGNED_MSI_RESOURCE_ID: $(IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID)
          IOT_HUB_SAS_KEY: $(IOTHUB-SAS-KEY)
          IOT_HUB_SAS_KEY_NAME: $(IOTHUB-SAS-KEY-NAME)

          # Environment variables for DPS E2E tests
          DPS_IDSCOPE: $(DPS-IDSCOPE)
          PROVISIONING_CONNECTION_STRING: $(PROVISIONING-CONNECTION-STRING)
          DPS_GLOBALDEVICEENDPOINT: $(DPS-GLOBALDEVICEENDPOINT)
          DPS_X509_PFX_CERTIFICATE_PASSWORD: $(DPS-X509-PFX-CERTIFICATE-PASSWORD)
          DPS_X509_GROUP_ENROLLMENT_NAME: $(DPS-X509-GROUP-ENROLLMENT-NAME)

          # Environment variables for Azure resources used for E2E tests (common)
          X509_CHAIN_ROOT_CA_CERTIFICATE: $(X509-CHAIN-ROOT-CA-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE1_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE1-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE2_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE2-CERTIFICATE)
          X509_CHAIN_INTERMEDIATE2_PFX_CERTIFICATE: $(X509-CHAIN-INTERMEDIATE2-PFX-CERTIFICATE)
          STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING)
          MSFT_TENANT_ID: $(MSFT-TENANT-ID)
          E2E_TEST_AAD_APP_CLIENT_ID: $(E2E-TEST-AAD-APP-CLIENT-ID)
          E2E_TEST_AAD_APP_CLIENT_SECRET: $(E2E-TEST-AAD-APP-CLIENT-SECRET)
          E2E_IKEY: $(E2E-IKEY)

          # Environment variables for the DevOps pipeline
          PROXY_SERVER_ADDRESS: 127.0.0.1:3128
          TARGET_BRANCH: $(System.PullRequest.TargetBranch)
          FRAMEWORK: $(FRAMEWORK)

          # Environment variables for invalid certificate tests
          IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT)
          IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT)
          DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT)
          PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT)

          # Environment variables used by Log Analytics Workspace for Azure Security Center
          LA_AAD_APP_ID: $(LA-AAD-APP-ID)
          LA_AAD_APP_CERT_BASE64: $(LA-AAD-APP-CERT-BASE64)
          LA_WORKSPACE_ID: $(LA-WORKSPACE-ID)

          # Environment variable for running samples/cleanups
          PATH_TO_DEVICE_PREFIX_FOR_DELETION_FILE: $(PATH-TO-DEVICE-PREFIX-FOR-DELETION-FILE)
          PNP_TC_DEVICE_CONN_STRING: $(PNP-TC-DEVICE-CONN-STRING)
          PNP_THERMOSTAT_DEVICE_CONN_STRING: $(PNP-THERMOSTAT-DEVICE-CONN-STRING)
          DPS_SYMMETRIC_KEY_INDIVIDUAL_ENROLLMENT_REGISTRATION_ID: $(DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLMENT-REGISTRATION-ID)
          DPS_SYMMETRIC_KEY_INDIVIDUAL_ENROLLEMNT_PRIMARY_KEY: $(DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLEMNT-PRIMARY-KEY)

      - task: CopyFiles@2
        displayName: "Copy TRX files to the artifacts folder"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          SourceFolder: "$(Build.SourcesDirectory)"
          Contents: "**/*.trx"
          TargetFolder: "$(Build.ArtifactStagingDirectory)"

      - task: CopyFiles@2
        displayName: "Copy ETL files to the artifacts folder"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          SourceFolder: "$(Build.SourcesDirectory)"
          Contents: "**/*.etl"
          TargetFolder: "$(Build.ArtifactStagingDirectory)"

      - task: PublishBuildArtifacts@1
        displayName: "Publish Artifact: testresults"
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          ArtifactName: testresults_windows_$(FRAMEWORK)

      - task: PublishTestResults@2
        displayName: "Publish Test Results **/*.trx"
        inputs:
          testRunner: VSTest
          testResultsFiles: "**/*.trx"
          testRunTitle: "Windows Tests ($(FRAMEWORK)) (Attempt $(System.JobAttempt))"
          platform: Windows
          configuration: "Debug UT + Release E2E ($(FRAMEWORK))"
        condition: eq(variables.SHOULD_RUN, 'True')

      - task: ComponentGovernanceComponentDetection@0
        displayName: Component Governance Detection
        condition: eq(variables.SHOULD_RUN, 'True')
        inputs:
          scanType: 'Register'
          verbosity: 'Verbose'
          alertWarningLevel: 'Low' # The task will present a warning, but will not cause the build to fail

  ### .Net SDL Analyzers ###
  - job: DOTNet_SDL_Analyzers
    displayName: .Net SDL Analyzers
    timeoutInMinutes: 60

    condition: succeeded()
    pool:
      vmImage: windows-2022
    steps:
      - powershell: .\build.ps1 -clean -build -configutaion Debug -package
        displayName: Build Package

      - task: ComponentGovernanceComponentDetection@0
        displayName: "Component Detection"
      
      - task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@2
        displayName: "Run PoliCheck"
        inputs:
          targetType: F
          optionsRulesDBPath: '$(Build.SourcesDirectory)\vsts\PolicheckExclusionsDB.mdb'
          optionsSEV: '1|2|3|4'
          optionsPE: 1
      
      - task: securedevelopmentteam.vss-secure-development-tools.build-task-antimalware.AntiMalware@4
        displayName: "Run MpCmdRun.exe"
        inputs:
          EnableServices: true
          SignatureFreshness: OneDay
          # Signature refreshes on Hosted Agents can sometimes have a delay of a day or two.
          # The support team already has a process to address this, so our pipeline can treat stale signatures as warnings (instead of treating it as an error).
          TreatStaleSignatureAs: Warning

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@3
        displayName: "Run BinSkim"
        inputs:
          arguments: 'analyze  $(Build.SourcesDirectory)\Microsoft.Azure.Devices.*.dll --recurse --verbose'

        # TODO #181 Config issue: must run on Debug builds only with valid PDBs.
        enabled: false

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-codemetrics.CodeMetrics@1
        displayName: "Run CodeMetrics"
        inputs:
          Files: '$(Build.SourcesDirectory)\**\Microsoft.Azure.Devices.*.dll'

        # TODO #181 Config issue: must run on Debug builds only with valid PDBs.
        enabled: false

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
        displayName: "Run CredScan"
        inputs:
          toolMajorVersion: V2
          suppressionsFile: vsts/CredScanSuppressions.json
          regexMatchTimeoutInSeconds: 5

          # TODO #181 Samples / tests fail the test due to fake connection strings.
          debugMode: false

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@3
        displayName: "Publish Security Analysis Logs"

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-report.SdtReport@2
        displayName: "Create Security Analysis Report"
        inputs:
          AllTools: true
          
      - task: ComponentGovernanceComponentDetection@0
        displayName: Component Governance Detection
        inputs:
          scanType: 'Register'
          verbosity: 'Verbose'
          alertWarningLevel: 'Low' # The task will present a warning, but will not cause the build to fail
        condition: always()

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-uploadtotsa.TSAUpload@2
        displayName: 'TSA upload'
        inputs:
          GdnPublishTsaOnboard: false
          GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)/vsts/TsaUploadConfigFile.json'
          GdnPublishTsaExportedResultsPublishable: true

      - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1
        displayName: "Post Analysis"
        inputs:
          AllTools: true

        # TODO #181 Enable post analysis to break builds after all above items are enabled.
        enabled: false

# END: SDL and Compliance tasks #

