[REST]Pushing a new image Horizon 8 2206 style

With Horizon 8 2206 one of the new features is the fact that you can select a new amount of cpu’s and memory when deploying a new image.

If you know me a but you might understand that I want to know how we can do this using the api’s. As we’ve seen before we needed to do a post against /inventory/v1/desktop-pools/{id}/action/schedule-push-image for build 2206 this was changed to /inventory/v2/desktop-pools/{id}/action/schedule-push-image.

Let’s compare the content of the body that we need to send.

v1:

{
  "add_virtual_tpm": false,
  "im_stream_id": "6f85b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
  "im_tag_id": "0103796c-102b-4ed3-953f-3dfe3d23e0fe",
  "logoff_policy": "WAIT_FOR_LOGOFF",
  "parent_vm_id": "vm-1",
  "snapshot_id": "snapshot-1",
  "start_time": 1587081283000,
  "stop_on_first_error": true
}

v2

{
  "add_virtual_tpm": false,
  "compute_profile_num_cores_per_socket": 1,
  "compute_profile_num_cpus": 4,
  "compute_profile_ram_mb": 4096,
  "im_stream_id": "6f85b3a5-e7d0-4ad6-a1e3-37168dd1ed51",
  "im_tag_id": "0103796c-102b-4ed3-953f-3dfe3d23e0fe",
  "logoff_policy": "WAIT_FOR_LOGOFF",
  "machine_ids": [
    "816d44cb-b486-3c97-adcb-cf3806d53657",
    "414927f3-1a3b-3e4c-81b3-d39602f634dc"
  ],
  "parent_vm_id": "vm-1",
  "selective_push_image": true,
  "snapshot_id": "snapshot-1",
  "start_time": 1587081283000,
  "stop_on_first_error": true
}

So besides the cpu/memory changes we obviously also have something called selective_push_image. This has to do with the added functionality of pushing a secondary image from Horizon 2111. While the example shows it as true the data model makes clear that it is not required and defaults to false. The array of machine_ids reflects the list of machines where the secondary image has to be applied.

compute_profile_num_cores_per_socket	integer($int32)
example: 1
minimum: 1
exclusiveMinimum: false
exclusiveMaximum: false
Indicates the number of cores per socket for the CPU in the compute profile to be configured on clones.
If set, both compute_profile_num_cpus and compute_profile_ram_mb need to be set.

compute_profile_num_cpus	integer($int32)
example: 4
minimum: 1
exclusiveMinimum: false
exclusiveMaximum: false
Indicates the number of CPUs in the compute profile to be configured on clones.
If set, this must be a multiple of compute_profile_num_cores_per_socket.

compute_profile_ram_mb	integer($int32)
example: 4096
minimum: 1024
exclusiveMinimum: false
exclusiveMaximum: false
Indicates the RAM in MB in the compute profile to be configured on clones.

machine_ids	[
example: List [ "816d44cb-b486-3c97-adcb-cf3806d53657", "414927f3-1a3b-3e4c-81b3-d39602f634dc" ]
Set of machines from the desktop pool on which the new image is to be applied. This can be set when selective_push_image is set to true.

selective_push_image	boolean
example: true
Indicates whether selective push image is to be applied. If set to true, the new image will be applied to specified machine_ids in the desktop pool. The image published with this option will be held as a pending image, unless it is promoted or cancelled. The default value is false.

To be able to use this I have updated my previous image deployment script for Horizon 2206.

New arguments are:

  • AddVirtualTPM
    • Boolean to add a virtual TPM or not
  • SecondaryImage
    • Boolean to define the image as secondary (required if you also supply machine_ids)
  • Machine_Ids
    • Array with machine_ids to supply the secondary image to
  • CoresPerSocket
    • Int with # of Cores per Socket
  • CPUs
    • Int for total number of cpus
  • MemoryinMB
    • Memory in mb so 4096 or 6192 for example

 

<#
    .SYNOPSIS
    Pushes a new Golden Image to a Desktop Pool

    .DESCRIPTION
    This script uses the Horizon rest api's to push a new golden image to a VMware Horizon Desktop Pool

    .EXAMPLE
    .\Horizon_Rest_Push_Image.ps1 -ConnectionServerURL https://pod1cbr1.loft.lab -Credentials $creds -vCenterURL "https://pod1vcr1.loft.lab" -DataCenterName "Datacenter_Loft" -baseVMName "W21h1-2021-09-08-15-48" -BaseSnapShotName "Demo Snapshot" -DesktopPoolName "Pod01-Pool02"

    .PARAMETER Credential
    Mandatory: No
    Type: PSCredential
    Object with credentials for the connection server with domain\username and password. If not supplied the script will ask for user and password.

    .PARAMETER ConnectionServerURL
    Mandatory: Yes
    Default: String
    URL of the connection server to connect to

    .PARAMETER vCenterURL
    Mandatory: Yes
    Username of the user to look for

    .PARAMETER DataCenterName
    Mandatory: Yes
    Domain to look in

    .PARAMETER BaseVMName
    Mandatory: Yes
    Domain to look in

    .PARAMETER BaseSnapShotName
    Mandatory: Yes
    Domain to look in

    .PARAMETER DesktopPoolName
    Mandatory: Yes
    Domain to look in

    .PARAMETER StoponError
    Mandatory: No
    Boolean to stop on error or not

    .PARAMETER logoff_policy
    Mandatory: No
    String FORCE_LOGOFF or WAIT_FOR_LOGOFF to set the logoff policy.

    .PARAMETER Scheduledtime
    Mandatory: No
    Time to schedule the image push in [DateTime] format.

    .PARAMETER AddVirtualTPM
    Mandatory: No
    Default: $False
    Boolean FORCE_LOGOFF or WAIT_FOR_LOGOFF to set the logoff policy.

    .PARAMETER SecondaryImage
    Mandatory: No (Yes if machine_ids is supplied)
    Default: $False
    Boolean FORCE_LOGOFF or WAIT_FOR_LOGOFF to set the logoff policy.

    .PARAMETER Machine_Ids
    Mandatory: No
    Array Array of Machine_ids to apply the secondary image to.

    .PARAMETER CoresPerSocket
    Mandatory: No (unless CPUs or MemoryinMB is supplies)
    Int Amount of cores per socket.

    .PARAMETER CPUs
    Mandatory: No (unless MemoryinMB or CoresPerSocket is supplies)
    Int Total number of cores.

    .PARAMETER MemoryinMB
    Mandatory: No (unless CPUs or CoresPerSocket is supplies)
    Int New memory in MB

    .NOTES
    Minimum required version: VMware Horizon 8 2206
    Created by: Wouter Kursten
    First version: 03-11-2021
    Changes: 05-09-2022 -   Added resizing of cpu/memory
                        -   Added secondary image functionality
                        -   Added option to add Virtual TPM


    .COMPONENT
    Powershell Core
#>

[CmdletBinding(DefaultParameterSetName = 'Generic')]
param (
    [Parameter(Mandatory=$false,
    HelpMessage='Credential object as domain\username with password' )]
    [PSCredential] $Credentials,

    [Parameter(Mandatory=$true,
    HelpMessage='FQDN of the connectionserver' )]
    [ValidateNotNullOrEmpty()]
    [string] $ConnectionServerURL,

    [parameter(Mandatory = $true,
    HelpMessage = "URL of the vCenter to look in i.e. https://vcenter.domain.lab")]
    [ValidateNotNullOrEmpty()]
    [string]$vCenterURL,

    [parameter(Mandatory = $true,
    HelpMessage = "Name of the Datacenter to look in.")]
    [ValidateNotNullOrEmpty()]
    [string]$DataCenterName,

    [parameter(Mandatory = $true,
    HelpMessage = "Name of the Golden Image VM.")]
    [ValidateNotNullOrEmpty()]
    [string]$BaseVMName,

    [parameter(Mandatory = $true,
    HelpMessage = "Name of the Snapshot to use for the Golden Image.")]
    [ValidateNotNullOrEmpty()]
    [string]$BaseSnapShotName,

    [parameter(Mandatory = $true,
    HelpMessage = "Name of the Desktop Pool.")]
    [ValidateNotNullOrEmpty()]
    [string]$DesktopPoolName,

    [parameter(Mandatory = $false,
    HelpMessage = "True or false for stop on error.")]
    [ValidateNotNullOrEmpty()]
    [bool]$StoponError = $true,

    [parameter(Mandatory = $false,
    HelpMessage = "Use WAIT_FOR_LOGOFF or FORCE_LOGOFF.")]
    [ValidateSet('WAIT_FOR_LOGOFF','FORCE_LOGOFF', IgnoreCase = $false)]
    [string]$logoff_policy = "WAIT_FOR_LOGOFF",

    [parameter(Mandatory = $false,
    HelpMessage = "DateTime object for the moment of scheduling the image push.Defaults to immediately")]
    [datetime]$Scheduledtime,

    [parameter(Mandatory = $false,
    HelpMessage = "Bool for adding a Virtual TPM or not.")]
    [ValidateNotNullOrEmpty()]
    [bool]$AddVirtualTPM = $False,

    [parameter(Mandatory = $false,
    HelpMessage = "True or false to set this image as secondary image.")]
    [ValidateNotNullOrEmpty()]
    [bool]$SecondaryImage = $False,

    [parameter(Mandatory = $false,
    HelpMessage = "Array of machine_ids to apply the secondary image to.")]
    [ValidateNotNullOrEmpty()]
    [array]$Machine_Ids,

    [parameter(Mandatory = $false,
    HelpMessage = "New Number of cores per socket.")]
    [ValidateNotNullOrEmpty()]
    [int]$CoresPerSocket,

    [parameter(Mandatory = $false,
    HelpMessage = "New Number of CPU's.")]
    [ValidateNotNullOrEmpty()]
    [int]$CPUs,

    [parameter(Mandatory = $false,
    HelpMessage = "New amount of memory in MB.")]
    [ValidateNotNullOrEmpty()]
    [int]$MemoryinMB
)

if($Credentials){
    $username=($credentials.username).split("\")[1]
    $domain=($credentials.username).split("\")[0]
    $password=$credentials.password
}
else{
    $credentials = Get-Credential
    $username=($credentials.username).split("\")[1]
    $domain=($credentials.username).split("\")[0]
    $password=$credentials.password
}

$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password) 
$UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

function Get-HRHeader(){
    param($accessToken)
    return @{
        'Authorization' = 'Bearer ' + $($accessToken.access_token)
        'Content-Type' = "application/json"
    }
}
function Open-HRConnection(){
    param(
        [string] $username,
        [string] $password,
        [string] $domain,
        [string] $url
    )

    $Credentials = New-Object psobject -Property @{
        username = $username
        password = $password
        domain = $domain
    }

    return invoke-restmethod -Method Post -uri "$ConnectionServerURL/rest/login" -ContentType "application/json" -Body ($Credentials | ConvertTo-Json)
}
function Close-HRConnection(){
    param(
        $accessToken,
        $ConnectionServerURL
    )
    return Invoke-RestMethod -Method post -uri "$ConnectionServerURL/rest/logout" -ContentType "application/json" -Body ($accessToken | ConvertTo-Json)
}

if($CPUs -AND $CoresPerSocket -AND $MemoryinMB){
    $resize = $true
}
elseif($CPUs -OR $CoresPerSocket -OR $MemoryinMB){
    throw "If either CPUs, CoresPerSOcket or MemoryinGB is supplied, all must be supplied."
}
else{
    $resize = $false
}

if($Machine_Ids -AND !($SecondaryImage)){
    throw "If either Machine_Ids is supplied SecondaryImage also needs to be supplied."
}


try{
    $accessToken = Open-HRConnection -username $username -password $UnsecurePassword -domain $Domain -url $ConnectionServerURL
}
catch{
    throw "Error Connecting: $_"
}

$vCenters = Invoke-RestMethod -Method Get -uri "$ConnectionServerURL/rest/monitor/v2/virtual-centers" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$vcenterid = ($vCenters | where-object {$_.name -like "*$vCenterURL*"}).id
$datacenters = Invoke-RestMethod -Method Get -uri "$ConnectionServerURL/rest/external/v1/datacenters?vcenter_id=$vcenterid" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$datacenterid = ($datacenters | where-object {$_.name -eq $DataCenterName}).id
$basevms = Invoke-RestMethod -Method Get -uri "$ConnectionServerURL/rest/external/v1/base-vms?datacenter_id=$datacenterid&filter_incompatible_vms=false&vcenter_id=$vcenterid" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$basevmid = ($basevms | where-object {$_.name -eq $baseVMName}).id
$basesnapshots = Invoke-RestMethod -Method Get -uri "$ConnectionServerURL/rest/external/v1/base-snapshots?base_vm_id=$basevmid&vcenter_id=$vcenterid" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$basesnapshotid = ($basesnapshots | where-object {$_.name -eq $BaseSnapShotName}).id
$desktoppools = Invoke-RestMethod -Method Get -uri "$ConnectionServerURL/rest/inventory/v1/desktop-pools" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$desktoppoolid = ($desktoppools | where-object {$_.name -eq $DesktopPoolName}).id

$datahashtable = [ordered]@{}
$datahashtable.add('add_virtual_tpm',$AddVirtualTPM)
if($resize){
    $datahashtable.add('compute_profile_num_cores_per_socket',$CoresPerSocket)
    $datahashtable.add('compute_profile_num_cpus',$CPUs)
    $datahashtable.add('compute_profile_ram_mb',$MemoryinMB)
}
$datahashtable.add('logoff_policy',$logoff_policy)
if($Machine_Ids){
    $datahashtable.add('machine_ids',$Machine_Ids)
}
$datahashtable.add('parent_vm_id',$basevmid)
if($SecondaryImage){
    $datahashtable.add('selective_push_image',$SecondaryImage)
}
$datahashtable.add('snapshot_id',$basesnapshotid)
if($Scheduledtime){
    $starttime = get-date $Scheduledtime
    $epoch = ([DateTimeOffset]$starttime).ToUnixTimeMilliseconds()
    $datahashtable.add('start_time',$epoch)
}
$datahashtable.add('stop_on_first_error',$StoponError)
$json = $datahashtable | convertto-json

Invoke-RestMethod -Method Post -uri "$ConnectionServerURL/rest/inventory/v2/desktop-pools/$desktoppoolid/action/schedule-push-image" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken) -body $json

As always the script is available on Github.

Usage:

I am using all the new arguments except the virtual tpm one.

D:\GIT\Various_Scripts\Horizon_Rest_Push_Image_VDI_2206.ps1 -ConnectionServerURL https://pod1cbr1.loft.lab -Credentials $creds -vCenterURL "https://pod1vcr1.loft.lab" -DataCenterName "Datacenter_Loft" -baseVMName "W21h1-gi-2022-08-19-09-36" -BaseSnapShotName "VM Snapshot 9%2f5%2f2022, 6:50:12 PM" -DesktopPoolName "Pod01-Pool03" -CPU 2 -CoresPerSocket 1 -MemoryinMB 4096 -SecondaryImage $true -Machine_Ids $array

Bookmark the permalink.

Comments are closed.