Solved

Best Practice for Template Management


Userlevel 2
Badge +3

We have several AHV clusters across the WANscape all managed from Prism Central.

On one AHV cluster, I created a Windows Server 2016 and a Windows Server 2019 VM from CD ISOs, patched and tweaked and these are our “Golden Images”.  I clone those and run sysprep and those are what I make new VMs from.

Now I have two problems: 1.) in each cluster we have an RF2 container and an RF3 container so these VMs are bound to one or the other, and 2.) how do I get these to other clusters?

To address the differing containers, I end up using the image service (create image of the disks and create new VM from those disks).  But now I’m wasting storage because I’m keeping copies of the same VM on two different containers.

To address the differing clusters, I was thinking about setting up Protection Domains and letting Nutanix replicate them to each cluster.  They would only get updated once or twice per month for Windows patches.  Doing it this way would allow me to control replication schedule and bandwidth but would only replicate snapshots so recreating the VMs on the other end would require manually restoring from snapshot.

If I am not mistaken, there is an Image Service in Prism Central that would be globally available to all the clusters?  If I put the disk images in that, is there a way of controlling the bandwidth used?

Does anyone have any best practices to accomplish this?

Thanks!

icon

Best answer by TimGaray 15 June 2020, 14:37

View original

This topic has been closed for comments

11 replies

Userlevel 7
Badge +34

Thanks for sharing @TimGaray I’ll see if we can get you a reply!

Userlevel 6
Badge +5

Hi TimGaray,

 

Help me understand the set up a little better, please. There are 2 VMs on 2 different containers. The VMs serve as Gold images not only for the cluster they reside on but also for other clusters within the environment.

 

The goal is to be able to deploy VMs off these Gold images to both containers and to other clusters. Is that a correct summary of the task?

 

There is no best practices guide at the moment for images or templates at the moment.

 

I would love to give you a solution as this is a valid request, but I can’t and I apologise. There are improvements on the process and the feature in the pipeline.

 

At this point in time, you could use Prism Central image management feature. Note that with this feature images would be placed on SelfService container at both Prism Central as well as at the cluster that receives the image. From there you would have to copy images to all of the containers of interest.

Image Management section of Prism Central Guide talks about how to upload images from a workstation, a central server or import it from one of the clusters.

KB-2663 AHV | How to move VMs between containers within an AHV cluster explains how to move images between containers. The option is only available in CLI.

 

You could use Protection Domains, of course. As you can imagine, it would still require some manual effort to create images from the VMs and place them into all containers that you wish to deploy.

 

I sincerely hope that the above helps. Let me know, please if you have further questions.

Userlevel 2
Badge +3

Thank you @Alona, that is a correct description of the environment.

 

Due to the multiple containers in each cluster (one RF2 and one RF3), I am leaning towards using the Prism Central image management.  Thank you for the link to the guide.

 

If I get something up and running, I will be sure and post back my results.

Userlevel 2
Badge +3

After reviewing the Image Management material, although images seem to make the most sense there does not seem to be a way to throttle the bandwidth.  Some of our sites have small WAN connections and if I upload an 80GB disk image then Prism Central could flood that connection when transferring the image to that remote cluster.

Userlevel 6
Badge +5

Hi TimGaray,

I understand your concern in relation to bandwidth. There is no obvious option to throttle bandwidth, unfortunately. I would suggest to open a case with Nutanix Support for a possible custom tweaking.

Look forward to reading about the results of your efforts.

Userlevel 2
Badge +3

It would appear that images are also stored on specific storage containers and it won’t let me create a VM in one container with a disk image in another container.

 

So, whether I create a VM from disk images or clone from VMs, I have to keep a set on each storage container in a cluster (usually two: one RF2 and one RF3).

 

I’ll put in a support ticket and see what happens.

 

Userlevel 6
Badge +5

Yes, that’s right. That’s why I mentioned that you would need to copy images between containers and referred you to the KB for the same.

Userlevel 2
Badge +3

So this is what I ended up doing.  I will do it this way until such time as maybe there are changes such as the ability to deploy across different storage containers.  So far only one AHV cluster has more than one storage container so it isn’t too bad yet.

 

On the source AHV cluster, I have a Windows 2012R2, a Windows 2016 and a Windows 2019 “golden image” VM setup.  I then clone those and run sysprep and shut them down.

 

I have a protection domain setup to replicate those sysprep VMs to the other AHV clusters every night.  I also have the original (not sysprepped) VMs setup in a local protection domain just for backup purposes.

 

When I update the golden images with Windows Patches, I wait until the new images are replicated to the remote AHV clusters.  I wrote a PowerShell script that will go through each remote AHV cluster and delete existing disk images, restore the sysprepped VMs from snapshots, upload new disk images from the restored VMs then delete the restored VMs.  It doesn’t take that long to run (maybe 20 minutes or so for all of them).

 

I also have a PowerShell script to do the same thing on the source AHV cluster to create images for the RF2 storage container and the RF3 storage container.

 

It sounds like a lot but with the PowerShell scripts it’s not that bad.  So far bandwidth hasn’t been an issue but as we bring more AHV clusters onboard at remote sites then there may be cases where we need to throttle the bandwidth of the protection domain to those remote locations.

 

 

Userlevel 2
Badge +3

Here is the code I use for creating the disk images on the remote AHV clusters from the Protection Domain snapshots:

 

# This PS script will restore Golden Image VMs from most current snapshot and create disk images
#
#

$MyVersion = "2020.06.12.01"

$CurUser = [Environment]::UserName
$CurDomain = [Environment]::UserDomainName
$DomainUser = $CurDomain + "\" + $CurUser
$CurMachine = $env:computername
$currentDir = (Get-Location).Path
$Culture = Get-Culture
$GLOBAL:SessionLog = ""
$LogFile = $currentDir + "\GoldenImages_" + $CurUser + ".log"
$CurUserAD = Get-ADUser $CurUser -Properties * -Server $CurDomain
$ADDomain = Get-ADDomain $CurDomain
$CurrentDomain = "LDAP://" + $ADDomain.DistinguishedName




################################################################################################################
# VM Input Section
################################################################################################################
$MyCreds = Get-Credential -Message "Enter DC Credentials" -UserName ($CurUserAD.CN + "@" + $ADDomain.DNSRoot)

$PathPrefix = "//Clone-"
$VMPrefix = "GoldenClone-"

$VMList = @("Golden-2016-Server-RF2-SYSPREP","Golden-2019-Server-RF2-SYSPREP","Golden-2012R2-Server-RF2-SYSPREP")

$PDGolden = "Golden_Sysprep_Image_Replication"

$Prod4PE = "RemoteAHVCluster.mydomain.com" #Production-4
$Prod4Container = "PROD4_RF2"

################################################################################################################
################################################################################################################




Start-Transcript -Path "$currentDir\TRANSCRIPT-GoldenImages-$CurMachine-$CurUser.log" -Append




# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Functions


# This function writes to a log file and to the screen and keeps all the data in a GLOBAL variable for later emailing
Function WriteLog
{
Param ($Info, $NewLine, $Color)
$LogDT = Get-Date -Format "yyyyMMddHHmmss"

IF ($NewLine -eq "1")
{
Write-Host $Info -ForeGroundColor $Color
Add-Content $LogFile ($LogDT + ": " + $Info)
$GLOBAL:SessionLog += $Info + "`r`n"
}
Else
{
Write-Host $Info -NoNewLine -ForeGroundColor $Color
If ($Info -eq ".") { [System.IO.File]::AppendAllText($LogFile,$Info,[System.Text.Encoding]::ASCII) }
Else { [System.IO.File]::AppendAllText($LogFile,($LogDT + ": " + $Info),[System.Text.Encoding]::ASCII) }
$GLOBAL:SessionLog += $Info
}
}

# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------




$StartTime = Get-Date
WriteLog "" 1 "White"
WriteLog ("Starting Script: " + $StartTime) 1 "White"
WriteLog "" 1 "White"

WriteLog ("Program Version " + $MyVersion + " running.") 1 "White"
WriteLog "" 1 "White"

WriteLog ("Current User: " + $CurUserAD.CN) 1 "White"
WriteLog (" Email: " + $CurUserAD.Mail) 1 "White"
WriteLog (" User Domain: " + $ADDomain.NetBIOSName + " (" + $ADDomain.DNSRoot + ")") 1 "White"
WriteLog "" 1 "White"


# Import cmdlets for Nutanix and VMware
Add-PSSnapin NutanixCmdletsPSSnapin



# Production-4
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Connecting to Production-4 (" + $Prod4PE + ")...") 1 "White"
Connect-NTNXCluster -Server $Prod4PE -Username $MyCreds.Username -Password $MyCreds.Password -AcceptInvalidSSLCerts -ForcedConnection | Out-Null
$NTNXConnection = ""
$NTNXConnection = Get-NutanixCluster
If ($NTNXConnection.IsConnected -eq $True)
{
WriteLog "Getting 2 Most Recent Snapshots for Protection Domain..." 1 "White"
$MostRecentSnapshots = Get-NTNXProtectionDomainSnapshot -PdName $PDGolden | Sort snapshotCreateTimeUsecs -DESC | Select -First 2
ForEach ($Snap in $MostRecentSnapshots) { WriteLog (" " + $Snap.snapshotID + " Created: " + ([timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds(($Snap.snapshotCreateTimeUsecs)/1000000)))) 1 "White" }

WriteLog "" 1 "White"
WriteLog ("Restoring VMs...") 1 "White"

ForEach ($VM in $VMList)
{
Try { $Results = Restore-NTNXEntity -PdName $PDGolden -VmNames $VM -Replace $False -VmNamePrefix $VMPrefix -SnapshotId $MostRecentSnapshots[0].snapshotID -ErrorAction Stop }
Catch { $Results = "" }
If ($Results -match "success")
{ WriteLog (" " + $VM + " restored from " + ([timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds(($MostRecentSnapshots[0].snapshotCreateTimeUsecs)/1000000)))) 1 "Green" }
Else
{
Try { $Results = Restore-NTNXEntity -PdName $PDGolden -VmNames $VM -Replace $False -VmNamePrefix $VMPrefix -SnapshotId $MostRecentSnapshots[1].snapshotID -ErrorAction Stop }
Catch { $Results = "" }
If ($Results -match "success")
{ WriteLog (" " + $RVM + " restored from " + ([timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds(($MostRecentSnapshots[1].snapshotCreateTimeUsecs)/1000000)))) 1 "Yellow" }
Else
{ WriteLog (" " + $RVM + " not restored!") 1 "Red" }
}
}


# Delete pre-existing images
WriteLog "" 1 "White"
WriteLog "Deleting pre-exising images..." 1 "White"
$ImageList = Get-NTNXImage | Where {($_.Name -match "2012-") -Or ($_.Name -match "2016-") -Or ($_.Name -match "2019-")}
ForEach ($Image in $ImageList) { Remove-NTNXImage -ImageId $Image.uuid }


# Windows Server 2016
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + ($VMPrefix + $VMList[0]) + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[0])}
If (!$SourceVM) { Start-Sleep -Seconds 20 ; $SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[0])} }
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# 2016 OSDisk
WriteLog " Uploading 2016-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name "2016-OSDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# 2016 LogsDisk
WriteLog " Uploading 2016-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name "2016-LogsDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# Windows Server 2019
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + ($VMPrefix + $VMList[1]) + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[1])}
If (!$SourceVM) { Start-Sleep -Seconds 20 ; $SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[1])} }
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# 2019 OSDisk
WriteLog " Uploading 2019-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name "2019-OSDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# 2019 LogsDisk
WriteLog " Uploading 2019-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name "2019-LogsDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# Windows Server 2012R2
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + ($VMPrefix + $VMList[2]) + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[2])}
If (!$SourceVM) { Start-Sleep -Seconds 20 ; $SourceVM = Get-NTNXVM | Where {$_.vmname -eq ($VMPrefix + $VMList[2])} }
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# 2012 OSDisk
WriteLog " Uploading 2012-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name "2012-OSDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# 2012 LogsDisk
WriteLog " Uploading 2012-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $Prod4Container
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name "2012-LogsDisk" -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# Delete Cloned VMs
WriteLog "" 1 "White"
WriteLog "Deleting cloned VMs..." 1 "White"
$ClonedVMList = Get-NTNXVM | Where { $_.vmname -match $VMPrefix }
ForEach ($ClonedVM in $ClonedVMList) { Remove-NTNXVirtualMachine -vmid $ClonedVM.vmid }


Disconnect-NTNXCluster *
}



WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog "Everything should be done!" 1 "White"

# Show how long it took to run this script
WriteLog "" 1 "White"
WriteLog ("Execution Time: " + (new-timespan -start $StartTime)) 1 "White"
$EndTime = Get-Date
WriteLog ("Script End: " + $EndTime) 1 "White"
WriteLog "" 1 "White"

Stop-Transcript

Sorry… I tried to format this as code but nothing happened in the editor so I just went with “quote”.

Userlevel 2
Badge +3

This is the code I use to create images from sysprepped VMs onto two different storage containers (in the same source AHV cluster):

 

# This PS script will create images of sysprep VM disks on RF2 and create new VM on RF3
#
#

$MyVersion = "2020.06.11.01"

$CurUser = [Environment]::UserName
$CurDomain = [Environment]::UserDomainName
$DomainUser = $CurDomain + "\" + $CurUser
$CurMachine = $env:computername
$currentDir = (Get-Location).Path
$Culture = Get-Culture
$GLOBAL:SessionLog = ""
$LogFile = $currentDir + "\SysprepRF2RF3_" + $CurUser + ".log"
$CurUserAD = Get-ADUser $CurUser -Properties * -Server $CurDomain
$RootUserAD = Get-ADUser $CurUserAD.extensionAttribute5 -Properties * -Server $CurDomain
$ADDomain = Get-ADDomain $CurDomain
$CurrentDomain = "LDAP://" + $ADDomain.DistinguishedName


Start-Transcript -Path "$currentDir\TRANSCRIPT-SysprepRF2RF3-$CurMachine-$CurUser.log" -Append



################################################################################################################
# VM Input Section
################################################################################################################
$MyCreds = Get-Credential -Message "Enter DC Credentials" -UserName ($CurUserAD.CN + "@" + $ADDomain.DNSRoot)


$Sysprep2012 = "Golden-2012R2-Server-RF2-SYSPREP"
$Sysprep2016 = "Golden-2016-Server-RF2-SYSPREP"
$Sysprep2019 = "Golden-2019-Server-RF2-SYSPREP"
$PrismElement = "SourceAHVCluster.mydomain.com"

$DestContainerRF2 = "PRODSQL_RF2"
$DestContainerRF3 = "PRODSQL_RF3"

$VMNet = "VMNetwork"

################################################################################################################
################################################################################################################






# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

# Functions


# This function writes to a log file and to the screen and keeps all the data in a GLOBAL variable for later emailing
Function WriteLog
{
Param ($Info, $NewLine, $Color)
$LogDT = Get-Date -Format "yyyyMMddHHmmss"

IF ($NewLine -eq "1")
{
Write-Host $Info -ForeGroundColor $Color
Add-Content $LogFile ($LogDT + ": " + $Info)
$GLOBAL:SessionLog += $Info + "`r`n"
}
Else
{
Write-Host $Info -NoNewLine -ForeGroundColor $Color
If ($Info -eq ".") { [System.IO.File]::AppendAllText($LogFile,$Info,[System.Text.Encoding]::ASCII) }
Else { [System.IO.File]::AppendAllText($LogFile,($LogDT + ": " + $Info),[System.Text.Encoding]::ASCII) }
$GLOBAL:SessionLog += $Info
}
}

# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------




$StartTime = Get-Date
WriteLog "" 1 "White"
WriteLog ("Starting Script: " + $StartTime) 1 "White"
WriteLog "" 1 "White"

WriteLog ("Program Version " + $MyVersion + " running.") 1 "White"
WriteLog "" 1 "White"

WriteLog ("Current User: " + $CurUserAD.CN) 1 "White"
WriteLog (" Email: " + $CurUserAD.Mail) 1 "White"
WriteLog (" User Domain: " + $ADDomain.NetBIOSName + " (" + $ADDomain.DNSRoot + ")") 1 "White"
WriteLog (" Root User: " + $CurUserAD.extensionAttribute5) 1 "White"
WriteLog (" Root Email: " + $RootUserAD.mail) 1 "White"
WriteLog "" 1 "White"



Add-PSSnapin NutanixCmdletsPSSnapin


Connect-NTNXCluster -Server $PrismElement -Username $MyCreds.Username -Password $MyCreds.Password -AcceptInvalidSSLCerts -ForcedConnection | Out-Null
Get-NutanixCluster


# Delete pre-existing images
WriteLog "" 1 "White"
WriteLog "Deleting pre-existing images..." 1 "White"
$ImageList = Get-NTNXImage | Where {($_.Name -match "2012-") -Or ($_.Name -match "2016-") -Or ($_.Name -match "2019-")}
ForEach ($Image in $ImageList) { Remove-NTNXImage -ImageId $Image.uuid }



# Windows Server 2012R2
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + $Sysprep2012 + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq $Sysprep2016}
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# RF2 2012R2 OSDisk
WriteLog " Uploading RF2 2012-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2012-OSDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF2 2012R2 LogsDisk
WriteLog " Uploading RF2 2012-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2012-LogsDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# RF3 2012R2 OSDisk
WriteLog " Uploading RF3 2012-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2012-OSDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF3 2012R2 LogsDisk
WriteLog " Uploading RF3 2012-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2012-LogsDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"




# Windows Server 2016
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + $Sysprep2016 + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq $Sysprep2016}
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# RF2 2016 OSDisk
WriteLog " Uploading RF2 2016-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2016-OSDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF2 2016 LogsDisk
WriteLog " Uploading RF2 2016-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2016-LogsDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# RF3 2016 OSDisk
WriteLog " Uploading RF3 2016-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2016-OSDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF3 2016 LogsDisk
WriteLog " Uploading RF3 2016-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2016-LogsDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"



# Windows Server 2019
WriteLog "" 1 "White"
WriteLog "" 1 "White"
WriteLog ("Starting " + $Sysprep2019 + "...") 1 "White"
$SourceVM = Get-NTNXVM | Where {$_.vmname -eq $Sysprep2019}
$VDiskPaths = $SourceVM.nutanixVirtualDisks
$VDisks = Get-NTNXVMDisk -vmid $SourceVM.vmid -includeDiskSizes | Where {$_.id -match "scsi"} | Sort id

# RF2 2019 OSDisk
WriteLog " Uploading RF2 2019-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2019-OSDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF2 2019 LogsDisk
WriteLog " Uploading RF2 2019-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF2
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2019-LogsDisk-RF2") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"


# RF3 2019 OSDisk
WriteLog " Uploading RF3 2019-OSDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[0].vmDiskUuid})
$OSDiskTask = New-NTNXImage -Name ("2019-OSDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# RF3 2019 LogsDisk
WriteLog " Uploading RF3 2019-LogsDisk..." 1 "White"
$ImageSpec = New-NTNXObject -Name ImageImportSpec
$ImageSpec.containerName = $DestContainerRF3
$ImageSpec.url = "nfs://127.0.0.1" + ($VDiskPaths | Where {$_ -match $VDisks[1].vmDiskUuid})
$LogsDiskTask = New-NTNXImage -Name ("2019-LogsDisk-RF3") -ImageType "disk_image" -ImageImportSpec $ImageSpec

# Wait for tasks to complete
WriteLog " Waiting for upload tasks to complete..." 0 "White"
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
While (!$Task1.completeTime -or !$Task2.completeTime)
{
WriteLog "." 0 "White"
Start-Sleep -Seconds 10
$Task1 = Get-NTNXTask -Taskid $OSDiskTask.taskUuid -IncludeCompleted
$Task2 = Get-NTNXTask -Taskid $LogsDiskTask.taskUuid -IncludeCompleted
}
WriteLog "Complete!" 1 "Green"



# Show how long it took to run this script
WriteLog "" 1 "White"
If ($StartBuildTime) { WriteLog ("Build Time: " + (New-TimeSpan -start $StartBuildTime)) 1 "White" }
WriteLog "" 1 "White"
WriteLog ("Execution Time: " + (New-TimeSpan -start $StartTime)) 1 "White"
$EndTime = Get-Date
WriteLog ("Script End: " + $EndTime) 1 "White"
WriteLog "" 1 "White"


Stop-Transcript

 

Ok, so this time formatting as code worked.  Must have needed to refresh my browser screen…

 

Userlevel 6
Badge +5

Thank you, Tim, for sharing this with the community!