Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Selenium WebDriver for .NET: open a Chrome session, navigate to your VSTS (now Azure DevOps) URL, complete authentication, wait for the page or target element, then cast the driver to ITakesScreenshot and call GetScreenshot().SaveAsFile(). The result is a PNG of the page currently loaded in the browser.

What “VSTS” means today

Visual Studio Team Services (VSTS) was renamed Azure DevOps Services. Existing organizations still have URLs and code that use older terminology, but current Microsoft tooling and pipeline tasks use Azure DevOps names. The same Selenium approach works with either an older VSTS address or a current https://dev.azure.com/your-org/your-project address.

Prerequisites

  • A .NET project (for example, a console app, MSTest project, NUnit project, or xUnit project).
  • The Selenium WebDriver .NET package and a browser driver compatible with the browser installed on the machine. Selenium Manager can obtain a matching driver in current Selenium releases; pin browser and driver versions in controlled build images when reproducibility matters.
  • Permission to access the Azure DevOps organization, project and page you intend to capture.
  • A non-interactive sign-in strategy for automation. Do not put a personal password, access token or cookie in source control.

Capture the visible VSTS page

Install Selenium’s WebDriver package with your normal NuGet workflow, then use this complete minimal example. Replace the URL with your organization and project.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

using var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://dev.azure.com/your-org/your-project");

var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
screenshot.SaveAsFile("vsts-page.png");

GetScreenshot() represents an image of the page currently loaded in the browser. SaveAsFile writes the image as PNG. This captures the current viewport, not automatically the entire document. The browser must have reached the page you want; a screenshot of a sign-in form, redirect or loading shell is still a technically valid screenshot.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Wait for the page instead of using a fixed sleep

Azure DevOps renders substantial content asynchronously. Prefer an explicit wait for a stable element that proves the requested view is ready. Selectors vary by organization and UI version, so inspect your page and choose a durable attribute or heading.

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;

using var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://dev.azure.com/your-org/your-project/_ boards/board");

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
wait.Until(d => d.FindElement(By.CssSelector("[data-testid='some-stable-element']")).Displayed);

((ITakesScreenshot)driver).GetScreenshot().SaveAsFile("board.png");

Use a real selector from your application; the data-testid value above is illustrative and is not an Azure DevOps universal selector. If the page has no reliable marker, wait for a URL condition, document title, or a specific text node, and combine it with a short, bounded delay only when an animation or late image genuinely requires one.

Authentication considerations

Navigate first and complete the organization’s required sign-in flow. Microsoft Entra ID policies can include redirects, multi-factor authentication, conditional access and organization-specific identity providers. A local headed browser can support an interactive sign-in, but a CI job generally needs a service-safe identity and a headless-compatible flow. Never attempt to bypass a bot check or MFA policy; configure an approved test account, pre-authenticated profile, or test environment instead. Verify that the screenshot shows the intended project rather than an access-denied or sign-in page.

Full-page and clipped screenshots

A normal Selenium screenshot is viewport-sized. For a page longer than the browser window, use the browser’s DevTools screenshot command and its capture settings. DevTools supports capturing beyond the viewport and clipping to a selected rectangle; PNG is the default format.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.DevTools;
using OpenQA.Selenium.DevTools.V125.Page;

using var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://dev.azure.com/your-org/your-project");

// Use an explicit wait appropriate to the page before this call.
var devTools = driver.GetDevToolsSession();
var page = new OpenQA.Selenium.DevTools.V125.Page.PageAdapter(devTools);
var result = await page.CaptureScreenshot(new CaptureScreenshotCommandSettings
{
    Format = ScreenshotFormat.Png,
    CaptureBeyondViewport = true
});

File.WriteAllBytes("vsts-full-page.png", Convert.FromBase64String(result.Data));

DevTools namespaces are versioned in Selenium .NET. The version in the example must match the DevTools API assembly available in your installed Selenium package; if your package exposes a different version, use that namespace and its corresponding page adapter. API signatures can change between Selenium releases, so compile this example against the exact package version used by your project.

Capture a region

Pass a Clip rectangle to the same DevTools command when only part of the page is needed. Coordinates are CSS pixels relative to the page, and the rectangle must be inside the page’s layout area.

var result = await page.CaptureScreenshot(new CaptureScreenshotCommandSettings
{
    Format = ScreenshotFormat.Png,
    Clip = new Viewport
    {
        X = 0,
        Y = 120,
        Width = 1200,
        Height = 700,
        Scale = 1
    }
});
File.WriteAllBytes("vsts-region.png", Convert.FromBase64String(result.Data));

For a single card or panel, scrolling it into view and using Selenium’s viewport capture may be simpler. DevTools clipping is preferable when the coordinates are known and you need a deterministic rectangle.

Make captures useful and repeatable

Control the viewport

Set a consistent window size before navigation so screenshots do not change with the machine’s desktop resolution.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
driver.Manage().Window.Size = new System.Drawing.Size(1440, 1000);

In headless mode, set the size through Chrome options as well; otherwise responsive breakpoints can produce a different layout than local runs.

Headless execution for services and CI

An agent running as a service may not have an interactive desktop. Configure Chrome for headless operation and disable assumptions about a visible display.

var options = new ChromeOptions();
options.AddArgument("--headless=new");
options.AddArgument("--window-size=1440,1000");
options.AddArgument("--disable-gpu");
using var driver = new ChromeDriver(options);

Microsoft-hosted Azure Pipelines agents provide popular browsers and matching drivers. Self-hosted service agents need the browser, driver and any Linux display dependencies installed and maintained by you. Confirm the browser starts under the same account that runs the agent.

Lazy-loaded content and scrolling

A full-page operation can still miss content that an application loads only after scrolling. If the page uses lazy loading, scroll in controlled increments, wait for the network-driven content to appear, then capture. Do not treat the image as evidence that every asynchronous request completed unless your readiness condition proves it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Attach the image to Azure DevOps test results

Saving a PNG on the agent is not enough for it to appear in an Azure DevOps test report. Add the file explicitly through the test framework’s attachment API.

MSTest

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class VstsScreenshotTests
{
    public TestContext TestContext { get; set; }

    [TestMethod]
    public void CapturePage()
    {
        // Create driver, navigate, wait and save the image here.
        var fileName = Path.GetFullPath("vsts-page.png");
        TestContext.AddResultFile(fileName);
    }
}

Call AddResultFile after the file has been written and while the test is still running. Use an absolute path to avoid ambiguity about the test runner’s working directory.

NUnit 3.7 and later

TestContext.AddTestAttachment(Path.GetFullPath("vsts-page.png"));

Place the call after saving the image. The pipeline’s test-result publication step must run even when the test fails; configure cleanup and result publishing so a diagnostic screenshot is not discarded with the failed process.

Choosing the right capture workflow

Need Approach Output and limitation
What a user sees in the current window ITakesScreenshot.GetScreenshot() Viewport PNG; simplest and most portable.
A document longer than the viewport DevTools capture with CaptureBeyondViewport Full-page PNG when the browser and page support the command.
One rectangle DevTools Clip Deterministic region in CSS-pixel coordinates.
Evidence in a test report Save first, then add a framework attachment Local file plus an Azure DevOps test-result attachment.
Service-hosted or Microsoft-hosted CI Headless Chrome with a controlled window size Works without a desktop; requires matching browser dependencies.

Troubleshooting

The image is a sign-in page or blank shell

The capture happened before authentication or asynchronous rendering finished. Log the final URL and title, wait for a page-specific element, and make the identity flow explicit. Check that the test account can open the project in a normal browser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

SessionNotCreatedException or the driver will not start

The browser and driver are incompatible, the executable is unavailable to the service account, or headless dependencies are missing. Update the browser and driver together, let Selenium Manager resolve them where appropriate, and run the same command under the CI account.

Element-not-found or timeout errors

Selectors copied from a transient class or a different organization UI are brittle. Inspect the rendered page, choose a stable attribute, and increase the timeout only after confirming the page is genuinely slow. A longer timeout cannot fix an incorrect selector.

The “full-page” image is still truncated

Use the DevTools capture path rather than the viewport screenshot, verify that CaptureBeyondViewport is enabled, and account for lazy-loaded content. If the browser’s DevTools version does not expose the setting, update Selenium or use a compatible browser build.

The screenshot exists but is absent from the test report

The test framework attachment call was omitted, the path was relative to a different working directory, or the pipeline did not publish test results. Attach an absolute path and inspect the test-results publication task.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CI captures differ from local captures

Responsive breakpoints, fonts, browser versions, timezone, authentication state and headless rendering can all change pixels. Fix the window size, browser image and test data; avoid visual comparisons that depend on uncontrolled external content.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One request returns a PNG, JPEG, WebP or PDF, while its capture pipeline accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups and chat widgets before the shot. You can turn each cleanup step off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status.

For a direct call, see the ScreenshotNeo API documentation:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://dev.azure.com/your-org/your-project -o vsts-page.webp

The same endpoint can be called from C# or another service without managing a browser:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
using System.Net.Http;

using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(90) };
var url = "https://api.screenshotneo.com/v1/shot?access_key=YOUR_API_KEY&url=" +
          Uri.EscapeDataString("https://dev.azure.com/your-org/your-project");
var bytes = await http.GetByteArrayAsync(url);
await File.WriteAllBytesAsync("vsts-page.webp", bytes);

For completeness, equivalent calls are:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://dev.azure.com/your-org/your-project"}, timeout=90)
open("vsts-page.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://dev.azure.com/your-org/your-project' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, device presets and custom viewports, retina scale, PDF paper and margin controls, custom CSS and JavaScript, clicks, selector or network-idle waits, request blocking, headers, cookies, user agents, Authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, a usage API and an OpenAPI specification. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

Every plan includes every feature. The free plan provides 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, with yearly billing giving two months free. Sign up for the free plan to try the API without a card.

Security and reliability checklist

  • Keep API keys, Azure credentials and cookies in a secret store.
  • Use a dedicated test identity with the least access needed for the project.
  • Close the driver in a using block so failed tests do not leave browser processes running.
  • Write screenshots to unique, job-scoped paths when tests run in parallel.
  • Record the URL, viewport, browser version and readiness condition alongside important evidence.
  • Expect third-party content, feature flags and permission changes to alter pixels; use stable test data for comparisons.

Frequently Asked Questions

Can Selenium capture a VSTS page without opening a visible browser window?

Yes. Configure Chrome with a headless argument and a fixed window size, then run the same WebDriver screenshot code. Service agents commonly require this mode.

Does GetScreenshot capture the entire Azure DevOps page?

No. It captures the current viewport. Use the DevTools screenshot command with CaptureBeyondViewport for a full-page capture or Clip for a defined region.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why is my screenshot not shown in an Azure DevOps test report?

A file saved on the agent is not automatically a report artifact. Add it with MSTest TestContext.AddResultFile or NUnit TestContext.AddTestAttachment, then publish test results.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.