Running interactive tasks on a Windows 10 / server Virtual Machine and VMSS through Azure DevOps

When it comes to executing UI-based or interactive tasks in automated environments, traditional headless agents often fall short. Here’s a way to make it work.

What are interactive tasks?

In a pipeline, interactive tasks/tests are automated processes that require a graphical user interface to simulate user interactions with an application. These tests often:

  • Validate GUI components.

  • Require input simulations like mouse clicks or keystrokes.

  • Run on applications that depend on display rendering.

While headless environments work well for many scenarios, certain use cases, such as UI testing or hardware integration tests, necessitate a fully interactive session.

How to configure it?

We first tried to add a VMSS as Azure Devops Agents and use the “tick” to run it as interactive and use a standard W10 / server VMSS to run the tasks.

Sadly, we’ve never been able to run interactive tests with this option(or at least for the moment ! Keep an eye on it if there’s something new / working better that could be coming soon)…

To address this, we had to configure interactive agents on a Windows 10 Virtual Machine or a Virtual Machine Scale Set (VMSS) to run pipeline tasks that require a graphical user interface (GUI). The pool type has been changed (we cannot refer to a “simple” VMSS and we had to choose the “self hosted” agent pool type instead.)

Prepare your Windows 10 VM/VMSS Instances for Interactive tasks

To set up interactive tasking on VM/ VMSS instances, we’ll use the following custom script at VM / VMSS startup (you can configure it through the Azure UI or directly with IaC, referencing the script in the “custom script” extension). This scrpit (that will be run at VM’s creation) ensures the system remains logged in, avoiding interruptions like sleep mode or screen savers, and installing the Azure DevOps agent with interactive mode enabled. The downside of this method is that we’ll have to add our agent as “self hosted” , even if we’re using a VMSS on Azure.

Custom Script for Interactive Agent Configuration

Warning : this script will setup the machine as always on, always run and always logged in with an admin user. Customize it if such features aren’t needed !

# Creds: User / pass
$user = "${var.admin_username}"
$password = "${var.admin_password}"

# Disable standby mode
powercfg -change -standby-timeout-ac 0
powercfg -change -monitor-timeout-ac 0

# Disable automatic session close
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ScreenSaveActive" -Value "0" 
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ScreenSaverIsSecure" -Value "0"

# Disable hibernation
powercfg -h off
powercfg -setacvalueindex SCHEME_CURRENT SUB_SLEEP STANDBYIDLE 0
powercfg -setdcvalueindex SCHEME_CURRENT SUB_SLEEP STANDBYIDLE 0
powercfg -setacvalueindex SCHEME_CURRENT SUB_SLEEP HIBERNATEIDLE 0
powercfg -setdcvalueindex SCHEME_CURRENT SUB_SLEEP HIBERNATEIDLE 0

# Never stop the display
powercfg -change -monitor-timeout-ac 0
powercfg /change standby-timeout-dc 0

powercfg -setacvalueindex SCHEME_CURRENT SUB_SLEEP STANDBYIDLE 0
powercfg -setdcvalueindex SCHEME_CURRENT SUB_SLEEP STANDBYIDLE 0
powercfg -setacvalueindex SCHEME_CURRENT SUB_DISK DISKIDLE 0
powercfg -setdcvalueindex SCHEME_CURRENT SUB_DISK DISKIDLE 0


# Disable the NIC-standby
Get-NetAdapter | ForEach-Object { Set-NetAdapterPowerManagement -Name $_.Name -AllowWakeArmed Only -WakeOnMagicPacket $true -Force }

# Auto login for interactive sessions
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "AutoAdminLogon" -Value "1"
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultUserName" -Value $user
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" -Name "DefaultPassword" -Value $password

# Disable display of error reporting
Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\Windows Error Reporting" -Name "DontShowUI" -Value 1
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Windows Error Reporting" -Name "DontShowUI" -Value 1

# Disable OOBE notifs
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableOOBE" -Value 0 -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "NoOOBE" -Value 1 -Force

# Disable LUA and admin approvals for software install
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Value 0 -Force
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "ConsentPromptBehaviorAdmin" -Value 0 -Force

# Disable OOBE privacy settings screen
$regKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE"
Set-ItemProperty -Path $regKey -Name "PrivacySetting" -Value 0 -Force
Set-ItemProperty -Path $regKey -Name "NoPrivacySettings" -Value 1 -Force
New-ItemProperty -Path $regKey -Name "PrivacyConsentStatus" -Value 1 -PropertyType DWORD -Force 
New-ItemProperty -Path $regKey -Name "SkipMachineOOBE" -Value 1 -PropertyType DWORD -Force 
New-ItemProperty -Path $regKey -Name "ProtectYourPC" -Value 3 -PropertyType DWORD -Force 
New-ItemProperty -Path $regKey -Name "SkipUserOOBE" -Value 1 -PropertyType DWORD -Force 

# Disable "Send Microsoft info about your device" at startup
Set-ItemProperty -Path $regKey -Name "OOBECompleted" -Value 1 -Force

# Disable post upgrade reboot
Set-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" -Name "NoAutoRebootWithLoggedOnUsers" -Value 1 -Force

# Force activating user account at startup (avoid login prompt) 
Set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" -Name "DontDisplayLastUserName" -Value 1 -Force


# Variables for the agent
$AgentURL = "https://vstsagentpackage.azureedge.net/agent/4.248.1/vsts-agent-win-x64-4.248.1.zip"
$AgentDirectory = "C:\agentinteractive"
[System.Environment]::SetEnvironmentVariable("AGENT_USERCAPABILITY_Interactive", "true", [System.EnvironmentVariableTarget]::Machine)

# Download / install agent
New-Item -ItemType Directory -Path $AgentDirectory -Force
Invoke-WebRequest -Uri $AgentURL -OutFile "$AgentDirectory\agent.zip"
Expand-Archive -Path "$AgentDirectory\agent.zip" -DestinationPath $AgentDirectory

# Agent configuration
cd $AgentDirectory
.\config.cmd --unattended --addcap InteractiveMode true --auth pat --token "${var.token}" --pool YOUR_POOL --url https://dev.azure.com/yourOrganization --agent $env:COMPUTERNAME-interactive --acceptTeeEula --runAsAutoLogon --windowsLogonAccount $user --windowsLogonPassword $password

#restart 
Restart-Computer -Force

Key Points in the Script

  1. Preventing Sleep and Timeout:

    • Sleep and display timeouts are disabled using the powercfg command.

    • Screen savers and session logouts are also disabled.

  2. Auto-Login Configuration:

    • Ensures the VMSS instance automatically logs in with the specified user.
  3. Azure DevOps Interactive Agent:

    • Downloads and installs the Azure DevOps agent.

    • Configures it in interactive mode, essential for UI tests.

    • Important: You’ll need to give a token in order to be able to communicate with Azure Devops.

  4. Disabling Unnecessary Prompts:

    • Removes OOBE and privacy prompts to streamline the user experience.
  5. Restart to Apply Settings:

    • A restart ensures all configurations take effect.

Testing Interactive Agents

Once the script is executed:

  1. The VM / VMSS instance will automatically reboot / log in with the specified user.

  2. The Azure DevOps agent will register in interactive mode.

  3. UI tests can now run seamlessly on the VMSS instance.

To verify:

  • Check the Azure DevOps agent pool to confirm the agent is online.

  • Run a UI test pipeline and monitor the execution.

If the registration worked, we can see our agents in the agent list of the agent-pool.

Exemple of interactive test

pool:

  name: "YourPool"

steps:

  - task: PowerShell@2
    displayName: "Open Notepad, write 'Hello' and take a screenshot"
    inputs:
      targetType: "inline"
      script: |
        # Launch Notepad
        Start-Process notepad.exe
        Start-Sleep -Seconds 2  

        # write something
        Add-Type -AssemblyName System.Windows.Forms
        [System.Windows.Forms.SendKeys]::SendWait("Hello")

        # Take a screenshot

        $screenshot = [System.Drawing.Bitmap]::new([System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width, [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height)
        $graphics = [System.Drawing.Graphics]::FromImage($screenshot)
        $graphics.CopyFromScreen([System.Drawing.Point]::Empty, [System.Drawing.Point]::Empty, $screenshot.Size)
        $screenshotPath = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY\screenshot.png"
        $screenshot.Save($screenshotPath, [System.Drawing.Imaging.ImageFormat]::Png)
        Write-Output "Screenshot saved to $screenshotPath"

        # Close notepad
        Stop-Process -Name notepad -Force

Conclusion

Running interactive tests on Windows 10 VMSS requires careful configuration to ensure the environment stays active and user session remains open. The script provided simplifies this setup, enabling robust automation of UI-based tasks. By leveraging Azure DevOps agents in interactive mode, you can confidently execute tests that replicate real user interactions.

0
Subscribe to my newsletter

Read articles from Christophe Perroud directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Christophe Perroud
Christophe Perroud