Protecting your Citrix desktops on Nutanix using powershell
3 min read

Another week, another idea comes up to check if I could make it happen with Powershell. I wanted to write a script that checks my current machine catalog and puts VMs that are not in a Nutanix Protection Domain in a PD to make sure that we can protect those VMs using the policies Nutanix offers.
The following tasks had to be automated using powershell:
- Check if a machine catalog is persistent
- Get all desktops in the machine catalog
- Check if there’s a Nutanix Protection Domain available with a certain prefix (read more here).
- Check if the VM isn’t already protected
- Check if the VM can be added there
- If so: Add the VM
- If not: create a Protection Domain and add the VM(s) there
I took the liberty of borrowing bits and pieces of Steven Poitras that is published on the Nutanix GitHub here:NTNX-Protect-VM.ps1 (Powershell script) and Sandeep has been helped me by answering my questions patiently.
Here’s the script:
<#
.SYNOPSIS
Will fetch all desktops from a machine catalogs and put them into protection domains
.DESCRIPTION
Will fetch all desktops from a machine catalogs and put them into protection domains but it will asume at least one PD with the prefix is there.
.EXAMPLE
PS C:\PSScript > .\XD_PD_v1.ps1
.INPUTS
None. You cannot pipe objects to this script.
.OUTPUTS
No objects are output from this script.
.NOTES
NAME: XD_PD_v1.ps1
VERSION: 1.0
AUTHOR: Kees Baggerman with some help from Sandeep MP and Steven Potrias a
LASTEDIT: August 2017
#>
[CmdletBinding(SupportsShouldProcess = $False, ConfirmImpact = "None", DefaultParameterSetName = "") ]
Param(
# Nutanix cluster IP address
[Parameter(Mandatory = $false)]
[Alias('IP')] [string] $nxIP,
# Nutanix cluster username
[Parameter(Mandatory = $false)]
[Alias('User')] [string] $nxUser,
# Nutanix cluster password
[Parameter(Mandatory = $true)]
[Alias('Password')] [Security.SecureString] $nxPassword
)
# Importing the proper cmldlets and snapins
Import-Module "C:\Program Files (x86)\Nutanix Inc\NutanixCmdlets\Modules\NutanixCmdletsPSSnapin.dll"
Add-PSSnapin Nutanix*, Citrix*
# Defining script variables
$DDC = ""
$nxIP = ""
$nxUser = ""
$pdPrefix = "XenDesktop-PD"
$vmPerPD = ""
$MachineCatalog = ""
# Connecting to the Nutanix Cluster IP
Connect-NutanixCluster -Server $nxIP -UserName $nxUser -Password $nxPassword -AcceptInvalidSSLCerts
# Checking if the Machine catalog is persistent
$persistentMCs = Get-BrokerCatalog | where {$_.PersistUserChanges -eq "OnLocal"}
if($persistentMCs.name -match $MachineCatalog) {
# Collecting the Persistent Machine Catalog and putting them into a variable
$VMs = get-brokermachine -adminaddress $ddc -CatalogName $machinecatalog
foreach ($VM in $VMs) {
$VMName = $VM.MachineName.Split("\")[1]
# Get existing PDs matching prefix
$pds = Get-NTNXProtectionDomain | where {$_.name -match $pdPrefix}
# Get Un-protected VMs
$unProtectedVM = Get-NTNXUnprotectedVM | where {$_.vmName -eq $VMName}
# Stop the script when there's no unprotected VMs
if ($unProtectedVM.count -gt 0) {
Write-Host "Found $($unProtectedVM.count) unprotected vms..."
} else {
Write-Host "No unprotected VMs found, exiting..."
break
}
# Getting the PDs count and determine next action
if ($pds.Count -gt 0) {
# Matching PDs exist
Write-Host "Found $($pds.Count) matching existing PDs with space..."
[int]$existingPDSpace = 0
# Find available space in existing PDs
$pdsWithSpace = $pds | where {($_.vms).count -lt $vmPerPD}
$pdsWithSpace | %{
# Find available space
$l_pdSpace = $vmPerPD - ($_.vms).count
# Add available space to counter
$existingPDSpace += $l_pdSpace
}
# Find starting increment from existing
$startingInt = (($pds.name).split("-") -match "^[0-9]+$" | measure -Maximum).maximum + 1
} else {
Write-Host "Found $($pds.Count) matching existing PDs with space..."
$startingInt = 0
}
# Find needed space
$newPDSpace = if ($existingPDSpace -lt $unProtectedVM.Count) {$unProtectedVM.Count - $existingPDSpace}
# Find number of new PD required
$numNewPD = [Math]::Round($newPDSpace/$vmPerPD)
# Create $numNewPD Protection Domains
$pdsWithSpace | %{
# Get avail space
$l_availSpace = $vmPerPD - ($_.vms).count }
# Find number of new PD required
$numNewPD = [Math]::Round($newPDSpace/$vmPerPD)
# Create $numNewPD Protection Domains
Write-Host "Creating $numNewPD new PDs..."
if ($numNewPD -gt 0) {
for($i = 0;$i -lt $numNewPD;$i++)
{
# $l_pdName = $pdPrefix+"-"+$i
$l_pdName = "$pdPrefix-$i"
# Create PD
Write-Host "Creating PD: $l_pdName"
$l_PD = Add-NTNXProtectionDomain -value $l_pdName
# Add cron schedule to PD
$pdsWithSpace += $l_PD
}
}
$pdsWithSpace | %{
# Get avail space
$l_availSpace = $vmPerPD - ($_.vms).count }
# Add VMs to PD (either existing or new)
if ($Pds.name -notmatch $pdPrefix) {
Write-Host "Adding $($l_vmObj.count) VMs to PD: $($_.name)"
Add-NTNXProtectionDomainVM -Name $VMName -PdName $($_.name)
}
else {
Write-Host "Adding VMs to PD: $($pds.name)"
Add-NTNXProtectionDomainVM -Name $VMName -PdName $($pds.name)
}
break
}
}
else { write-host "No persisent machine catalog defined"}
Kees Baggerman
Latest posts by Kees Baggerman (see all)
- When the Orchard Ships Production Software: AI-Augmented Development in the Real World - May 17, 2026
- Nutanix Documentation Script v5.0: Visual Reports, Brand Templates, and Seven Embedded Diagrams - May 15, 2026
- Nutanix AHV and Citrix MCS: Adding a persistent disk via Powershell – v2 - November 19, 2019
- Recovering a Protection Domain snapshot to a VM - September 13, 2019
- Checking power settings on VMs using powershell - September 11, 2019


One Comment