Search Results for: horizon

Horizon 8.0 (2006) API Changelog

These are all available methods in Horizon 8.0 2006.

API Explorer Page: VMware Horizon Server API – VMware API Explorer – VMware {code}

Swagger Specification Download: https://vdc-download.vmware.com/vmwb-repository/dcr-public/f1131c66-d190-4fe8-9684-d1617307afdc/6b717dc5-634a-4f27-9107-ce475def55d4/REST-api-docs_8.0_postRC.json

There are changes from Horizon 7.* but I am only documenting for 8.* so this is the full list of available methods.

Methods

/

/refresh post
/login post
/logout post

/config

/config/v1/environment-properties get
/config/v1/ic-domain-accounts get
/config/v1/ic-domain-accounts post
/config/v1/ic-domain-accounts/{id} delete
/config/v1/ic-domain-accounts/{id} get
/config/v1/ic-domain-accounts/{id} put
/config/v1/im-assets get
/config/v1/im-assets post
/config/v1/im-assets/{id} delete
/config/v1/im-assets/{id} get
/config/v1/im-assets/{id} put
/config/v1/im-assets/action/bulk-create post
/config/v1/im-streams get
/config/v1/im-streams post
/config/v1/im-streams/{id} delete
/config/v1/im-streams/{id} get
/config/v1/im-streams/{id} put
/config/v1/im-streams/action/bulk-create post
/config/v1/im-tags get
/config/v1/im-tags post
/config/v1/im-tags/{id} delete
/config/v1/im-tags/{id} get
/config/v1/im-tags/{id} put
/config/v1/im-tags/action/bulk-create post
/config/v1/im-versions get
/config/v1/im-versions post
/config/v1/im-versions/{id} delete
/config/v1/im-versions/{id} get
/config/v1/im-versions/{id} put
/config/v1/im-versions/action/bulk-create post
/config/v1/rcx/clients post
/config/v1/rcx/clients/{id} delete
/config/v1/rcx/clients/{id} put
/config/v1/rcx/servers get
/config/v1/settings get
/config/v1/settings put
/config/v1/settings/feature get
/config/v1/settings/feature put
/config/v1/settings/general get
/config/v1/settings/general put
/config/v1/settings/security get
/config/v1/settings/security put
/config/v1/virtual-centers get

/entitlements

/entitlements/v1/application-pools delete
/entitlements/v1/application-pools get
/entitlements/v1/application-pools post
/entitlements/v1/application-pools/{id} get
/entitlements/v1/desktop-pools delete
/entitlements/v1/desktop-pools get
/entitlements/v1/desktop-pools post
/entitlements/v1/desktop-pools/{id} get

/external

/external/v1/ad-domains get
/external/v1/ad-users-or-groups get
/external/v1/ad-users-or-groups/{id} get
/external/v1/ad-users-or-groups/action/change-user-password post
/external/v1/ad-users-or-groups/action/validate-user-encrypted-credentials post
/external/v1/base-snapshots get
/external/v1/base-vms get
/external/v1/customization-specifications get
/external/v1/datacenters get
/external/v1/datastore-paths get
/external/v1/datastores get
/external/v1/hosts-or-clusters get
/external/v1/network-interface-cards get
/external/v1/network-labels get
/external/v1/resource-pools get
/external/v1/vm-folders get
/external/v1/vm-templates get

/inventory

/inventory/v1/application-icons get
/inventory/v1/application-icons/{id} get
/inventory/v1/application-pools get
/inventory/v1/application-pools post
/inventory/v1/application-pools/{id} delete
/inventory/v1/application-pools/{id} get
/inventory/v1/application-pools/{id} put
/inventory/v1/desktop-pools get
/inventory/v1/desktop-pools/{id} get
/inventory/v1/desktop-pools/{id}/action/add-machines post
/inventory/v1/desktop-pools/{id}/action/add-machines-by-name post
/inventory/v1/desktop-pools/{id}/action/remove-machines post
/inventory/v1/desktop-pools/{id}/installed-applications get
/inventory/v1/farms get
/inventory/v1/farms/{id} get
/inventory/v1/farms/{id}/installed-applications get
/inventory/v1/machines delete
/inventory/v1/machines get
/inventory/v1/machines/{id} delete
/inventory/v1/machines/{id} get
/inventory/v1/machines/{id}/action/assign-users post
/inventory/v1/machines/{id}/action/unassign-users post
/inventory/v1/machines/action/enter-maintenance post
/inventory/v1/machines/action/exit-maintenance post
/inventory/v1/machines/action/rebuild post
/inventory/v1/machines/action/recover post
/inventory/v1/machines/action/reset post
/inventory/v1/machines/action/restart post
/inventory/v1/sessions get
/inventory/v1/sessions/{id} get
/inventory/v1/sessions/action/disconnect post
/inventory/v1/sessions/action/logoff post
/inventory/v1/sessions/action/reset post
/inventory/v1/sessions/action/restart post
/inventory/v1/sessions/action/send-message post
/inventory/v2/desktop-pools get
/inventory/v2/desktop-pools/{id} get

/monitor

/monitor/ad-domains get
/monitor/connection-servers get
/monitor/event-database get
/monitor/farms get
/monitor/gateways get
/monitor/rds-servers get
/monitor/saml-authenticators get
/monitor/v1/connection-servers/{id} get
/monitor/v1/farms/{id} get
/monitor/v1/gateways/{id} get
/monitor/v1/pods get
/monitor/v1/pods/{id} get
/monitor/v1/rds-servers/{id} get
/monitor/v1/saml-authenticators/{id} get
/monitor/v1/true-sso get
/monitor/v1/true-sso/{id} get
/monitor/v1/view-composers/{vcId} get
/monitor/v1/virtual-centers/{id} get
/monitor/v2/ad-domains get
/monitor/v2/connection-servers get
/monitor/v2/gateways get
/monitor/v2/pods get
/monitor/v2/saml-authenticators get
/monitor/v2/view-composers get
/monitor/v2/virtual-centers get
/monitor/view-composers get
/monitor/virtual-centers get

Horizon 8.4 (2111) API Changelog

4These are the added REST API calls in Horizon 8.4 (2111) since 8.3.

API Explorer Page: VMware Horizon Server API – VMware API Explorer – VMware {code}

Swagger Specification Download: https://vdc-download.vmware.com/vmwb-repository/dcr-public/8a9b6483-f9e8-4da4-89d6-32ce0f2e7c8f/807647c4-d4d4-40aa-9e54-bdd662c115b2/rest-api-swagger-docs.json

Methods

/inventory

/inventory/v1/desktop-pools post
/inventory/v1/desktop-pools/{id} delete
/inventory/v1/desktop-pools/{id} put
/inventory/v1/desktop-pools/{id}/action/validate-installed-applications post
/inventory/v1/desktop-pools/{id}/tasks/{taskId}/action/pause post
/inventory/v1/desktop-pools/{id}/tasks/{taskId}/action/resume post
/inventory/v1/desktop-pools/action/validate-specified-names post
/inventory/v1/farms/{id}/action/add-rds-servers post
/inventory/v1/farms/{id}/action/cancel-scheduled-maintenance post
/inventory/v1/farms/{id}/action/remove-rds-servers post
/inventory/v1/farms/{id}/action/schedule-maintenance post
/inventory/v1/farms/{id}/action/validate-installed-applications post
/inventory/v1/global-application-entitlements post
/inventory/v1/global-application-entitlements/{id} delete
/inventory/v1/global-application-entitlements/{id} put
/inventory/v1/global-application-entitlements/{id}/compatible-backup-global-application-entitlements get
/inventory/v1/global-desktop-entitlements/{id} delete
/inventory/v1/global-desktop-entitlements/{id} put
/inventory/v1/global-desktop-entitlements/{id}/compatible-backup-global-desktop-entitlements get
/inventory/v1/global-sessions get
/inventory/v1/global-sessions/action/disconnect post
/inventory/v1/global-sessions/action/logoff post
/inventory/v1/global-sessions/action/reset post
/inventory/v1/global-sessions/action/restart post
/inventory/v1/global-sessions/action/send-message post
/inventory/v2/farms post
/inventory/v2/farms/{id} put
/inventory/v2/global-application-entitlements get
/inventory/v2/global-application-entitlements/{id} get
/inventory/v2/global-desktop-entitlements get
/inventory/v2/global-desktop-entitlements post
/inventory/v2/global-desktop-entitlements/{id} get
/inventory/v3/farms get
/inventory/v3/farms/{id} get
/inventory/v5/desktop-pools get
/inventory/v5/desktop-pools/{id} get

04

Horizon 8.3 (2106) API Changelog

These are the added REST API calls in Horizon 8.3 (2012) since 8.2.

API Explorer Page: VMware Horizon Server API – VMware API Explorer – VMware {code}

Swagger Specification Download: https://vdc-download.vmware.com/vmwb-repository/dcr-public/8ed56396-c758-4cec-a55e-932ea4cf2268/53865939-6188-4f0d-88f7-6c6ec254ee6a/rest-api-swagger-docs.json

Methods

/config

/config/v1/federation-access-groups get
/config/v1/federation-access-groups post
/config/v1/federation-access-groups/{id} delete
/config/v1/federation-access-groups/{id} get

/external

/external/v1/ad-domains/{id}/action/add-auxiliary-accounts post
/external/v1/ad-domains/{id}/action/delete-auxiliary-accounts post
/external/v1/ad-domains/action/update-auxiliary-accounts post
/external/v1/audit-events get
/external/v1/audit-events/extended-attributes get
/external/v3/ad-domains get

/monitor

/monitor/v3/ad-domains get

Horizon 8.2 (2103) API Changelog

These are the added REST API calls in Horizon 8.2 (2103) since 8.1.

API Explorer Page: VMware Horizon Server API – VMware API Explorer – VMware {code}

Swagger Specification Download: https://vdc-download.vmware.com/vmwb-repository/dcr-public/83f9bf12-28a3-46d4-86fe-dc460f2cbf59/ff2ae361-a1a6-40e0-977e-7ad324ad0556/rest-api-swagger-docs.json

Methods

/config

/config/v1/local-access-groups get
/config/v1/local-access-groups/{id} get

/external

/external/v1/ad-domains/{id}/action/unbind post
/external/v1/ad-domains/{id}/action/update post
/external/v1/ad-domains/action/bind post
/external/v1/datastore-clusters get
/external/v1/datastores/action/compute-requirements post
/external/v2/ad-domains get

/inventory

/inventory/v1/application-icons post
/inventory/v1/application-pools/{id}/action/add-custom-icon post
/inventory/v1/application-pools/{id}/action/remove-custom-icon post
/inventory/v1/application-pools/action/check-name-availability post
/inventory/v1/desktop-pools/action/check-name-availability post
/inventory/v1/farms post
/inventory/v1/farms/{id} delete
/inventory/v1/farms/{id} put
/inventory/v1/farms/action/check-name-availability post
/inventory/v1/machines/action/check-name-availability post
/inventory/v1/rds-servers/action/check-name-availability post
/inventory/v2/application-pools post
/inventory/v2/application-pools/{id} put
/inventory/v2/farms get
/inventory/v2/farms/{id} get
/inventory/v3/application-pools get
/inventory/v3/application-pools/{id} get
/inventory/v4/desktop-pools get
/inventory/v4/desktop-pools/{id} get

Horizon 8.1 (2012) API Changelog

These are the added REST API calls in Horizon 8.1 (2012) since 8.0.

API Explorer Page: VMware Horizon Server API – VMware API Explorer – VMware {code}

Swagger Specification Download: https://vdc-download.vmware.com/vmwb-repository/dcr-public/01aa80e7-3ded-46f2-8b49-32ef6ea4569a/69ccda3d-b983-458d-a998-0a1bd71f9c85/api-docs-8_1_GA.json

Methods

/config

/config/v2/environment-properties get
/config/v2/virtual-centers get

/entitlements

/entitlements/v1/global-application-entitlements delete
/entitlements/v1/global-application-entitlements get
/entitlements/v1/global-application-entitlements post
/entitlements/v1/global-application-entitlements/{id} get
/entitlements/v1/global-desktop-entitlements delete
/entitlements/v1/global-desktop-entitlements get
/entitlements/v1/global-desktop-entitlements post
/entitlements/v1/global-desktop-entitlements/{id} get

/external

/external/v1/virtual-machines get

/federation

/federation/v1/cpa get 2012
/federation/v1/cpa put 2012
/federation/v1/cpa/action/eject post 2012
/federation/v1/cpa/action/initialize post 2012
/federation/v1/cpa/action/join post 2012
/federation/v1/cpa/action/uninitialize post 2012
/federation/v1/cpa/action/unjoin post 2012
/federation/v1/cpa/tasks get 2012
/federation/v1/cpa/tasks/{id} get 2012
/federation/v1/home-sites delete 2012
/federation/v1/home-sites get 2012
/federation/v1/home-sites post 2012
/federation/v1/home-sites/{id} get 2012
/federation/v1/home-sites/action/resolve post 2012
/federation/v1/pod-assignments get 2012
/federation/v1/pod-assignments/{id} get 2012
/federation/v1/pods get 2012
/federation/v1/pods/{id} get 2012
/federation/v1/pods/{id} put 2012
/federation/v1/pods/{id}/endpoints get 2012
/federation/v1/pods/{id}/endpoints/{endpointId} get 2012
/federation/v1/sites get 2012
/federation/v1/sites post 2012
/federation/v1/sites/{id} delete 2012
/federation/v1/sites/{id} get 2012
/federation/v1/sites/{id} put 2012

/inventory

/inventory/v1/desktop-pools/{id}/action/cancel-scheduled-push-image post 2012
/inventory/v1/desktop-pools/{id}/action/schedule-push-image post 2012
/inventory/v1/desktop-pools/{id}/tasks get 2012
/inventory/v1/desktop-pools/{id}/tasks/{taskId} get 2012
/inventory/v1/desktop-pools/{id}/tasks/{taskId}/action/cancel post 2012
/inventory/v1/global-application-entitlements get 2012
/inventory/v1/global-application-entitlements/{id} get 2012
/inventory/v1/global-application-entitlements/{id}/compatible-local-application-pools get 2012
/inventory/v1/global-application-entitlements/{id}/local-application-pools delete 2012
/inventory/v1/global-application-entitlements/{id}/local-application-pools get 2012
/inventory/v1/global-application-entitlements/{id}/local-application-pools post 2012
/inventory/v1/global-desktop-entitlements get 2012
/inventory/v1/global-desktop-entitlements post 2012
/inventory/v1/global-desktop-entitlements/{id} get 2012
/inventory/v1/global-desktop-entitlements/{id}/compatible-local-desktop-pools get 2012
/inventory/v1/global-desktop-entitlements/{id}/local-desktop-pools delete 2012
/inventory/v1/global-desktop-entitlements/{id}/local-desktop-pools get 2012
/inventory/v1/global-desktop-entitlements/{id}/local-desktop-pools post 2012
/inventory/v1/machines/{id}/action/assign-aliases post 2012
/inventory/v1/machines/{id}/action/unassign-aliases post 2012
/inventory/v1/physical-machines get 2012
/inventory/v1/physical-machines/{id} delete 2012
/inventory/v1/physical-machines/{id} get 2012
/inventory/v1/physical-machines/action/register post 2012
/inventory/v1/rds-servers get 2012
/inventory/v1/rds-servers/{id} delete 2012
/inventory/v1/rds-servers/{id} get 2012
/inventory/v1/rds-servers/{id} put 2012
/inventory/v1/rds-servers/action/recover post 2012
/inventory/v1/rds-servers/action/register post 2012
/inventory/v2/application-pools get 2012
/inventory/v2/application-pools/{id} get 2012
/inventory/v2/machines get 2012
/inventory/v2/machines/{id} get 2012
/inventory/v3/desktop-pools get 2012
/inventory/v3/desktop-pools/{id} get 2012

/monitor

/monitor/v1/desktop-pools/metrics get

Horizon 8 API Changelog

This series of pages contain the available REST API methods for Horizon 8 in 2006 and all changes since. Data is based on the Swagger exports from the API explorer (documentation tab)

Horizon 8.0 version 2006

Horizon 8.1 version 2012

Horizon 8.2 version 2103

Horizon 8.3 version 2106

Horizon 8.4 version 2111

Horizon 8.5 version 2203 (no changes)

Horizon 8.6 version 2206

Horizon 8.7 version 2209

Horizon 8.8 version 2212

Horizon 8.9 version 2303

Powershell script to gather (Global) Horizon sessions using REST api’s

This ia a rather quick one as I already had some of the basics in my last script to send messages to global sessions. I have created a script that will gather all sessions in a Horizon environment. The global sessions api call has only been available since Horizon 2111 so when trying with an older version of Horizon it will return errors. Because some of the methods I use Powershell 7 is required.

There are 4 arguments:

  1. Credential : Optional credentials object from get-credential (wil ask for credentials if not provided)
  2. ConnectionServerFQDN : name says enough, fqdn for a connection server to connect to
  3. global : switch to enable gathering of all Global sessions
  4. pod_name: name of a single pod to collect all sessions from in case of a CPA

Usage:

.\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN "server.domain.dom"

And the other options are

.\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN "server.domain.dom" -global
.\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN "server.domain.dom" -global -pod_name "podname"

The Script can be found down below or on Github:

<#
    .SYNOPSIS
    Gets all (global) sessions for an Horizon environment

    .DESCRIPTION
    This script uses the Horizon rest api's to all gather alls essions in a Horizon (Cloud Pod) environment

    .EXAMPLE
    .\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab

    .EXAMPLE
    .\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab -global

    .EXAMPLE
    .\Horizon_Rest_Get_Sessions.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab -pod_name "Horizon_pod2"

    .PARAMETER Credential
    Mandatory: No
    Type: PSCredential
    Object with credentials for the connection server with domain\username and password

    .PARAMETER ConnectionServerFQDN
    Mandatory: Yes
    Default: String
    FQDN of the connection server to connect to

    .PARAMETER global
    Mandatory: No
    Switch to select global sessions or only sessions for the local pod (Horizon 2111 and later)

    .PARAMETER pod_name
    Mandatory: No
    String for name of the pod to get sessions for. (Horizon 2111 and later)

    .NOTES
    Created by: Wouter Kursten
    First version: 03-06-2022

    .COMPONENT
    Powershell Core

#>

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

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

    [Parameter(Mandatory=$false,
    ParameterSetName="globalsessions",
    HelpMessage='Parameter to select Global Sessions' )]
    [switch] $global,

    [Parameter(Mandatory=$false,
    ParameterSetName="globalsessions",
    HelpMessage='Name of the pod to get the sessions for, needs -Global, defaults to *' )]
    [string] $pod_name
)

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 "$url/rest/login" -ContentType "application/json" -Body ($Credentials | ConvertTo-Json)
}

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

function Get-HorizonRestData(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='Array of ordered hashtables' )]
        [array] $filters,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='Type of filter Options: And, Or' )]
        [ValidateSet('And','Or')]
        [string] $Filtertype,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='Page size, default = 500' )]
        [int] $pagesize = 500,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [string] $RestMethod,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='$True for rest methods that contain pagination and filtering, default = False' )]
        [switch] $filteringandpagination,

        [Parameter(Mandatory=$false,
        ParameterSetName="id",
        HelpMessage='To be used with single id based queries like /monitor/v1/connection-servers/{id}' )]
        [string] $id,

        [Parameter(Mandatory=$false,
        HelpMessage='Extra additions to the query url that comes before the paging/filtering parts like brokering_pod_id=806ca in /rest/inventory/v1/global-sessions?brokering_pod_id=806ca&page=2&size=100' )]
        [string] $urldetails
    )
    
    if($filteringandpagination){
        if ($filters){
            $filterhashtable = [ordered]@{}
            $filterhashtable.add('type',$filtertype)
            $filterhashtable.filters = @()
            foreach($filter in $filters){
                $filterhashtable.filters+=$filter
            }
            $filterflat=$filterhashtable | convertto-json -Compress
            if($urldetails){
                $urlstart= $ServerURL+"/rest/"+$RestMethod+"?"+$urldetails+"&filter="+$filterflat+"&page="
            }
            else{
                $urlstart= $ServerURL+"/rest/"+$RestMethod+"?filter="+$filterflat+"&page="
            }
        }
        else{
            if($urldetails){
                $urlstart= $ServerURL+"/rest/"+$RestMethod+"?"+$urldetails+"&page="
            }
            else{
                $urlstart= $ServerURL+"/rest/"+$RestMethod+"?page="
            }
        }
        $results = [System.Collections.ArrayList]@()
        $page = 1
        $uri = $urlstart+$page+"&size=$pagesize"

        $response = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
        $response.foreach({$results.add($_)}) | out-null
        if ($responseheader.HAS_MORE_RECORDS -contains "TRUE") {
            do {
                $page++
                $uri = $urlstart+$page+"&size=$pagesize"
                $response = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
                $response.foreach({$results.add($_)}) | out-null
            } until ($responseheader.HAS_MORE_RECORDS -notcontains "TRUE")
        }
    }
    elseif($id){
        $uri= $ServerURL+"/rest/"+$RestMethod+"/"+$id
        $results = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
    }
    else{
        if($urldetails){
            $uri= $ServerURL+"/rest/"+$RestMethod+"?"+$urldetails
        }
        else{
            $uri= $ServerURL+"/rest/"+$RestMethod
        }

        $results = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
    }

    return $results
}

function get-horizonglobalsessions(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken,

        [Parameter(Mandatory=$true,
        HelpMessage='Id of the Local pod to query' )]
        [string] $podid
    )
    try{
        $results=Get-HorizonRestData -ServerURL $url -RestMethod "/inventory/v1/global-sessions" -accessToken $accessToken -urldetails "pod_id=$podid"
    }
    catch{
        throw $_
    }
    return $results
}

function Get-Pods(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken
    )

    try{
        $results=Get-HorizonRestData -ServerURL $url -RestMethod "/federation/v1/pods" -accessToken $accessToken
    }
    catch{
        throw $_
    }
    return $results
}


if($Credential){
    $creds = $credential
}
else{
    $creds = get-credential
}

$ErrorActionPreference = 'Stop'

$username=($creds.username).split("\")[1]
$domain=($creds.username).split("\")[0]
$password=$creds.password

$url = "https://$ConnectionServerFQDN"

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

$accessToken = Open-HRConnection -username $username -password $UnsecurePassword -domain $Domain -url $url


if($global){
    $pods=get-pods -accessToken $accessToken -ServerURL $url
    if($pod_name){
        $pod = $pods | Where-Object {$_.name -eq $pod_name}
        $podid=$pod.id
        $sessions = get-horizonglobalsessions -accessToken $accessToken -ServerURL $url -podid $podid -filteringandpagination
        return $sessions
    }
    else{
        $sessions=@()
        foreach ($pod in $pods){
            $podid=$pod.id
            $sessions += get-horizonglobalsessions -accessToken $accessToken -ServerURL $url -podid $podid -filteringandpagination
        }
        return $sessions
    }
}
else{
    $sessions = Get-HorizonRestData -ServerURL $url -RestMethod "/inventory/v1/sessions" -accessToken $accessToken -filteringandpagination
    return $sessions
}

 

PowerCLI Script to Horizon Desktop Pool machine counts & provisioning type

A long time ago in a galaxy far way I used to be a freelancer for ControlUp creating Script Actions and that actually helped me in securing a job with this great company. One of the first SBA’s that I made was one to change the amount of machines in a desktop pool. Recently one of our customers asked if it was possible to also control the minimum amount and powered on machines. Today I have updated this sba and it will be published shortly (if it hasn’t been published when you read this hit me up for a preview sba xml file). I took it a step further though and added the option to change the provisioning type. With a small security piece in place to prevent you from accidentally changing the type. Besides this being published as an sba I have also published a script that can be used from any computer using PowerCLI.

To be clear: this script uses PowerCLI with the SOAP api’s so it should work with almost all Horizon Versions since 7.5. If I find the time I will create a REST version but that will only work with Horizon 8 2111 and above.

The parameters:

  • Credentials : This optional parameter needs a credential object from get-credential. If you don’t supply it you will get a popup for credentials
  • HVDesktopPoolname: Required parameter with the name of the Desktop Pool to change
  • HVConnectionServerFQDN: Required parameter with the FQDN for a connection server to connect to
  • Provisioningtype: Optional Parameter if you want to change the provisioning type. Has to be either UP_FRONT or ON_DEMAND
  • ChangeProvisioningtype: optional parameter that needs either $true or $false and defaults to $false if not provided. The script will error if you set this to false while the provisionintype is different from the current one.
  • maxNumberOfMachines: required parameter with the maximum amount of machines
  • minNumberOfMachines: required parameter when using ON_DEMAND as provisioning type for the minimum amount of machines. Validation is done later in the script so it will not ask for an amount if not provided.
  • numberOfSpareMachines: required parameter when using ON_DEMAND as provisioning type for the minimum amount of powered on machines. Validation is done later in the script so it will not ask for an amount if not provided.

Usage:

Set-Desktoppoolmachinecountandtype.ps1 -Credentials $creds  -HVDesktopPoolname Pod01-Pool02 -HVConnectionServerFQDN pod1cbr1.loft.lab -Provisioningtype ON_DEMAND -maxNumberOfMachines 10 -minNumberOfMachines 3 -ChangeProvisioningtype $true -numberOfSpareMachines 4

or

Set-Desktoppoolmachinecountandtype.ps1 -Credentials $creds  -HVDesktopPoolname Pod01-Pool02 -HVConnectionServerFQDN pod1cbr1.loft.lab -Provisioningtype UP_FRONT -maxNumberOfMachines 10 -ChangeProvisioningtype $false

there’s an option to add -verbose for a bit more visibility, I will use this in my screenshots:

Changing the count for an pool that provisions all desktops up front

Changing the count & type but not setting the changeprovisioningtype to $true

Corrected changeprovisioningtype

As usual the script is available on Github or down below

<#
    .SYNOPSIS
    Changes the amount of Desktops in a Horizon Desktop Pool

    .DESCRIPTION
    This script changes the amount of Desktops in a Horizon Desktop Pool.

    .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 HVDesktopPoolname
    Name of the Desktop Pool to update

    .PARAMETER Provisioningtype
    Use ON_DEMAND to provision all desktops up front (will ignore minNumberOfMachines and numberOfSpareMachines

    .PARAMETER ChangeProvisioningtype
    User either True or False to enable or disable the changing of the provisioning type

    .PARAMETER maxNumberOfMachines
    Maximum number of desktops in the pool

    .PARAMETER minNumberOfMachines
    Minimum number of desktops in the pool

    .PARAMETER numberOfSpareMachines
    Minimum number of powered on desktops in the pool

    .PARAMETER HVConnectionServerFQDN
    FQDN for a connectionserver in the pod the pool belongs to.

    .EXAMPLE
    Set-Desktoppoolmachinecountandtype.ps1 -Credentials $creds  -HVDesktopPoolname Pod01-Pool02 -HVConnectionServerFQDN pod1cbr1.loft.lab -Provisioningtype ON_DEMAND -maxNumberOfMachines 10 -minNumberOfMachines 3 -ChangeProvisioningtype $true -numberOfSpareMachines 4
    
    .EXAMPLE
    Set-Desktoppoolmachinecountandtype.ps1 -Credentials $creds  -HVDesktopPoolname Pod01-Pool02 -HVConnectionServerFQDN pod1cbr1.loft.lab -Provisioningtype UP_FRONT -maxNumberOfMachines 10 -ChangeProvisioningtype $false

    .EXAMPLE
    Set-Desktoppoolmachinecountandtype.ps1 -Credentials $creds  -HVDesktopPoolname Pod01-Pool02 -HVConnectionServerFQDN pod1cbr1.loft.lab -maxNumberOfMachines 10

    .NOTES
    This script requires VMWare PowerCLI to be installed on the machine running the script.
    PowerCLI can be installed through PowerShell (PowerShell version 5 or higher required) by running the command 'Install-Module VMWare.PowerCLI -Force -AllowCLobber -Scope AllUsers' Or by using the 'Install VMware PowerCLI' script.
    Credentials can be set using the 'Prepare machine for Horizon View scripts' script.

    Modification history:   12/12/2019 - Wouter Kursten - First version
                            26/03/2022 - Wouter Kursten - Added options for on demand provisioning

    .LINK
    https://code.vmware.com/web/tool/11.3.0/vmware-powercli


    .COMPONENT
    VMWare PowerCLI

#>

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

    [Parameter(
        Mandatory=$true,
        HelpMessage='Name of the Desktop Pool'
    )]
    [ValidateNotNullOrEmpty()]
    [string] $HVDesktopPoolname,

    [Parameter(
        Mandatory=$true,
        HelpMessage='FQDN for the connection server'
    )]
    [ValidateNotNullOrEmpty()]
    [string] $HVConnectionServerFQDN,

    [Parameter(
        Mandatory=$false,
        HelpMessage='Provisioning type'
    )]
    [ValidateSet("UP_FRONT","ON_DEMAND")]
    [string] $Provisioningtype,

    [Parameter(
        Mandatory=$false,
        HelpMessage='Change Provisioning type?'
    )]
    [ValidateSet("True","False")]
    [bool] $ChangeProvisioningtype = $false,

    [Parameter(
        Mandatory=$true,
        HelpMessage='Maximum number of machines in the desktop.'
    )]
    [ValidateNotNullOrEmpty()]
    [int] $maxNumberOfMachines,

    [Parameter(
        Mandatory=$false,
        ParameterSetName = 'ondemand',
        HelpMessage='The minimum number of machines to have provisioned if on demand provisioning is selected. Will be ignored if provisioningtype is set to UP_FRONT.'
    )]
    [ValidateNotNullOrEmpty()]
    [int] $minNumberOfMachines,

    [Parameter(
        Mandatory=$false,
        ParameterSetName = 'ondemand',
        HelpMessage='Number of spare powered on machines. Will be ignored if provisioningtype is set to UP_FRONT.'
    )]
    [ValidateNotNullOrEmpty()]
    [int] $numberOfSpareMachines
)

$ErrorActionPreference = 'Stop'

function Load-VMWareModules {
    <# Imports VMware modules
    NOTES:
    - The required modules to be loaded are passed as an array.
    - In versions of PowerCLI below 6.5 some of the modules can't be imported (below version 6 it is Snapins only) using so Add-PSSnapin is used (which automatically loads all VMWare modules)
    #>

    param (
        [parameter(Mandatory = $true,
            HelpMessage = "The VMware module to be loaded. Can be single or multiple values (as array).")]
        [array]$Components
    )

    # Try Import-Module for each passed component, try Add-PSSnapin if this fails (only if -Prefix was not specified)
    # Import each module, if Import-Module fails try Add-PSSnapin
    foreach ($component in $Components) {
        try {
            $null = Import-Module -Name VMware.$component
        }
        catch {
            try {
                $null = Add-PSSnapin -Name VMware
            }
            catch {
                write-error 'The required VMWare modules were not found as modules or snapins. Please check the .NOTES and .COMPONENTS sections in the Comments of this script for details.'
                exit
            }
        }
    }
}

function Connect-HorizonConnectionServer {
    param (
        [parameter(Mandatory = $true,
            HelpMessage = "The FQDN of the Horizon View Connection server. IP address may be used.")]
        [string]$HVConnectionServerFQDN,
        [parameter(Mandatory = $true,
            HelpMessage = "The PSCredential object used for authentication.")]
        [PSCredential]$Credential
    )
    # Try to connect to the Connection server
    try {
        Connect-HVServer -Server $HVConnectionServerFQDN -Credential $Credential
    }
    catch {
        write-error "There was a problem connecting to the Horizon View Connection server: $_."
        exit
    }
}

function Disconnect-HorizonConnectionServer {
    param (
        [parameter(Mandatory = $true,
            HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )
    # Try to connect from the connection server
    try {
        Disconnect-HVServer -Server $HVConnectionServer -Confirm:$false
    }
    catch {
        write-error  "There was a problem disconnecting from the Horizon View Connection server: $_"
        exit
    }
}

function Get-HVDesktopPool {
    param (
        [parameter(Mandatory = $true,
        HelpMessage = "Name of the Desktop Pool.")]
        [string]$HVPoolName,
        [parameter(Mandatory = $true,
        HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )
    # Try to get the Desktop pools in this pod
    try {
        # create the service object first
        [VMware.Hv.QueryServiceService]$queryService = New-Object VMware.Hv.QueryServiceService
        # Create the object with the definiton of what to query
        [VMware.Hv.QueryDefinition]$defn = New-Object VMware.Hv.QueryDefinition
        # entity type to query
        $defn.queryEntityType = 'DesktopSummaryView'
        # Filter oud rds desktop pools since they don't contain machines
        $defn.Filter = New-Object VMware.Hv.QueryFilterEquals -property @{'memberName'='desktopSummaryData.displayName'; 'value' = "$HVPoolname"}
        # Perform the actual query
        [array]$queryResults= ($queryService.queryService_create($HVConnectionServer.extensionData, $defn)).results
        # Remove the query
        $queryService.QueryService_DeleteAll($HVConnectionServer.extensionData)
        # Return the results
        if (!$queryResults){
            write-error  "Can't find $HVPoolName, exiting"
            exit
        }
        elseif (($queryResults).desktopsummarydata.type -eq "MANUAL"){
            write-output  "This a manual Horizon View Desktop Pool, cannot change the amount of desktops"
            exit
        }
        elseif (($queryResults).desktopsummarydata.source -eq "VIRTUAL_CENTER"){
            write-output  "This a Full Clone Horizon View Desktop Pool, if the amount of desktops has been reduced the extra systems need to be removed manually"
            return $queryResults
        }
        else {
            return $queryResults
        }
    }
    catch {
        write-error  "There was a problem retreiving the Horizon View Desktop Pool: $_"
        exit
    }
}

function get-hvpoolspec{
    param (
        [parameter(Mandatory = $true,
            HelpMessage = "ID of the Desktop Pool.")]
        [VMware.Hv.DesktopId]$HVPoolID,
        [parameter(Mandatory = $true,
            HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )
    try {
        $HVConnectionServer.ExtensionData.Desktop.Desktop_Get($HVPoolID)
    }
    catch {
        write-error "There was a problem retreiving the desktop pool details: $_"
        exit
    }
}

function Set-HVPool {
    param (
        [parameter(Mandatory = $true,
            HelpMessage = "ID of the Desktop Pool.")]
        [VMware.Hv.DesktopId]$HVPoolID,
        [parameter(Mandatory = $true,
        HelpMessage = "Provisioning type UP_FRONT or ON_DEMAND")]
        [ValidateSet("UP_FRONT","ON_DEMAND")]
        [string] $Provisioningtype,
        [parameter(Mandatory = $true,
            HelpMessage = "Desired amount of desktops in the pool.")]
        [int]$maxNumberOfMachines,
        [parameter(Mandatory = $false,
        HelpMessage = "Desired amount of spare desktops in the pool.")]
        [int]$numberOfSpareMachines,
        [parameter(Mandatory = $false,
        HelpMessage = "Desired minimum amount of desktops in the pool.")]
        [int]$minNumberOfMachines,
        [parameter(Mandatory = $true,
            HelpMessage = "The Horizon View Connection server object.")]
        [VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$HVConnectionServer
    )
    if($Provisioningtype -eq "UP_FRONT"){
        try {
            # First define the Service we need
            [VMware.Hv.DesktopService]$desktopservice=new-object vmware.hv.DesktopService
            # Fill the helper for this service with the application information
            $desktophelper=$desktopservice.read($HVConnectionServer.extensionData, $HVPoolID)
            # Change the state of the application in the helper
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setMaxNumberOfMachines($maxNumberOfMachines)
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setProvisioningTime("UP_FRONT")
            # Apply the helper to the actual object
            $desktopservice.update($HVConnectionServer.extensionData, $desktophelper)
        }
        catch {
            write-error "There was a problem changing the desktop count: $_"
            exit
        }
    }
    else{
        try {
            # First define the Service we need
            [VMware.Hv.DesktopService]$desktopservice=new-object vmware.hv.DesktopService
            # Fill the helper for this service with the application information
            $desktophelper=$desktopservice.read($HVConnectionServer.extensionData, $HVPoolID)
            # Change the state of the application in the helper
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setminNumberOfMachines($minNumberOfMachines)
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setMaxNumberOfMachines($maxNumberOfMachines)
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setnumberOfSpareMachines($numberOfSpareMachines)
            $desktophelper.getAutomatedDesktopDataHelper().getVmNamingSettingsHelper().getPatternNamingSettingsHelper().setProvisioningTime("ON_DEMAND")
            # Apply the helper to the actual object
            $desktopservice.update($HVConnectionServer.extensionData, $desktophelper)
        }
        catch {
            write-error "There was a problem changing the desktop count: $_"
            exit
        }
    }
}

write-verbose "Script will change this Desktop Pool: $HVDesktopPoolName"
write-verbose "Script will connect to this Connection Server: $HVConnectionServerFQDN "
if($Provisioningtype){
    write-verbose "Provisioningtype was set to $Provisioningtype"
}
else{
    write-verbose "No ProvisioningType was provided"
}

write-verbose "ChangeProvisioningtype was set to $ChangeProvisioningtype"
write-verbose "New Maximum Desktop Count is $maxNumberOfMachines "
if($minNumberOfMachines){
    write-verbose "minNumberOfMachines was set to $minNumberOfMachines"
}
else{
    write-verbose "No minNumberOfMachines was provided"
}

if($numberOfSpareMachines){
    write-verbose "numberOfSpareMachines was set to $numberOfSpareMachines"
}
else{
    write-verbose "No numberOfSpareMachines was provided"
}

if($Credentials){
    $creds = $credentials
}
else{
    $creds = get-credential
}



# Connect to the Horizon View Connection Server

[VMware.VimAutomation.HorizonView.Impl.V1.ViewObjectImpl]$objHVConnectionServer = Connect-HorizonConnectionServer -HVConnectionServerFQDN $HVConnectionServerFQDN -Credential $creds

# Retreive the desktop pool
$HVPool=Get-HVDesktopPool -HVPoolName $HVDesktopPoolname -HVConnectionServer $objHVConnectionServer
write-verbose  "Retreived information about $HVDesktopPoolname"

# But we only need the ID
$HVPoolID=($HVPool).id

# Retreive the pool spec
$hvpoolspec=Get-HVPoolSpec -HVConnectionServer $objHVConnectionServer -HVPoolID $HVPoolID
$ProvisioningTime=($hvpoolspec).AutomatedDesktopData.VmNamingSettings.PatternNamingSettings.ProvisioningTime
write-verbose "Current provisioningtype = $ProvisioningTime"
write-verbose "Checking if provisioningtype matches the current setting and if I am allowed to change it."
if($Provisioningtype){
    if($ProvisioningTime -ne $provisioningtype -and $changeprovisioningtype -eq $False){
        write-error "Provisioningtype of $provisioningtype does not match the current provisioningtype. Set changeprovisioningtype to True to change the provisioningtype"
        exit
    }
    elseif($ProvisioningTime -ne $provisioningtype -and $changeprovisioningtype -eq $true){
        $Provisioningtype=$Provisioningtype.toupper()
        write-verbose "Changing Provisioningtype to $Provisioningtype"
    }
}
else{
    $Provisioningtype = $ProvisioningTime
}

if($Provisioningtype -eq "ON_DEMAND"){
    write-verbose "Checking if numberOfSpareMachines or minNumberOfMachines is missing"
    if(!$minNumberOfMachines -or !$numberOfSpareMachines){
        write-error "numberOfSpareMachines and minNumberOfMachines are required when using provisioningtype: $provisioningtype"
        exit
    }
}

# We cannot change manual pools so we give a warning about this and exit the script.
if ($hvpoolspec.Type -eq "MANUAL"){
    write-error "Could not execute, this a manual Horizon View Desktop Pool, cannot change the amount of desktops."
    exit
}

# When not all vm's are provisioned up front the max amount of machines can't be lower that the minimum amount or the number of spare machines.
if ($Provisioningtype -eq "ON_DEMAND"){
    if ($numberOfSpareMachines -ge $maxNumberOfMachines -or $minNumberOfMachines -ge $maxNumberOfMachines){
        write-error "Could not execute, the number of desktops cannot be smaller than the minimum amount of desktops or the number of spare desktops"
        exit
    }
}

# Change the desktop count in the pool

if($Provisioningtype -eq "UP_FRONT"){
    write-verbose "Provisioningtype is $Provisioningtype so ignoring minNumberOfMachines and numberOfSpareMachines if they have been added."
    write-verbose  "Trying to change $HVDesktopPoolname to $maxNumberOfMachines desktops."
    Set-HVPool -HVConnectionServer $objHVConnectionServer -HVPoolID $HVPoolID -maxNumberOfMachines $maxNumberOfMachines -Provisioningtype $Provisioningtype
    write-output  "Changed $HVDesktopPoolname to $maxNumberOfMachines desktops all provisioned up front."
}
else{
    write-verbose "Provisioningtype is $Provisioningtype so using minNumberOfMachines and numberOfSpareMachines."
    write-verbose  "Trying to change $HVDesktopPoolname to $maxNumberOfMachines desktops with a minimum of $minNumberOfMachines machines and $numberOfSpareMachines spares."
    Set-HVPool -HVConnectionServer $objHVConnectionServer -HVPoolID $HVPoolID -maxNumberOfMachines $maxNumberOfMachines -Provisioningtype $Provisioningtype -minNumberOfMachines $minNumberOfMachines -numberOfSpareMachines $numberOfSpareMachines
    write-output  "Changed $HVDesktopPoolname to $maxNumberOfMachines desktops with a minimum of $minNumberOfMachines machines and $numberOfSpareMachines spares."
}

# Disconnect from the connection server
Disconnect-HorizonConnectionServer -HVConnectionServer $objHVConnectionServer

 

Sending messages to Horizon Sessions using Powershell & REST api’s

Today I got the question from someone that they where trying to send messages to users but had issues with adding the session id’s to the json. I decided to make a quick and nice script that’s able to send a message to all sessions. This uses the standard functions that I always use, the standard Get-HorizonRestData function that I created in this blog post and two custom functions that I created for this script.

The first new function is get-horizonsessions this function gets all local sessions utilizing the get-horizonrestdata function. As you can see pretty basic nothing fancy about it.

function get-horizonsessions(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken
    )

    try{
        Get-HorizonRestData -ServerURL $url -RestMethod "/inventory/v1/sessions/" -accessToken $accessToken
    }
    catch{
        throw $_
    }
    return $results
}

The second one isn’t that more advanced besides that it has a few more parameters including one called $session_ids that requires an array of the session id’s to where you want to send a message. It creates an ordered hashtable that stores the message, message_type and the array of id’s. This hashtable is than converted to a json file and used as the body for the rest call.

function send-horizonmessage(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [array] $Session_Ids,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $Message,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [validateset("ERROR","WARNING","INFO", IgnoreCase = $false)]
        [string] $Message_Type
    )
    $jsonhashtable = [ordered]@{}
    $jsonhashtable.add('message',$message)
    $jsonhashtable.add('message_type',$Message_Type)
    $jsonhashtable.add('session_ids',$Session_Ids)

    $json = $jsonhashtable | convertto-json
    try{
        $results = Invoke-RestMethod -Method Post -uri "$ServerURL/rest/inventory/v1/sessions/action/send-message" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken) -body $json
    }
    catch{
        throw $_
    }
    return $results
}

Than I use these 2 lines to actually send the message (after authenticating first, duh). If you want to filter the sessions that can be added to the get-horizonsessions or manually do it on the $sessions array. Be aware that there are no names in this array so you need to gather any of the optional id’s first.

$sessions = get-horizonsessions -accessToken $accessToken -ServerURL $url

send-horizonmessage -accessToken $accessToken -ServerURL $url -Message_Type $Message_Type -message $message -Session_Ids ($sessions).id

And this is how you run the entire script.

.\Horizon_send_messages.ps1 -Credential $credential -ConnectionServerFQDN pod1cbr1.loft.lab -Message "retouw.nl test message" -Message_Type ERROR

And the entire script that’s also available at my Various_Scripts/Horizon_send_messages.ps1 at master · Magneet/Various_Scripts (github.com).

<#
    .SYNOPSIS
    Send a message to all user sessions

    .DESCRIPTION
    This script uses the Horizon rest api's to all sessions in a horizon pod

    .EXAMPLE
    .\find_user_assigned_desktops.ps1 -Credential $creds -ConnectionServerFQDN pod2cbr1.loft.lab -message "test message" -message_type "ERROR"

    .PARAMETER Credential
    Mandatory: No
    Type: PSCredential
    Object with credentials for the connection server with domain\username and password

    .PARAMETER ConnectionServerFQDN
    Mandatory: Yes
    Default: String
    FQDN of the connection server to connect to

    .PARAMETER message
    Mandatory: Yes
    Message to send to the users

    .PARAMETER message_type
    Mandatory: Yes
    Message type: INFO, ERROR or WARNING

    .NOTES
    Created by: Wouter Kursten
    First version: 23-12-2021

    .COMPONENT
    Powershell Core

#>

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

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

    [Parameter(Mandatory=$true,
    HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
    [string] $Message,

    [Parameter(Mandatory=$true,
    HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
    [validateset("ERROR","WARNING","INFO", IgnoreCase = $false)]
    [string] $Message_Type
)

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 "$url/rest/login" -ContentType "application/json" -Body ($Credentials | ConvertTo-Json)
}

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

function Get-HorizonRestData(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        ParameterSetName="filteringandpagination",
        HelpMessage='Array of ordered hashtables' )]
        [array] $filters,

        [Parameter(Mandatory=$true,
        ParameterSetName="filteringandpagination",
        HelpMessage='Type of filter Options: And, Or' )]
        [ValidateSet('And','Or')]
        [string] $Filtertype,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='Page size, default = 500' )]
        [int] $pagesize = 500,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [string] $RestMethod,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken,

        [Parameter(Mandatory=$false,
        ParameterSetName="filteringandpagination",
        HelpMessage='$True for rest methods that contain pagination and filtering, default = False' )]
        [switch] $filteringandpagination,

        [Parameter(Mandatory=$false,
        ParameterSetName="id",
        HelpMessage='To be used with single id based queries like /monitor/v1/connection-servers/{id}' )]
        [string] $id
    )
    if($filteringandpagination){
        if ($filters){
            $filterhashtable = [ordered]@{}
            $filterhashtable.add('type',$filtertype)
            $filterhashtable.filters = @()
            foreach($filter in $filters){
                $filterhashtable.filters+=$filter
            }
            $filterflat=$filterhashtable | convertto-json -Compress
            $urlstart= $ServerURL+"/rest/"+$RestMethod+"?filter="+$filterflat+"&page="
        }
        else{
            $urlstart= $ServerURL+"/rest/"+$RestMethod+"?page="
        }
        $results = [System.Collections.ArrayList]@()
        $page = 1
        $uri = $urlstart+$page+"&size=$pagesize"
        $response = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
        $response.foreach({$results.add($_)}) | out-null
        if ($responseheader.HAS_MORE_RECORDS -contains "TRUE") {
            do {
                $page++
                $uri = $urlstart+$page+"&size=$pagesize"
                $response = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
                $response.foreach({$results.add($_)}) | out-null
            } until ($responseheader.HAS_MORE_RECORDS -notcontains "TRUE")
        }
    }
    elseif($id){
        $uri= $ServerURL+"/rest/"+$RestMethod+"/"+$id
        $results = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
    }
    else{
        $uri= $ServerURL+"/rest/"+$RestMethod
        $results = Invoke-RestMethod $uri -Method 'GET' -Headers (Get-HRHeader -accessToken $accessToken) -ResponseHeadersVariable responseheader
    }

    return $results
}

function get-horizonsessions(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken
    )

    try{
        Get-HorizonRestData -ServerURL $url -RestMethod "/inventory/v1/sessions/" -accessToken $accessToken
    }
    catch{
        throw $_
    }
    return $results
}

function send-horizonmessage(){
    [CmdletBinding(DefaultParametersetName='None')] 
    param(
        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $ServerURL,

        [Parameter(Mandatory=$true,
        HelpMessage='Part after the url in the swagger UI i.e. /external/v1/ad-users-or-groups' )]
        [PSCustomObject] $accessToken,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [array] $Session_Ids,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [string] $Message,

        [Parameter(Mandatory=$true,
        HelpMessage='url to the server i.e. https://pod1cbr1.loft.lab' )]
        [validateset("ERROR","WARNING","INFO", IgnoreCase = $false)]
        [string] $Message_Type
    )
    $jsonhashtable = [ordered]@{}
    $jsonhashtable.add('message',$message)
    $jsonhashtable.add('message_type',$Message_Type)
    $jsonhashtable.add('session_ids',$Session_Ids)

    $json = $jsonhashtable | convertto-json
    try{
        $results = Invoke-RestMethod -Method Post -uri "$ServerURL/rest/inventory/v1/sessions/action/send-message" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken) -body $json
    }
    catch{
        throw $_
    }
    return $results
}

if($Credential){
    $creds = $credential
}
else{
    $creds = get-credential
}

$ErrorActionPreference = 'Stop'

$username=($creds.username).split("\")[1]
$domain=($creds.username).split("\")[0]
$password=$creds.password

$url = "https://$ConnectionServerFQDN"

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

$accessToken = Open-HRConnection -username $username -password $UnsecurePassword -domain $Domain -url $url

$sessions = get-horizonsessions -accessToken $accessToken -ServerURL $url

send-horizonmessage -accessToken $accessToken -ServerURL $url -Message_Type $Message_Type -message $message -Session_Ids ($sessions).id