Unleash the awesome superpowers of the Nutanix PowerShell cmdlets

073015_1027_Unleashthea1.jpgI don’t know if you are like me, but I’m a scripting nut. I figure that if I have to do something once, there is a fair chance I’ll have to do it again multiple times, sometimes months or even years later and I don’t want to have to bother to research it again, so why not simply script it now and sharpen my scripting skills in the process?

I also find that if you can script something, you have generally a better understanding of how it actually works as opposed to just pointing and clicking in a UI.

I started scripting using NT shell commands in Windows NT 4.0, and to this day I still have my beat up copy of Tim Hill’s « Windows NT Shell Scripting » which I use every now and then.

For a few years, I was also a hardcore Perl user and created all kinds of scripts for Windows and Active Directory management.

That was until PowerShell came out a few years ago. That scripting language is just something else. Once you start using it, you can’t stop. I’ve done tons of automation for Active Directory, Windows troubleshooting and VMware vSphere administration and reporting using the outstanding VMware vSphere PowerCLI extensions.

Which is why I’m really glad Nutanix came up with a bunch of PowerShell cmdlets that can help you automate operations on your Nutanix clusters. While those are relatively low touch solutions, there are certain features that can be tedious to setup for a large environment such as protection domains.

Others have already blogged about the Nutanix cmdlets, like Andre Leibovici‘s « Nutanix 4.0 PowerShell goes GA » or Kees Baggerman‘s excellent « Releasing the Nutanix Documentation Script », but I felt there wasn’t any good blog that took you through the process step by step and provided a few good examples of how to apply those cmdlets for true operational purposes. So here goes my two-cent.

 

Step 1 – Setting Nutanix PowerShell cmdlets up

First, let me start by saying that admittedly, the way the cmdlets have to be setup and used currently is not ideal. There isn’t a place on the web where you can simply download the cmdlets – you have to do it from Prism, the Nutanix web management interface.

Also, the built-in documentation of the cmdlets is somewhat lacking and you often have to dig into the support portal API reference document to get the cmdlets to do what you need them to do.

But enough ranting, let’s get down to business.

To install the cmdlets and get started:

  1. Connect to your Prism interface by opening the https://<cluster or CVM IP address>:9440/console/#login URL
  2. Click the Admin menu in the upper right corner and select Download Cmdlets Installer
  3. This downloads a NutanixCmdlets.msi file which you can then install on your Windows workstation or server
  4. You can then start the Nutanix cmdlets prompt by double clicking the NutanixCmdlets shortcut icon placed on your desktop

To double-check which version of the cmdlets you installed, use the following command:

Get-NTNXCmdletsInfo

…which should give you an output similar to the following:

Key Value
--- -----
version 4.1.2
BuildVersion 1.1.2-release4.1.2-dev-02232015
RestAPIVersion v1

That’s it! Fairly painless so far.

Step 2 – Running your first commands interactively

Just like with most other PowerShell extensions, the process with Nutanix cmdlets is quite straightforward:

  1. You start by connecting to a Nutanix cluster
  2. You then use commands to display, create, delete or modify objects in that cluster

To connect to a Nutanix cluster, you need to use the Connect-NutanixCluster cmdlet as in the following example:

Connect-NutanixCluster -server $myvarNutanixCluster -username $myvarNutanixClusterUsername -password $myvarNutanixClusterPassword –AcceptInvalidSSLCerts

The server, username and password are just plain text. I just used variable names in the example because you would typically set those up beforehand, but you could just as easily type them in the same command.

[box type=”info”] Note: At the time of writing (July 2015), the cmdlets were not updated with NOS 4.1.3 or NOS 4.1.4, so you will get a warning stating that the cmdlets version does not match the cluster version. This is also preventing scripts from executing properly because that prompt cannot be worked around. This issue is actively being worked on and should be resolved in the 4.5 release. In the meantime, you can call Nutanix support and get an updated msi package to work around this issue.[/box]

Assuming you connected successfully, you should see something similar to this:

Server : 192.168.0.10
UserName : admin
Password : ********
AcceptInvalidSSLCerts : True
ParameterSetName : __AllParameterSets
MyInvocation : System.Management.Automation.InvocationInfo
PagingParameters :
InvokeCommand : System.Management.Automation.CommandInvocationIntrinsics
Host : System.Management.Automation.Internal.Host.InternalHost
SessionState : System.Management.Automation.SessionState
Events : System.Management.Automation.PSLocalEventManager
JobRepository : System.Management.Automation.JobRepository
JobManager : System.Management.Automation.JobManager
InvokeProvider : System.Management.Automation.ProviderIntrinsics
Stopping : False
CommandRuntime : Connect-NutanixCluster
CurrentPSTransaction :
CommandOrigin : Runspace
IsConnected : True

As long as IsConnected displays True, you’re good to go.

What can you do from here? Before we move into specific examples, the full documentation is available on the Nutanix support portal. If you have access to that portal, (it requires registration), then you’ll find the official documentation for the cmdlets in the API Reference document. More specifically, the “About Nutanix PowerShell Cmdlets” section explains how to get started and the “PowerShell Scrip Tutorial” helps you get going. The “PowerShell Cmdlets Reference” section gives you the full references needed for advanced scripting.

For a publicly available list of supported cmdlets, you can also check out the awesome Nutanix PowerShell cmdlet Reference Poster in PDF format.

Of course, when you are done, you should disconnect from the Nutanix cluster by using the following command:

Disconnect-NutanixCluster -servers $myvarNutanixCluster

Step 3 – Using the Nutanix PowerShell cmdlets in scripts

Typically in PowerShell, all you need to do is include an Import-Module or Add-PSSnapin statement, and this is no different with the Nutanix cmdlets.

All you need to include in your script to be able to use them are the following two lines of code:

$myvarLoaded = Get-PSSnapin -Name NutanixCmdletsPSSnapin -ErrorAction SilentlyContinue | % {$_.Name}
if ($myvarLoaded -eq $null){Add-PSSnapin NutanixCmdletsPSSnapin}

After that, you simply add your Connect-NutanixCluster statement and off you go automating your tedious tasks.

I have included as an attachment a couple of PowerShell script templates that I use which includes those two lines as well as:

  1. Some extra error control when loading the snap-in
  2. Code to add the vSphere PowerCLI snap-in as well (for the template_vsphere.ps1 template)
  3. An OutputLogData function to handle the display of messages in script execution. The messages are color coded (green for informational messages, orange for warnings and red for errors) and optionally can be sent to a log file as well.

Feel free to re-use and/or modify it for your own purposes. The rest of the examples used in this blog have been created using that template.

Example 1 – Initializing storage on a freshly installed Nutanix cluster

In this example, we will create a storage pool and container on a freshly installed Nutanix cluster. The script also gives you the option simply to create a container with or without deduplication and/or compression just to show how that is done.

What the script does exactly:

  1. It connects to the Nutanix cluster you specified
  2. It creates a storage pool with a default or specified name. If the storage pool already exists, it gives you a warning message and continues.
  3. It creates a container with the options you specified (compression/deduplication)
  4. Optionally, it mounts the container as an NFS datastore on all the hosts in the Nutanix cluster, or on all the hosts in a given vSphere cluster

This script was tested on NOS 4.1.2 and ESXi 5.5 update 2. Note that it does only minimal error control.

<#
.SYNOPSIS
This script can be used to initialize storage on a freshly installed Nutanix cluster.
.DESCRIPTION
This script creates a storage pool and/or a container on a Nutanix cluster.
.PARAMETER help
Displays a help message (seriously, what did you think this was?)
.PARAMETER history
Displays a release history for this script (provided the editors were smart enough to document this...)
.PARAMETER log
Specifies that you want the output messages to be written in a log file as well as on the screen.
.PARAMETER debugme
Turns off SilentlyContinue on unexpected error messages.
.PARAMETER cluster
Nutanix cluster fully qualified domain name or IP address.
.PARAMETER username
Username used to connect to the Nutanix cluster.
.PARAMETER password
Password used to connect to the Nutanix cluster.
.PARAMETER storagepool
Name of the storage pool you want to create. If no name is specified, this will default to "<cluster name>-sp1". If a storage pool already exists, the script will not create a new one.
.PARAMETER container
Name of the container you want to create. If no name is specified, this will default to "<cluster name>-ct1". If a container already exists, the script will create a new one assuming the specified name is unique.
.PARAMETER rf
Specifies the replication factor (2 or 3; default used if not specified is 2).
.PARAMETER compression
Specifies that you want to enable compression on the container (default is not enabled).
.PARAMETER compressiondelay
Specifies the compression delay in seconds. If no compression delay is specified, then inline compression will be enabled (default is 0).
.PARAMETER dedupe
Specifies that you want to enable post process deduplication on the container (default is not enabled).
.PARAMETER fingerprint
Specifies that you want to enable inline fingerprinting on the container (default is not enabled).
.PARAMETER connectnfs
Specifies that you want to mount the new container as an NFS datastore on all ESXi hosts in the Nutanix cluster (default behavior will not mount the nfs datastore)
.PARAMETER vcenter
Hostname of the vSphere vCenter to which the hosts you want to mount the NFS datastore belong to. This is optional. By Default, if no vCenter server and vSphere cluster name are specified, then the NFS datastore is mounted to all hypervisor hosts in the Nutanix cluster. The script assumes the user running it has access to the vcenter server.
.PARAMETER vcluster
Name of the vSphere cluster with the hosts where you want to mount the NFS datastore. This is useful to specify if you have multiple compute clusters in your Nutanix cluster and you only want to mount your NFS datastore to the hosts of a specific compute cluster.
.EXAMPLE
Create a default storage pool and container on the specified Nutanix cluster:
PS> .\add-NutanixStorage.ps1 -cluster ntnxc1.local -username admin -password admin
.EXAMPLE
Create a storage pool and container on the specified Nutanix cluster and enable inline compression on the container, then mount it on all ESXi hosts:
PS> .\add-NutanixStorage.ps1 -cluster ntnxc1.local -username admin -password admin -storagepool spool1 -container compressed1 -compression -connectnfs
.EXAMPLE
Create a storage pool and container on the specified Nutanix cluster and mount the NFS datastore to the hosts that make up the vSphere cluster named "ntnxc1" and are managed by the vCenter server called "vcenter1":
PS> .\add-NutanixStorage.ps1 -cluster ntnxc1.local -username admin -password admin -storagepool spool1 -container compressed1 -connectnfs -vcenter vcenter1.mydomain.local -vcluster ntnxc1
.LINK
http://www.nutanix.com/services
.NOTES
Author: Stephane Bourdeaud (sbourdeaud@nutanix.com)
Revision: July 22nd 2015
#>

######################################
## parameters and initial setup ##
######################################

#let's start with some command line parsing
Param
(
#[parameter(valuefrompipeline = $true, mandatory = $true)] [PSObject]$myParam1,
[parameter(mandatory = $false)] [switch]$help,
[parameter(mandatory = $false)] [switch]$history,
[parameter(mandatory = $false)] [switch]$log,
[parameter(mandatory = $false)] [switch]$debugme,
[parameter(mandatory = $true)] [string]$cluster,
[parameter(mandatory = $true)] [string]$username,
[parameter(mandatory = $true)] [string]$password,
[parameter(mandatory = $false)] [string]$storagepool,
[parameter(mandatory = $false)] [string]$container,
[parameter(mandatory = $false)] [int]$rf,
[parameter(mandatory = $false)] [switch]$compression,
[parameter(mandatory = $false)] [int]$compressiondelay,
[parameter(mandatory = $false)] [switch]$dedupe,
[parameter(mandatory = $false)] [switch]$fingerprint,
[parameter(mandatory = $false)] [switch]$connectnfs,
[parameter(mandatory = $false)] [string]$vcenter,
[parameter(mandatory = $false)] [string]$vcluster
)

# get rid of annoying error messages
if (!$debugme) {$ErrorActionPreference = "SilentlyContinue"}

########################
## main functions ##
########################

#this function is used to output log data
Function OutputLogData
{
    #input: log category, log message
    #output: text to standard output
<#
.SYNOPSIS
Outputs messages to the screen and/or log file.
.DESCRIPTION
This function is used to produce screen and log output which is categorized, time stamped and color coded.
.NOTES
Author: Stephane Bourdeaud
.PARAMETER myCategory
This the category of message being outputed. If you want color coding, use either "INFO", "WARNING", "ERROR" or "SUM".
.PARAMETER myMessage
This is the actual message you want to display.
.EXAMPLE
PS> OutputLogData -mycategory "ERROR" -mymessage "You must specify a cluster name!"
#>

    param
    (
        [string] $category,
        [string] $message
    )

begin
{
     $myvarDate = get-date
     $myvarFgColor = "Gray"
     switch ($category)
     {
         "INFO" {$myvarFgColor = "Green"}
         "WARNING" {$myvarFgColor = "Yellow"}
         "ERROR" {$myvarFgColor = "Red"}
         "SUM" {$myvarFgColor = "Magenta"}
     }
}
process
{
     Write-Host -ForegroundColor $myvarFgColor "$myvarDate [$category] $message"
     if ($log) {Write-Output "$myvarDate [$category] $message" >>$myvarOutputLogFile}
}
end
{
Remove-variable category
Remove-variable message
Remove-variable myvarDate
Remove-variable myvarFgColor
}
}#end function OutputLogData

#########################
## main processing ##
#########################

#check if we need to display help and/or history
$HistoryText = @'
Maintenance Log
Date By Updates (newest updates at the top)
---------- ---- ---------------------------------------------------------------
06/19/2015 sb Initial release.
################################################################################
'@

$myvarScriptName = ".\add-NutanixStorage.ps1"
if ($help) {get-help $myvarScriptName; exit}
if ($History) {$HistoryText; exit}

#let's load the Nutanix cmdlets
if ((Get-PSSnapin -Name NutanixCmdletsPSSnapin -ErrorAction SilentlyContinue) -eq $null)#is it already there?
{
    Add-PSSnapin NutanixCmdletsPSSnapin #no? let's add it
    if (!$?) #have we been able to add it successfully?
    {
        OutputLogData -category "ERROR" -message "Unable to load the Nutanix snapin. Please make sure the Nutanix Cmdlets are installed on this server."
        return
    }
}

#let's make sure the VIToolkit is being used
if ($vcenter)
{
    if ((Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null)#is it already there?
    {
        Add-PSSnapin VMware.VimAutomation.Core #no? let's add it
        if (!$?) #have we been able to add it successfully?
        {
            OutputLogData -category "ERROR" -message "Unable to load the PowerCLI snapin. Please make sure PowerCLI is installed on this server."
            return
        }
    }
}

#initialize variables
    #misc variables
    $myvarElapsedTime = [System.Diagnostics.Stopwatch]::StartNew() #used to store script begin timestamp
    $myvarvCenterServers = @() #used to store the list of all the vCenter servers we must connect to
    $myvarOutputLogFile = (Get-Date -UFormat "%Y_%m_%d_%H_%M_")
    $myvarOutputLogFile += "OutputLog.log"
    $myvarNutanixHosts = @()

    ############################################################################
    # command line arguments initialization
    ############################################################################

    #let's initialize parameters if they haven't been specified
    if (!$rf) {$rf = 2} #configure default rf (replication factor) if it has not been specified
    if (($rf -gt 3) -or ($rf -lt 2))
    {
        OutputLogData -category "ERROR" -message "An invalid value ($rf) has been specified for the replication factor. It must be 2 or 3."
        break
    }

    if (!$fingerprint -and $dedupe)
    {
        OutputLogData -category "ERROR" -message "Deduplication can only be enabled when fingerprinting is also enabled."
        break
    }

    if ($compressiondelay -and !$compression)
    {
        OutputLogData -category "ERROR" -message "Compressiondelay can only be specified when compression is also used."
        break
    }

    if ($vcenter -and !$vcluster)
    {
        OutputLogData -category "ERROR" -message "You must specifiy a compute cluster name when you use the -vcenter parameter."
        break
    }


    ################################
    ## Main execution here ##
    ################################

    OutputLogData -category "INFO" -message "Connecting to Nutanix cluster $cluster..."
    if (!($myvarNutanixCluster = Connect-NutanixCluster -Server $cluster -UserName $username -Password $password –acceptinvalidsslcerts))#make sure we connect to the Nutanix cluster OK...
    {#error handling
        $myvarerror = $error[0].Exception.Message
        OutputLogData -category "ERROR" -message "$myvarerror"
        return
    }
    else #...otherwise show confirmation
    {
        OutputLogData -category "INFO" -message "Connected to Nutanix cluster $cluster."
    }#endelse

    if ($myvarNutanixCluster)
    {
        ######################
        #main processing here#
        ######################
        OutputLogData -category "INFO" -message "Creating the storage pool..."
        if (!$storagepool)
        {
            $storagepool = (Get-NTNXClusterInfo).Name + "-sp1" #figure out the cluster name and init default sp name
        }

        #add error control here to see if storage pool already exists
        if (!$storagepool -eq (Get-NTNXStoragePool | select -expand name))
        {
            #create the container
            New-NTNXStoragePool -Name $storagepool -Disks (Get-NTNXDisk | select -ExpandProperty id)
            #figure out the storage pool id
        }
        else
        {
            OutputLogData -category "WARN" -message "The storage pool $storagepool already exists..."
        }

        $myvarStoragePoolId = get-ntnxstoragepool | where{$_.name -eq $storagepool} | select -expand id #need to add select id property only here


        OutputLogData -category "INFO" -message "Creating the container..."
        if (!$container)
        {
            $container = (Get-NTNXClusterInfo).Name + "-ct1" #figure out the cluster name and init default ct name
        }

        #add error control here to see if the container already exists
        if (!(get-ntnxcontainer | where{$_.name -eq $container}))
        {
            if ($compression -and $compressiondelay -and $dedupe -and $fingerprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -CompressionDelayInSecs $compressiondelay -FingerPrintOnWrite ON -OnDiskDedup POST_PROCESS)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression -and $compressiondelay -and $dedupe)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -CompressionDelayInSecs $compressiondelay -OnDiskDedup POST_PROCESS )
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression -and $compressiondelay -and $fingerprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -CompressionDelayInSecs $compressiondelay -FingerPrintOnWrite ON)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression -and $compressiondelay)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -CompressionDelayInSecs $compressiondelay)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression -and $dedupe -and $fingerprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -FingerPrintOnWrite ON -OnDiskDedup POST_PROCESS)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression -and $fingerprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true -FingerPrintOnWrite ON)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($compression)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -CompressionEnabled:$true)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($dedupe -and $fingeprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -FingerPrintOnWrite ON -OnDiskDedup POST_PROCESS)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($dedupe)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -OnDiskDedup POST_PROCESS)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            elseif ($fingeprint)
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf -FingerPrintOnWrite ON)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
            else
            {
                if (New-NTNXContainer -Name $container -StoragePoolId $myvarStoragePoolId -ReplicationFactor $rf)
                {
                    OutputLogData -category "INFO" -message "Container $container was successfully created!"
                }
            }
        }
        else
        {
            OutputLogData -category "ERROR" -message "The container $container already exists..."
            break
        }

        if ($connectnfs)
        {
            if ($vcenter)
            {
                #connect to the vcenter server
                OutputLogData -category "INFO" -message "Connecting to vCenter server $vcenter..."
                if (!($myvarvCenterObject = Connect-VIServer $vcenter))#make sure we connect to the vcenter server OK...
                {#make sure we can connect to the vCenter server
                    $myvarerror = $error[0].Exception.Message
                    OutputLogData -category "ERROR" -message "$myvarerror"
                    return
                }
                else #...otherwise show the error message
                {
                    OutputLogData -category "INFO" -message "Connected to vCenter server $vcenter."
                }#endelse

                if ($myvarvCenterObject)
                {
                    #get the hosts in the specified cluster
                    $myvarESXiHosts = get-cluster $vcluster | get-vmhost | Get-VMHostNetworkAdapter -Name vmk0 | Select -ExpandProperty IP
                    foreach ($myvarHost in $myvarESXiHosts)
                    {
                        #make sure all hosts in the compute cluster are also in the Nutanix cluster and get their unique IDs
                        $myvarNutanixHosts += Get-NTNXHost | where {$_.hypervisorAddress -eq $myvarHost} | select -ExpandProperty serviceVMId
                        OutputLogData -category "INFO" -message "Found ESXi host with IP address $myvarHost in the Nutanix cluster $cluster..."
                    }
                }

                #mount the NFS datastore to the specified hosts
                if ($myvarNutanixHosts)
                {
                    Add-NTNXNfsDatastore -DatastoreName $container -ContainerName $container -NodeIds $myvarNutanixHosts | Out-Null
                    OutputLogData -category "INFO" -message "Mounted datastore $container to the ESXi hosts in $vcluster"
                }
                else
                {
                    OutputLogData -category "ERROR" -message "Could not find any host to mount the NFS datastore to in $vcluster..."
                }
                OutputLogData -category "INFO" -message "Disconnecting from vCenter server $vcenter..."
                Disconnect-viserver -Confirm:$False #cleanup after ourselves and disconnect from vcenter
            }
            else
            {
                OutputLogData -category "INFO" -message "Mounting the NFS datastore $container on all hypervisor hosts in the Nutanix cluster..."
                Add-NTNXNfsDatastore -DatastoreName $container -ContainerName $container | Out-Null
            }
        }
    }#endif

OutputLogData -category "INFO" -message "Disconnecting from Nutanix cluster $cluster..."
    Disconnect-NutanixCluster -Servers $cluster #cleanup after ourselves and disconnect from the Nutanix cluster

#########################
## cleanup ##
#########################

    #let's figure out how much time this all took
    OutputLogData -category "SUM" -message "total processing time: $($myvarElapsedTime.Elapsed.ToString())"

    #cleanup after ourselves and delete all custom variables
    Remove-Variable myvar*
    Remove-Variable ErrorActionPreference
    Remove-Variable help
    Remove-Variable history
    Remove-Variable log
    Remove-Variable cluster
    Remove-Variable username
    Remove-Variable password
    Remove-Variable storagepool
    Remove-Variable container
    Remove-Variable compression
    Remove-Variable compressiondelay
    Remove-Variable dedupe
    Remove-Variable fingerprint
    Remove-Variable connectnfs
    Remove-Variable rf
    Remove-Variable vcenter
    Remove-Variable vcluster
    Remove-Variable debugme

Example 2 – Setup a protection domain and consistency group for vSphere virtual machines in a given folder

Often, virtualization administrators group virtual machines that make up an application in one or more specific folder(s). Consistency groups in Nutanix enable taking synchronized crash-consistent snapshots for a group of virtual machines. Therefore, it makes sense to be able to create consistency groups based on how your virtual machines have been organized in folders in vCenter.

Specifically, the script will:

  1. Let you specify a vCenter server and a Nutanix cluster, along with one or more VM folder name(s). Scheduling options for the protection domain must also be specified as well as a retention policy for snapshots. You can also specify an interval in minutes if you are processing multiple folders and you would like to spread out the schedules of the protection domains snapshot operations.
  2. Create a protection domain on the Nutanix cluster named after each VM folder name.
  3. Create a consistency group in that protection domain also named after each VM folder and add all the VMs in that folder to that consistency group.
  4. Create a schedule for each protection domain using the local container as a target and at the specified date and time.
  5. Set the specified retention policy on each protection domain.
  6. Optionally, replicate once immediately so that your VMs are protected (use with caution when you have lots of consistency groups).

Note that the script does not cover VSS integration.

This script was tested on NOS 4.1.2 and ESXi 5.5 update 2. Note that it does only minimal error control.

<#
.SYNOPSIS
This script can be used to create protection domains and consistency groups based on a VM folder structure in vCenter.
.DESCRIPTION
This script creates protection domains with consistency groups including all VMs in a given vCenter server VM folder. Protection domains and consistency groups are automatically named "<clustername>-pd-<foldername>" and "<clustername>-cg-<foldername>".
.PARAMETER help
Displays a help message (seriously, what did you think this was?)
.PARAMETER history
Displays a release history for this script (provided the editors were smart enough to document this...)
.PARAMETER log
Specifies that you want the output messages to be written in a log file as well as on the screen.
.PARAMETER debugme
Turns off SilentlyContinue on unexpected error messages.
.PARAMETER cluster
Nutanix cluster fully qualified domain name or IP address.
.PARAMETER username
Username used to connect to the Nutanix cluster.
.PARAMETER password
Password used to connect to the Nutanix cluster.
.PARAMETER vcenter
Hostname of the vSphere vCenter to which the hosts you want to mount the NFS datastore belong to. This is optional. By Default, if no vCenter server and vSphere cluster name are specified, then the NFS datastore is mounted to all hypervisor hosts in the Nutanix cluster. The script assumes the user running it has access to the vcenter server.
.PARAMETER folder
Name of the VM folder object in vCenter which contains the virtual machines to be added to the protection domain and consistency group. You can specify multiple folder names by separating them with commas in which case you must enclose them in double quotes.
.PARAMETER repeatEvery
Valid values are HOURLY, DAILY and WEEKLY, followed by the number of repeats. For example, if you want backups to occur once a day, specify "DAILY,1" (note the double quotes).
.PARAMETER startOn
Specifies the date and time at which you want to start the backup in the format: "MM/dd/YYYY,HH:MM". Note that this should be in UTC znd enclosed in double quotes.
.PARAMETER retention
Specifies the number of snapshot versions you want to keep.
.PARAMETER replicateNow
This is an optional parameter. If you use -replicateNow, a snapshot will be taken immediately for each created consistency group.
.PARAMETER interval
This is an optional parameter. Specify the interval in minutes at which you want to separate each schedule. This is to prevent scheduling all protection domains snapshots at the same time. If you are processing multiple folders, the first protection domain will be scheduled at the exact specified time (say 20:00 UTC), the next protection domain will be scheduled at +interval minutes (so 20:05 UTC if your interval is 5), and so on...
.EXAMPLE
Create a protection domain for VM folders "appA" and "appB", schedule a replication every day at 8:00PM UTC, set a retention of 3 snapshots and replicate immediately:
PS> .\add-NutanixProtectionDomains.ps1 -cluster ntnxc1.local -username admin -password admin -vcenter vcenter1 -folder "appA,appB" -repeatEvery "DAILY,1" -startOn "07/29/2015,20:00" -retention 3 -replicateNow
.LINK
http://www.nutanix.com/services
.NOTES
Author: Stephane Bourdeaud (sbourdeaud@nutanix.com)
Revision: July 29th 2015
#>

######################################
## parameters and initial setup ##
######################################

#let's start with some command line parsing
Param
(
#[parameter(valuefrompipeline = $true, mandatory = $true)] [PSObject]$myParam1,
[parameter(mandatory = $false)] [switch]$help,
[parameter(mandatory = $false)] [switch]$history,
[parameter(mandatory = $false)] [switch]$log,
[parameter(mandatory = $false)] [switch]$debugme,
[parameter(mandatory = $true)] [string]$cluster,
[parameter(mandatory = $true)] [string]$username,
[parameter(mandatory = $true)] [string]$password,
[parameter(mandatory = $true)] [string]$vcenter,
[parameter(mandatory = $true)] [string]$folder,
[parameter(mandatory = $true)] [string]$repeatEvery,
[parameter(mandatory = $true)] [string]$startOn,
[parameter(mandatory = $true)] [string]$retention,
[parameter(mandatory = $false)] [switch]$replicateNow,
[parameter(mandatory = $false)] [int]$interval
)

# get rid of annoying error messages
if (!$debugme) {$ErrorActionPreference = "SilentlyContinue"}

########################
## main functions ##
########################

#this function is used to output log data
Function OutputLogData
{
    #input: log category, log message
    #output: text to standard output
<#
.SYNOPSIS
Outputs messages to the screen and/or log file.
.DESCRIPTION
This function is used to produce screen and log output which is categorized, time stamped and color coded.
.NOTES
Author: Stephane Bourdeaud
.PARAMETER myCategory
This the category of message being outputed. If you want color coding, use either "INFO", "WARNING", "ERROR" or "SUM".
.PARAMETER myMessage
This is the actual message you want to display.
.EXAMPLE
PS> OutputLogData -mycategory "ERROR" -mymessage "You must specify a cluster name!"
#>

    param
    (
        [string] $category,
        [string] $message
    )
begin
{
     $myvarDate = get-date
     $myvarFgColor = "Gray"
     switch ($category)
     {
         "INFO" {$myvarFgColor = "Green"}
         "WARNING" {$myvarFgColor = "Yellow"}
         "ERROR" {$myvarFgColor = "Red"}
         "SUM" {$myvarFgColor = "Magenta"}
     }
}
process
{
     Write-Host -ForegroundColor $myvarFgColor "$myvarDate [$category] $message"
     if ($log) {Write-Output "$myvarDate [$category] $message" >>$myvarOutputLogFile}
}
end
{
Remove-variable category
Remove-variable message
Remove-variable myvarDate
Remove-variable myvarFgColor
}
}#end function OutputLogData

#########################
## main processing ##
#########################

#check if we need to display help and/or history
$HistoryText = @'
Maintenance Log
Date By Updates (newest updates at the top)
---------- ---- ---------------------------------------------------------------
06/19/2015 sb Initial release.
################################################################################
'@

$myvarScriptName = ".\add-NutanixProtectionDomains.ps1"
if ($help) {get-help $myvarScriptName; exit}
if ($History) {$HistoryText; exit}

#let's load the Nutanix cmdlets
if ((Get-PSSnapin -Name NutanixCmdletsPSSnapin -ErrorAction SilentlyContinue) -eq $null)#is it already there?
{
    Add-PSSnapin NutanixCmdletsPSSnapin #no? let's add it
    if (!$?) #have we been able to add it successfully?
    {
        OutputLogData -category "ERROR" -message "Unable to load the Nutanix snapin. Please make sure the Nutanix Cmdlets are installed on this server."
        return
    }
}

#let's make sure the VIToolkit is being used
if ((Get-PSSnapin VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null)#is it already there?
{
    Add-PSSnapin VMware.VimAutomation.Core #no? let's add it
    if (!$?) #have we been able to add it successfully?
    {
        OutputLogData -category "ERROR" -message "Unable to load the PowerCLI snapin. Please make sure PowerCLI is installed on this server."
        return
    }
}

#initialize variables

    #misc variables
    $myvarElapsedTime = [System.Diagnostics.Stopwatch]::StartNew() #used to store script begin timestamp
    $myvarvCenterServers = @() #used to store the list of all the vCenter servers we must connect to
    $myvarOutputLogFile = (Get-Date -UFormat "%Y_%m_%d_%H_%M_")
    $myvarOutputLogFile += "OutputLog.log"

    ############################################################################
    # command line arguments initialization
    ############################################################################

    #let's initialize parameters if they haven't been specified
    $myvarFolders = $folder.Split("{,}")
    if ($interval -and (($interval -le 0) -or ($interval -ge 60)))
    {
        OutputLogData -category "ERROR" -message "Interval must be between 1 and 59 minutes!"
        break
    }

    ################################
    ## Main execution here ##
    ################################

    OutputLogData -category "INFO" -message "Connecting to Nutanix cluster $cluster..."
    if (!($myvarNutanixCluster = Connect-NutanixCluster -Server $cluster -UserName $username -Password $password –acceptinvalidsslcerts))#make sure we connect to the Nutanix cluster OK...
    {#error handling
        $myvarerror = $error[0].Exception.Message
        OutputLogData -category "ERROR" -message "$myvarerror"
        return
    }
    else #...otherwise show confirmation
    {
        OutputLogData -category "INFO" -message "Connected to Nutanix cluster $cluster."
    }#endelse

    if ($myvarNutanixCluster)
    {
        #connect to the vcenter server
        OutputLogData -category "INFO" -message "Connecting to vCenter server $vcenter..."
        if (!($myvarvCenterObject = Connect-VIServer $vcenter))#make sure we connect to the vcenter server OK...
        {#make sure we can connect to the vCenter server
            $myvarerror = $error[0].Exception.Message
            OutputLogData -category "ERROR" -message "$myvarerror"
            return
        }
        else #...otherwise show the error message
        {
            OutputLogData -category "INFO" -message "Connected to vCenter server $vcenter."
        }#endelse

        if ($myvarvCenterObject)
        {
            ######################
            #main processing here#
            ######################
            $myvarLoopCount = 0
            foreach ($myvarFolder in $myvarFolders)
            {
                #let's make sure the protection domain doesn't already exist
                $myvarPdName = (Get-NTNXClusterInfo).Name + "-pd-" + $myvarFolder
                if (Get-NTNXProtectionDomain -Name $myvarPdName)
                {
                    OutputLogData -category "WARN" -message "The protection domain $myvarPdName already exists! Skipping to the next item..."
                    continue
                }

                #retrieve list of VMs in the specified folder
                OutputLogData -category "INFO" -message "Retrieving the names of the VMs in $myvarFolder..."
                $myvarVMs = Get-Folder -Name $myvarFolder | get-vm | select -ExpandProperty Name
                if (!$myvarVMs)
                {#no VM in that folder...
                    OutputLogData -category "WARN" -message "No VM object was found in $myvarFolder or that folder was not found! Skipping to the next item..."
                    continue
                }

                #create the protection domain
                OutputLogData -category "INFO" -message "Creating the protection domain $myvarPdName..."
                Add-NTNXProtectionDomain -Input $myvarPdName | Out-Null

                #create the consistency group
                $myvarCgName = (Get-NTNXClusterInfo).Name + "-cg-" + $myvarFolder
                OutputLogData -category "INFO" -message "Creating the consistency group $myvarCgName..."
                Add-NTNXProtectionDomainVM -Name $myvarPdName -ConsistencyGroupName $myvarCgName -Names $myvarVMs | Out-Null

                ####################
                #create the schedule
                ####################
                #let's parse the repeatEvery argument (exp format: DAILY,1)
                $myvarType = ($repeatEvery.Split("{,}"))[0]
                $myvarEveryNth = ($repeatEvery.Split("{,}"))[1]
                #let's parse the startOn argument (exp format: MM/dd/YYYY,HH:MM in UTC)
                $myvarDate = ($startOn.Split("{,}"))[0]
                $myvarTime = ($startOn.Split("{,}"))[1]
                $myvarMonth = ($myvarDate.Split("{/}"))[0]
                $myvarDay = ($myvarDate.Split("{/}"))[1]
                $myvarYear = ($myvarDate.Split("{/}"))[2]
                $myvarHour = ($myvarTime.Split("{:}"))[0]
                $myvarMinute = ($myvarTime.Split("{:}"))[1]
                #let's figure out the target date for that schedule
                if ($interval -and ($myvarLoopCount -ge 1))
                {#an interval was specified and this is not the first time we create a schedule
                    $myvarTargetDate = (Get-Date -Year $myvarYear -Month $myvarMonth -Day $myvarDay -Hour $myvarHour -Minute $myvarMinute -Second 00 -Millisecond 00).AddMinutes($interval * $myvarLoopCount)
                }
                else
                {#no interval was specified, or this is our first time in this loop withna valid object
                    $myvarTargetDate = Get-Date -Year $myvarYear -Month $myvarMonth -Day $myvarDay -Hour $myvarHour -Minute $myvarMinute -Second 00 -Millisecond 00
                }
                $myvarUserStartTimeInUsecs = [long][Math]::Floor((($myvarTargetDate - (New-Object DateTime 1970, 1, 1, 0, 0, 0, ([DateTimeKind]::Utc))).Ticks / [timespan]::TicksPerSecond)) * 1000 * 1000

                #let's create the schedule
                OutputLogData -category "INFO" -message "Creating the schedule for $myvarPdName to start on $myvarTargetDate UTC..."
                Add-NTNXProtectionDomainCronSchedule -Name $myvarPdName -Type $myvarType -EveryNth $myvarEveryNth -UserStartTimeInUsecs $myvarUserStartTimeInUsecs | Out-Null

                #configure the retention policy
                OutputLogData -category "INFO" -message "Configuring the retention policy on $myvarPdName to $retention..."
                Set-NTNXProtectionDomainRetentionPolicy -pdname ((Get-NTNXProtectionDomain -Name $myvarPdName).Name) -Id ((Get-NTNXProtectionDomainCronSchedule -Name $myvarPdName).Id) -LocalMaxSnapshots $retention | Out-Null

                if ($replicateNow)
                {
                    #replicate now
                    OutputLogData -category "INFO" -message "Starting an immediate replication for $myvarPdName..."
                    Add-NTNXOutOfBandSchedule -Name $myvarPdName | Out-Null
                }

                ++$myvarLoopCount
            }
        }
    }#endif

OutputLogData -category "INFO" -message "Disconnecting from Nutanix cluster $cluster..."
    Disconnect-NutanixCluster -Servers $cluster #cleanup after ourselves and disconnect from the Nutanix cluster
    OutputLogData -category "INFO" -message "Disconnecting from vCenter server $vcenter..."
    Disconnect-viserver -Confirm:$False #cleanup after ourselves and disconnect from vcenter

#########################
## cleanup ##
#########################

    #let's figure out how much time this all took
    OutputLogData -category "SUM" -message "total processing time: $($myvarElapsedTime.Elapsed.ToString())"

    #cleanup after ourselves and delete all custom variables
    Remove-Variable myvar*
    Remove-Variable ErrorActionPreference
    Remove-Variable help
    Remove-Variable history
    Remove-Variable log
    Remove-Variable cluster
    Remove-Variable username
    Remove-Variable password
    Remove-Variable folder
    Remove-Variable repeatEvery
    Remove-Variable startOn
    Remove-Variable retention
    Remove-Variable replicateNow
    Remove-Variable vcenter
    Remove-variable interval
    Remove-Variable debugme

EDIT (August 2016): I was asked to produce a script to enable existing protection domain editing by specifying VM names or part of VMs name.  I have included the source code below.

<#
.SYNOPSIS
 This script can be used to add unprotected vm(s) to an existing protection domain on Nutanix.
.DESCRIPTION
 This script adds existing virtual machines to an existing protection domain if they are not already protected.
.PARAMETER help
 Displays a help message (seriously, what did you think this was?)
.PARAMETER history
 Displays a release history for this script (provided the editors were smart enough to document this...)
.PARAMETER log
 Specifies that you want the output messages to be written in a log file as well as on the screen.
.PARAMETER debugme
 Turns off SilentlyContinue on unexpected error messages.
.PARAMETER cluster
 Nutanix cluster fully qualified domain name or IP address.
.PARAMETER username
 Username used to connect to the Nutanix cluster.
.PARAMETER password
 Password used to connect to the Nutanix cluster.
.PARAMETER pd
 Name of the protection domain you want to add vms to.
.PARAMETER vm
 Name of the virtual machine(s) you want to add. You can specify mulitple vm names by use a comma separated list enclosed in double quotes or using the * wildcard. Note that when you use the * wildcard, it will try to match that string (exp: *mystring or mystring* or my*string will always behave as *mystring*)
.PARAMETER replicateNow
 This is an optional parameter. If you use -replicateNow, a snapshot will be taken immediately for each created consistency group.
.EXAMPLE
 Add all VMs that start with myvm* to protection domain "mypd" and replicate immediately:
 PS> .\add-NutanixProtectionDomains.ps1 -cluster ntnxc1.local -username admin -password admin -pd mypd -vm myvm* -replicateNow
.LINK
 http://www.nutanix.com/services
.NOTES
 Author: Stephane Bourdeaud (sbourdeaud@nutanix.com)
 Revision: August 18th 2016
#>

######################################
## parameters and initial setup ##
######################################
#let's start with some command line parsing
Param
(
 #[parameter(valuefrompipeline = $true, mandatory = $true)] [PSObject]$myParam1,
 [parameter(mandatory = $false)] [switch]$help,
 [parameter(mandatory = $false)] [switch]$history,
 [parameter(mandatory = $false)] [switch]$log,
 [parameter(mandatory = $false)] [switch]$debugme,
 [parameter(mandatory = $true)] [string]$cluster,
 [parameter(mandatory = $true)] [string]$username,
 [parameter(mandatory = $true)] [string]$password,
 [parameter(mandatory = $true)] [string]$pd,
 [parameter(mandatory = $true)] [string]$vm,
 [parameter(mandatory = $false)] [switch]$replicateNow,
 [parameter(mandatory = $false)] [int]$interval
)

# get rid of annoying error messages
if (!$debugme) {$ErrorActionPreference = "SilentlyContinue"}

########################
## main functions ##
########################

#this function is used to output log data
Function OutputLogData 
{
 #input: log category, log message
 #output: text to standard output
<#
.SYNOPSIS
 Outputs messages to the screen and/or log file.
.DESCRIPTION
 This function is used to produce screen and log output which is categorized, time stamped and color coded.
.NOTES
 Author: Stephane Bourdeaud
.PARAMETER myCategory
 This the category of message being outputed. If you want color coding, use either "INFO", "WARNING", "ERROR" or "SUM".
.PARAMETER myMessage
 This is the actual message you want to display.
.EXAMPLE
 PS> OutputLogData -mycategory "ERROR" -mymessage "You must specify a cluster name!"
#>
 param
 (
 [string] $category,
 [string] $message
 )

 begin
 {
 $myvarDate = get-date
 $myvarFgColor = "Gray"
 switch ($category)
 {
 "INFO" {$myvarFgColor = "Green"}
 "WARNING" {$myvarFgColor = "Yellow"}
 "ERROR" {$myvarFgColor = "Red"}
 "SUM" {$myvarFgColor = "Magenta"}
 }
 }

 process
 {
 Write-Host -ForegroundColor $myvarFgColor "$myvarDate [$category] $message"
 if ($log) {Write-Output "$myvarDate [$category] $message" >>$myvarOutputLogFile}
 }

 end
 {
 Remove-variable category
 Remove-variable message
 Remove-variable myvarDate
 Remove-variable myvarFgColor
 }
}#end function OutputLogData

#########################
## main processing ##
#########################

#check if we need to display help and/or history
$HistoryText = @'
 Maintenance Log
 Date By Updates (newest updates at the top)
 ---------- ---- ---------------------------------------------------------------
 08/18/2016 sb Initial release.
################################################################################
'@
$myvarScriptName = ".\add-VmToPd.ps1"
 
if ($help) {get-help $myvarScriptName; exit}
if ($History) {$HistoryText; exit}


#let's load the Nutanix cmdlets
if ((Get-PSSnapin -Name NutanixCmdletsPSSnapin -ErrorAction SilentlyContinue) -eq $null)#is it already there?
{
 Add-PSSnapin NutanixCmdletsPSSnapin #no? let's add it
 if (!$?) #have we been able to add it successfully?
 {
 OutputLogData -category "ERROR" -message "Unable to load the Nutanix snapin. Please make sure the Nutanix Cmdlets are installed on this server."
 return
 }
}

#initialize variables
 #misc variables
 $myvarElapsedTime = [System.Diagnostics.Stopwatch]::StartNew() #used to store script begin timestamp
 $myvarvCenterServers = @() #used to store the list of all the vCenter servers we must connect to
 $myvarOutputLogFile = (Get-Date -UFormat "%Y_%m_%d_%H_%M_")
 $myvarOutputLogFile += "OutputLog.log"
 #initialize the array variable we are going to use to store vm objects to add to the protection domain
 #[System.Collections.ArrayList]$myvarVMsToAddToPd = New-Object System.Collections.ArrayList($null)
 $myvarVMsToAddToPd = @()
 
 ############################################################################
 # command line arguments initialization
 ############################################################################ 
 #let's initialize parameters if they haven't been specified
 $myvarVMs = $vm.Split("{,}")
 
 
 ################################
 ## Main execution here ##
 ################################
 OutputLogData -category "INFO" -message "Connecting to Nutanix cluster $cluster..."
 if (!($myvarNutanixCluster = Connect-NutanixCluster -Server $cluster -UserName $username -Password $password –acceptinvalidsslcerts -ForcedConnection))#make sure we connect to the Nutanix cluster OK...
 {#error handling
 $myvarerror = $error[0].Exception.Message
 OutputLogData -category "ERROR" -message "$myvarerror"
 return
 }
 else #...otherwise show confirmation
 {
 OutputLogData -category "INFO" -message "Connected to Nutanix cluster $cluster."
 }#endelse
 
 if ($myvarNutanixCluster)
 { 
 ######################
 #main processing here#
 ######################
 
 #start by making sure the protection domain exists
 OutputLogData -category "INFO" -message "Getting protection domain $pd..."
 if (Get-NTNXProtectionDomain -Name $pd)
 {
 $myvarPdObject = Get-NTNXProtectionDomain -Name $pd
 #take the array of vm names specified by the user and process each entry
 foreach ($myvarVM in $myvarVMs)
 {
 if ($myvarVM -match '\*')
 {
 #strip wildcard
 $myvarVM = $myvarVM -replace '\*'
 #retrieve vms using search
 if ($myvarSearchedVMs = Get-NTNXVM -SearchString $myvarVM)
 {
 #process each retrieved entry
 foreach ($myvarSearchedVM in $myvarSearchedVMs)
 {
 #check protection status
 if ($myvarSearchedVM.protectionDomainName)
 {
 #warn that vm is already protected and move on
 OutputLogData -category "WARN" -message "VM $($myvarSearchedVM.vmName) is already in protection domain $($myvarSearchedVM.protectionDomainName)..."
 continue
 }
 else #the vm is not in a protection domain
 {
 #add vm to the list
 OutputLogData -category "INFO" -message "Adding VM $($myvarSearchedVM.vmName) to the list of VMs to add to protection domain $pd..."
 $myvarVMsToAddToPd += $myvarSearchedVM.vmName
 }
 }#end foreach searched vm
 }
 else #we did not find any matching VM
 {
 OutputLogData -category "WARN" -message "Could not find any VM matching string $myvarVM..."
 }
 }
 else #the vm name did not contain a wildcard
 {
 #retrieve the vm object
 if ($myvarExactVM = Get-NTNXVM | Where-Object {$_.vmName -eq $myvarVM})
 {
 #check that vm protection status
 if ($myvarExactVM.protectionDomainName)
 {
 #warn that vm is already protected and move on
 OutputLogData -category "WARN" -message "VM $($myvarExactVM.vmName) is already in protection domain $($myvarExactVM.protectionDomainName)..."
 continue
 }
 else #the vm is not in a protection domain
 {
 #add vm to the list
 OutputLogData -category "INFO" -message "Adding VM $($myvarExactVM.vmName) to the list of VMs to add to protection domain $pd..."
 $myvarVMsToAddToPd += $myvarExactVM.vmName
 }
 }
 else
 {
 OutputLogData -category "WARN" -message "Could not find VM $($myvarExactVM.vmName)..."
 }
 }#endif contains wildcard
 }#end foreach vm
 #add vms to the pd
 if ($myvarVMsToAddToPd)
 {
 OutputLogData -category "INFO" -message "Adding VMs to protection domain $pd..."
 Add-NTNXProtectionDomainVM -Name $pd -Names $myvarVMsToAddToPd | Out-Null
 }#endif vms to add to pd?
 }
 else
 {
 $myvarerror = $error[0].Exception.Message
 OutputLogData -category "ERROR" -message "$myvarerror"
 break
 }
 
 if ($replicateNow)
 {
 #replicate now
 OutputLogData -category "INFO" -message "Starting an immediate replication for the protection domain $pd..."
 Add-NTNXOutOfBandSchedule -Name $pd | Out-Null
 }

 } 
 
 OutputLogData -category "INFO" -message "Disconnecting from Nutanix cluster $cluster..."
 Disconnect-NutanixCluster -Servers $cluster #cleanup after ourselves and disconnect from the Nutanix cluster
 
#########################
## cleanup ##
#########################

 #let's figure out how much time this all took
 OutputLogData -category "SUM" -message "total processing time: $($myvarElapsedTime.Elapsed.ToString())"
 
 #cleanup after ourselves and delete all custom variables
 Remove-Variable myvar*
 Remove-Variable ErrorActionPreference
 Remove-Variable help
 Remove-Variable history
 Remove-Variable log
 Remove-Variable cluster
 Remove-Variable username
 Remove-Variable password
 Remove-Variable pd
 Remove-Variable vm
 Remove-Variable replicateNow
 Remove-Variable debugme

 

For more coding examples (including in other languages such as Python), make sure to check out the Nutanix GitHub repository.

Conclusion

Using PowerShell is easy, and so is using the Nutanix PowerShell cmdlets. While the Prism UI is very intuitive and easy to use, sometimes you just don’t want to click 100 times for something a script can get done in a few seconds. Now you have all the tools required to automate your environment, so why don’t you get started?

Let me know what you come up with and if you hit any problem.

Don’t forget to leverage the almighty Nutanix Community as well and once you’ve unleashed your scripting superpower, enter our Total Recode coding challenge for a chance to win a 4000$ drone, a 2500$ home lab or 2000$ in cash! Now who said coding skills couldn’t make you rich?

0 0 votes
Article Rating

Stephane Bourdeaud

I am a Senior Consultant for Nutanix working out of France. I have over 18 years of experience in the IT industry including 15 as an infrastructure architect at IBM. I am a virtualization specialist holding VCP3, VCP4, VCP5, VCAP5-DCA and VCAP5-DCD certifications.

You may also like...

Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

[…] PowerCLI and the Nutanix cmdlets to be installed. For instructions on how to set that up, refer to this blog […]

1
0
Would love your thoughts, please comment.x
()
x

FOR FREE. Download Nutanix port diagrams

Join our mailing list to receive an email with instructions on how to download 19 port diagrams in MS Visio format.

NOTE: if you do not get an email within 1h, check your SPAM filters

You have Successfully Subscribed!

Pin It on Pinterest