So last year I shared a script to send messages to local sessions in a pod regardless of Cloud Pod Architecture being used or not. With Horizon 8 2111 one of the new features is that you can also send messages to Global Sessions in other pods. I have created a script that messages to all those users. I used the same script as base but had to update Get-HorizonRestData to reflect the fact that sometimes some things need to be added to the url like ?pod_id=$podid in this case:
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,
[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"
write-host $uri
$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
}
I also had to change the get-horizonsessions to get-horizonglobalsessions
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
}
And a new function get-pods
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
}
while function send-horizongmessage was upgraded to send-horizonglobalmessage
function send-horizonglobalmessage(){
[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,
[Parameter(Mandatory=$true,
HelpMessage='Id of the Local pod to query' )]
[string] $podid
)
$jsonhashtable = [ordered]@{}
$jsonhashtable.global_session_action_specs=@()
$sessiondetailshashtable = [ordered]@{}
$sessiondetailshashtable.ids=$Session_Ids
$sessiondetailshashtable.pod_id=$podid
$jsonhashtable.global_session_action_specs+=$sessiondetailshashtable
$jsonhashtable.message = $message
$jsonhashtable.message_type = $Message_Type
$json = $jsonhashtable | convertto-json -depth 100
try{
$results = Invoke-RestMethod -Method Post -uri "$ServerURL/rest/inventory/v1/global-sessions/action/send-message" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken) -body $json
}
catch{
throw $_
}
return $results
}
and some other of the runtime pieces also changed
$pods=get-pods -accessToken $accessToken -ServerURL $url
foreach($pod in $pods){
$podid=$pod.id
$sessions = get-horizonglobalsessions -accessToken $accessToken -ServerURL $url -podid $podid
send-horizonglobalmessage -accessToken $accessToken -ServerURL $url -Message_Type $Message_Type -message $message -Session_Ids ($sessions).id -podid $podid
}
And this is how you run it with the output in the vdi desktops
D:\GIT\Various_Scripts\Horizon_send_Global_messages.ps1 -Credential $creds -ConnectionServerFQDN pod1cbr1.loft.lab -message "test vExpert test" -Message_Type ERROR
This is the complete script but it can also be found on Github
<#
.SYNOPSIS
Send a message to all global sessions
.DESCRIPTION
This script uses the Horizon rest api's to all global sessions in a horizon cloud pod
.EXAMPLE
.\Horizon_send_Global_messages.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: 17-03-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=$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,
[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"
write-host $uri
$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
}
function send-horizonglobalmessage(){
[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,
[Parameter(Mandatory=$true,
HelpMessage='Id of the Local pod to query' )]
[string] $podid
)
$jsonhashtable = [ordered]@{}
$jsonhashtable.global_session_action_specs=@()
$sessiondetailshashtable = [ordered]@{}
$sessiondetailshashtable.ids=$Session_Ids
$sessiondetailshashtable.pod_id=$podid
$jsonhashtable.global_session_action_specs+=$sessiondetailshashtable
$jsonhashtable.message = $message
$jsonhashtable.message_type = $Message_Type
$json = $jsonhashtable | convertto-json -depth 100
try{
$results = Invoke-RestMethod -Method Post -uri "$ServerURL/rest/inventory/v1/global-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
$pods=get-pods -accessToken $accessToken -ServerURL $url
foreach($pod in $pods){
$podid=$pod.id
$sessions = get-horizonglobalsessions -accessToken $accessToken -ServerURL $url -podid $podid
send-horizonglobalmessage -accessToken $accessToken -ServerURL $url -Message_Type $Message_Type -message $message -Session_Ids ($sessions).id -podid $podid
}