ScreenshotNeo

BlogHow-to

How to Automate Screenshots in VMware

Use vSphere’s createScreenshot_Task API or PowerCLI to capture VM console images reliably, with vSphere 8 migration notes and troubleshooting.

By the ScreenshotNeo team1 October 20268 min read

Direct answer: automate VMware screenshots with the vSphere VirtualMachine.createScreenshot_Task operation. In PowerCLI, retrieve the VM view, call CreateScreenshot_Task(), wait for the task, then retrieve the PNG path returned by the task or by the interface you used. The VM must be powered on and the account needs the VirtualMachine.Interact.CreateScreenshot privilege.

The operation has existed since vSphere API 4.0. If the VM is powered off, vSphere reports InvalidPowerState. In vSphere 8, the older /screen POST route is no longer implemented; migrate scripts to the API operation. Broadcom documents the Managed Object Browser (MOB) method and the ESXi host UI as alternatives.

What the screenshot API captures

createScreenshot_Task creates an image of the virtual machine’s console display. It is different from a VM snapshot: a screenshot is an image file, while a snapshot stores VM state for rollback or testing.

Method Best use Requirements vSphere 8 note
createScreenshot_Task Repeatable server-side console capture Powered-on VM and VirtualMachine.Interact.CreateScreenshot Supported API path
MOB method Manual recovery or migration work Browser access and privileges Image is saved in the VM directory and must be retrieved
ESXi host UI One-off operator capture Access to the host interface Documented fallback
Invoke-VMScript Screenshot generated by an application inside the guest Powered-on VM, VMware Tools, guest credentials and guest-operation privileges Does not capture the vSphere console automatically

Prerequisites and permissions

  1. Install VMware PowerCLI on the automation host.
  2. Connect to vCenter with an account that has VirtualMachine.Interact.CreateScreenshot.
  3. Identify the VM by inventory path, name, or managed object reference.
  4. Confirm the VM is powered on and has reached the display state you need.
  5. Choose a deterministic filename and retention policy for generated PNG files.

Use least-privilege access where possible. Guest-operation permissions required by Invoke-VMScript are separate from the screenshot interaction privilege.

PowerCLI: capture a VM screenshot

This script connects to vCenter, selects a VM, invokes the API task, waits for completion, and prints the task result. The returned result identifies the generated image location for the selected vSphere interface.

# Requires VMware.PowerCLI
$vcServer = "vcenter.example.com"
$vmName   = "Ubuntu-Web-01"

Connect-VIServer -Server $vcServer

$vm = Get-VM -Name $vmName -ErrorAction Stop
if ($vm.PowerState -ne "PoweredOn") {
    throw "VM '$vmName' must be powered on. Current state: $($vm.PowerState)"
}

# Get the underlying VirtualMachine managed object.
$vmView = Get-View -Id $vm.Id

# createScreenshot_Task returns a Task managed object reference.
$taskMoRef = $vmView.CreateScreenshot_Task()
$taskView = Get-View -Id $taskMoRef

while ($taskView.Info.State -eq "running" -or $taskView.Info.State -eq "queued") {
    Start-Sleep -Seconds 2
    $taskView.UpdateViewData("Info.State", "Info.Error", "Info.Result")
}

if ($taskView.Info.State -ne "success") {
    $message = $taskView.Info.Error.LocalizedMessage
    throw "Screenshot task failed: $message"
}

$result = $taskView.Info.Result
Write-Host "Screenshot task completed. Returned location/result: $result"

Disconnect-VIServer -Server $vcServer -Confirm:$false

Depending on the PowerCLI and vCenter version, the task result is a datastore or VM-directory path rather than the image bytes. Retrieve that path through the datastore browser, an HTTPS interface, or the method documented for your environment, then rename the file using a stable convention such as vm-name_2026-10-01T120000Z.png.

Selecting a VM by inventory path

$vm = Get-VM -Location "Production" -Name "Ubuntu-Web-01" -ErrorAction Stop
$vmView = Get-View -Id $vm.Id
$taskMoRef = $vmView.CreateScreenshot_Task()

Capturing many VMs

$vms = Get-VM -Tag "Nightly-Screenshot" | Where-Object PowerState -eq "PoweredOn"

foreach ($vm in $vms) {
    try {
        $view = Get-View -Id $vm.Id
        $taskRef = $view.CreateScreenshot_Task()
        $task = Get-View -Id $taskRef
        do {
            Start-Sleep -Seconds 2
            $task.UpdateViewData("Info.State", "Info.Error", "Info.Result")
        } while ($task.Info.State -in @("queued", "running"))

        if ($task.Info.State -eq "success") {
            [pscustomobject]@{ VM = $vm.Name; Result = $task.Info.Result; Status = "Success" }
        } else {
            [pscustomobject]@{ VM = $vm.Name; Result = $task.Info.Error.LocalizedMessage; Status = "Failed" }
        }
    } catch {
        [pscustomobject]@{ VM = $vm.Name; Result = $_.Exception.Message; Status = "Failed" }
    }
}

Calling the API with HTTPS clients

The operation is part of the vSphere managed-object API. The examples below send a SOAP request to /sdk. They assume you already have a vCenter session cookie in VSPHERE_SESSION and the target VM managed-object identifier in VM_MOREF. Obtain the session with an authenticated vSphere SDK or the login method supported by your vCenter version.

cURL

export VCENTER='https://vcenter.example.com'
export VSPHERE_SESSION='YOUR_SESSION_COOKIE'
export VM_MOREF='vm-123'

curl --fail --silent --show-error \
  --insecure \
  -H "Cookie: vmware-api-session-id=${VSPHERE_SESSION}" \
  -H 'Content-Type: text/xml; charset=utf-8' \
  --data "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:vim25=\"urn:vim25\">
  <soapenv:Body>
    <vim25:CreateScreenshot_Task>
      <_this type=\"VirtualMachine\">${VM_MOREF}</_this>
    </vim25:CreateScreenshot_Task>
  </soapenv:Body>
</soapenv:Envelope>" \
  "${VCENTER}/sdk"

Python

import os
import requests

vcenter = os.environ["VCENTER"].rstrip("/")
session_id = os.environ["VSPHERE_SESSION"]
vm_moref = os.environ["VM_MOREF"]

body = f'''<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:vim25="urn:vim25">
  <soapenv:Body>
    <vim25:CreateScreenshot_Task>
      <_this type="VirtualMachine">{vm_moref}</_this>
    </vim25:CreateScreenshot_Task>
  </soapenv:Body>
</soapenv:Envelope>'''

response = requests.post(
    f"{vcenter}/sdk",
    headers={
        "Cookie": f"vmware-api-session-id={session_id}",
        "Content-Type": "text/xml; charset=utf-8",
    },
    data=body,
    verify=True,
    timeout=90,
)
response.raise_for_status()
print(response.text)

Node.js

const vcenter = process.env.VCENTER.replace(/\/$/, '');
const session = process.env.VSPHERE_SESSION;
const vmMoref = process.env.VM_MOREF;

const body = `<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:vim25="urn:vim25">
  <soapenv:Body>
    <vim25:CreateScreenshot_Task>
      <_this type="VirtualMachine">${vmMoref}</_this>
    </vim25:CreateScreenshot_Task>
  </soapenv:Body>
</soapenv:Envelope>`;

const response = await fetch(`${vcenter}/sdk`, {
  method: 'POST',
  headers: {
    'Cookie': `vmware-api-session-id=${session}`,
    'Content-Type': 'text/xml; charset=utf-8'
  },
  body
});

if (!response.ok) throw new Error(`${response.status} ${await response.text()}`);
console.log(await response.text());

For production code, use the official vSphere SDK for your language when available. SDK clients handle managed-object references, task polling, authentication, and version details more safely than hand-built XML.

vSphere 8 migration: replace the old /screen URL

Older scripts commonly posted to a URL shaped like /screen?id=...&h=...&w=.... Broadcom states that this route is no longer implemented in vSphere 8. Replace it with createScreenshot_Task. If a migration cannot use an SDK immediately, the MOB method or ESXi host UI can produce the image; the MOB image is saved in the VM directory and then has to be retrieved.

  1. Remove calls to the legacy /screen endpoint.
  2. Resolve the target VM to its VirtualMachine managed object.
  3. Call createScreenshot_Task.
  4. Poll the task until it is success or error.
  5. Retrieve the returned image location using your datastore or host workflow.

Guest-side screenshots with Invoke-VMScript

Use Invoke-VMScript when the required image is generated inside the guest, such as an application window captured by a guest utility. It runs PowerShell, batch, or Bash in the guest; it does not capture the vSphere console by itself.

$vm = Get-VM -Name "Ubuntu-Web-01"
$guestCredential = Get-Credential

Invoke-VMScript \
  -VM $vm \
  -GuestCredential $guestCredential \
  -ScriptType Bash \
  -ScriptText 'gnome-screenshot -f /tmp/application.png'

The VM must be powered on, VMware Tools must be installed and running, guest credentials must work, and the account needs guest-operation privileges. Network connectivity to the ESXi system is also required. Copy the resulting file out of the guest with a separate transfer method.

Snapshots are not screenshots

New-Snapshot records VM state for rollback. With -Quiesce on a powered-on VM, VMware Tools can quiesce the guest file system. It does not create a PNG of the console.

New-Snapshot -VM "Ubuntu-Web-01" -Name "Before-Automation-Test" -Quiesce

Reliability, performance and cost

  • Wait for readiness: a powered-on VM can still be booting. Poll guest state or use an application readiness check before requesting the capture.
  • Poll tasks: do not assume the API call completed when the task reference is returned. Handle queued, running, success, and error.
  • Throttle batches: avoid launching a large number of captures simultaneously. Queue work and limit concurrency to protect vCenter and datastore operations.
  • Keep retrieval separate: record the task result immediately, then download or copy the image with retries.
  • Use TLS verification: keep certificate validation enabled in production. Do not copy --insecure from a diagnostic command into a scheduled job.
  • Retention: screenshots can consume datastore or object storage capacity. Set expiration rules and deterministic names.
  • Cost: vSphere screenshot tasks have no ScreenshotNeo usage charge, but they consume vCenter, ESXi, datastore, network, and storage resources.

Troubleshooting

Symptom Cause Fix
InvalidPowerState The VM is not powered on Start the VM or skip it until it reaches PoweredOn.
Permission denied Missing VirtualMachine.Interact.CreateScreenshot Grant the privilege at the VM or appropriate inventory level, then reconnect.
404 or unsupported /screen request Legacy route removed in vSphere 8 Use createScreenshot_Task, MOB, or the ESXi host UI.
Task remains queued vCenter or host is busy Continue polling, reduce batch concurrency, and inspect vCenter task events.
Task fails after boot Display is not ready or the host is under pressure Wait for guest readiness, retry with backoff, and check host and datastore health.
No file where expected The API returned a VM-directory or datastore path Use the returned result and retrieve it through the matching datastore or host interface.
Invoke-VMScript fails VMware Tools, guest credentials, network access, or guest privileges are missing Verify each prerequisite and test a harmless guest command first.
SOAP authentication error Expired or incorrectly supplied session cookie Create a new vCenter session and send the cookie expected by that API version.

Or skip the browser setup

If the image you need is a website rather than a VMware console, ScreenshotNeo provides a single screenshot API request. Its capture flow accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status.

See the ScreenshotNeo API documentation for all options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also has an MCP server for Claude, Cursor, and other MCP clients, with take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

FAQ

Does the VM have to be powered on?

Yes. The API reports InvalidPowerState when the VM is not powered on.

Can I use a VM snapshot to get a screenshot?

No. A snapshot stores VM state. Use createScreenshot_Task for a console image.

Is VMware Tools required?

Not for createScreenshot_Task. VMware Tools are required for guest commands run with Invoke-VMScript.

What changed in vSphere 8?

The former /screen POST method is no longer implemented. Use the API task operation or the documented MOB and ESXi alternatives.

Can Invoke-VMScript capture the vSphere console?

No. It runs a script inside the guest. The script must use guest software to create its own image.

How should I store generated images?

Use the task result to locate the file, copy it to controlled storage, assign deterministic names, and enforce retention limits.