Virtualization
banshee/virtualization-collection/virtualization/hyperv/winrm/
checkpoints
The number and the age of the oldest checkpoint of each virtual machine of a Hyper-V host, by WinRM and PowerShell.
par banshee1 installation1 version
v1Dernière
Publiée le 27 octobre 2026. First version.
// The checkpoints of the virtual machines of a Hyper-V host, by WinRM: for each machine, how many it has and
// the age of the oldest. A checkpoint forgotten grows its differencing disk until the volume is full, and a
// backup that failed may leave its own behind.
// Runs one PowerShell script that only reads two WMI classes of Hyper-V, whose properties and values Microsoft
// documents (root\virtualization\v2): Msvm_ComputerSystem (ElementName, the name; Name, the id of the
// machine, a GUID that tells it from the host) and Msvm_VirtualSystemSettingData, one per checkpoint of a
// machine (VirtualSystemType "Microsoft:Hyper-V:Snapshot:..."; VirtualSystemIdentifier, the id of the
// machine; CreationTime, turned into days on the host, by its own clock). The recovery points that Hyper-V
// Replica keeps on a replica ("Microsoft:Hyper-V:Snapshot:Replica:...") are left to it.
// With $ErrorActionPreference set to Stop, a right missing on Hyper-V ends the script with no answer, rather
// than an empty list.
need('protocol:winrm');
per('vm', 'Virtual machines');
output('checkpoints', 'number', array('label' => 'Checkpoints', 'per' => 'vm'));
output('oldest', 'number', array('label' => 'Age of the oldest checkpoint', 'unit' => 'd', 'per' => 'vm'));
watch.above('old', 'Old checkpoint', 'oldest', array('warning' => 7, 'critical' => 30));
watch.above('many', 'Many checkpoints', 'checkpoints', array('warning' => 5, 'enabled' => false));
$read = winrm.run("$ErrorActionPreference = 'Stop'; "
. "[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding; "
. "$now = Get-Date; "
. "ConvertTo-Json -Compress -Depth 3 -InputObject @{"
. "vms = @(Get-CimInstance -Namespace root\\virtualization\\v2 -ClassName Msvm_ComputerSystem"
. " | Where-Object Name -match '^[0-9A-Fa-f]{8}-' | Select-Object ElementName, Name); "
. "checkpoints = @(Get-CimInstance -Namespace root\\virtualization\\v2 -ClassName Msvm_VirtualSystemSettingData"
. " | Where-Object { $_.VirtualSystemType -like 'Microsoft:Hyper-V:Snapshot:*'"
. " -and $_.VirtualSystemType -notlike 'Microsoft:Hyper-V:Snapshot:Replica:*' }"
. " | Select-Object VirtualSystemIdentifier, @{Name = 'Days'; Expression = {($now - $_.CreationTime).TotalDays}})}");
$answer = text.trim($read['output']);
if (list.count(text.match($answer, '^\{')) == 0) {
fail('misconfigured', 'PowerShell did not return the checkpoints', $answer . $read['errors']);
}
$read_checkpoints = json.read($answer);
// {"checkpoints": [{"VirtualSystemIdentifier": "0B8C5A1E-...", "Days": 12.4}, ...]}: counted and the oldest
// kept, by machine.
$count = array();
$oldest = array();
foreach ($read_checkpoints['checkpoints'] as $checkpoint) {
$id = text.lower($checkpoint['VirtualSystemIdentifier']);
$count[$id] = list.get($count, $id, 0) + 1;
if ($checkpoint['Days'] > list.get($oldest, $id, -1)) {
$oldest[$id] = $checkpoint['Days'];
}
}
// {"vms": [{"ElementName": "web01", "Name": "0B8C5A1E-..."}, ...]}: each machine, with 0 when it has no
// checkpoint. Two machines of one name each keep their own, the second carrying its id, as in
// hyperv/winrm/vms.
$seen = array();
foreach ($read_checkpoints['vms'] as $vm) {
$name = $vm['ElementName'];
if (list.has($seen, $name)) {
$name = $name . ' #' . $vm['Name'];
}
$seen[$name] = true;
$id = text.lower($vm['Name']);
value('checkpoints', list.get($count, $id, 0), $name);
if (list.has($oldest, $id)) {
value('oldest', number.round($oldest[$id], 1), $name);
}
}