Skip to content

Instantly share code, notes, and snippets.

@instance-id
Created August 2, 2022 22:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save instance-id/b6cf748b01dac369b2c71049a701ac5f to your computer and use it in GitHub Desktop.
Save instance-id/b6cf748b01dac369b2c71049a701ac5f to your computer and use it in GitHub Desktop.
Azure Horizon Cloud API
<#
.NOTES
============================================================
Version: v0.1.00
Created: 08/02/2022
Last Edit: 08/02/2022
Platform: Windows
Filename: horizon_api.ps1
PSVersion: 7.2.5
============================================================
.DESCRIPTION
Azure/VMWare Horizon Cloud API Information Gathering
#>
# --| Static Import / Type Definition ---------------------
# --| -----------------------------------------------------
# -- https://stackoverflow.com/questions/41897114/unexpected-error-occurred-running-a-simple-unauthorized-rest-query?rq=1
Add-Type -TypeDefinition @'
public class SSLHandler
{
public static System.Net.Security.RemoteCertificateValidationCallback GetSSLHandler()
{
return new System.Net.Security.RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; });
}
}
'@
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = [SSLHandler]::GetSSLHandler()
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [System.Net.SecurityProtocolType]::Tls12
# --| Path Variables --------------------------------------
# --| -----------------------------------------------------
$currentPath = $PSScriptRoot
$poolDataOutput = [system.io.path]::combine($currentPath, 'output', 'pooldata.csv')
$sessionsOutput = [system.io.path]::combine($currentPath, 'output', 'sessions.csv')
$desktopsOutput = [system.io.path]::combine($currentPath, 'output', 'desktops.csv')
$appsDataOutput = [system.io.path]::combine($currentPath, 'output', 'appsdata.csv')
$utilDataOutput = [system.io.path]::combine($currentPath, 'output', 'utildata.csv')
$logPath = [system.io.path]::combine($currentPath, 'logger.ps1')
$domainCredPath = [system.io.path]::combine($credPath, 'credentials', 'domain.crypt')
$vmwareCredPath = [system.io.path]::combine($credPath, 'credentials', 'vmware.crypt')
$loadCredPath = [system.io.path]::combine($credPath, 'credentials', 'load_credentials.ps1')
if (!(test-path $domainCredPath) -or !(test-path $vmwareCredPath)) {
Write-Host 'Credentials not found. Please run the setup script.'
exit 1
}
. $logPath
. $loadCredPath
function Log($message, $severity = 'INFO', $logSuffix = 'horizon_api') {
LogMessage "${duaNumber}" "${message}" "$severity" "${logSuffix}"
}
$domainCredentials = GetCredentialsFromFile $domainCredPath
$vmwareCredentials = GetCredentialsFromFile $vmwareCredPath
# --| Helper Functions ------------------------------------
# --| -----------------------------------------------------
function BuildURI([string]$apiPath) {
return "${baseURL}${apiPath}"
}
# Convert Unix time to datetime
function ConvertTime($unixTime) {
return (([System.DateTimeOffset]::FromUnixTimeSeconds($($unixTime / 1000))).DateTime).ToString()
}
# --| Function Variables ----------------------------------
# --| -----------------------------------------------------
$poolDataList = [System.Collections.ArrayList]@()
$sessionsList = [System.Collections.ArrayList]@()
$appsDataList = [System.Collections.ArrayList]@()
$desktopsList = [System.Collections.ArrayList]@()
$utilDataList = [System.Collections.ArrayList]@()
# --| Pool Data ---------------------------------
$poolDataPropertyList = 'name', 'hydraNodeName', 'requestedSize', 'actualSize', 'dateCreated', 'poolOnline', `
'deleteQuota', 'defaultPoolProtocol', 'OsDiskConfiguration', 'encrypted', 'highlyAvailable', 'internal', `
'deleteProtected', 'lastUpdated', 'preferredClientType', 'poolSizeType', 'poolSessionType', 'supportedPoolProtocols'
$poolDataProperties = @{Property = $poolDataPropertyList }
# --| Session Data ------------------------------
$sessionPropertyList = 'poolName', 'userName', 'clientBuild', 'connectionType', 'domain', 'duration', 'lastActiveTime', `
'loginStatus', 'loginTime', 'userAgentOrHostOsName', 'userId', 'vmName'
$sessionProperties = @{Property = $sessionPropertyList }
# --| Application Data --------------------------
$appExclusion = 'explorer', 'svchost', 'dllhost', 'smss', 'csrss', 'wininit', 'winlogon', 'WUDFHost', 'lsass', 'dwm'
$appPropertyList = 'poolName', 'name', 'cpuPercentage', 'createdTimestampInMS', 'diskIOBytes', 'memoryBytes', 'processId', `
'userId', 'userName', 'vmName'
$appProperties = @{Property = $appPropertyList }
# --| Desktop Data ------------------------------
$desktopPropertyList = 'poolName', 'name', 'dnsName', 'ipAddress', 'guestOS', 'desktopManagerName', 'numCPUs', 'poolId', `
'macAddress', 'vmModel', 'diskSize', 'diskType', 'sessionAllocationState', 'powerOnDate', 'vmpowerState', 'state', 'vmId', `
'daaSAgentVersion', 'vmlifeState', 'vmwareToolsState', 'agentErrorCode', 'memorySizeMB', 'hydraNodeId', 'viewAgentVersion'
$desktopProperties = @{Property = $desktopPropertyList }
# --| Utilization Data --------------------------
$utilizationPropertyList = 'hydraNodeName', 'maxPossibleDesktopSessions', 'usedDesktopSessions', 'maxPossibleApplicationSessions', `
'usedApplicationSessions', 'hydraNodeId'
$utilizationProperties = @{Property = $utilizationPropertyList }
# --| Authentication Variables ----------------------------
# --| -----------------------------------------------------
[string]$base = 'cloud'
[string]$baseURL = "https://$base.horizon.vmware.com"
[string]$loginURL = '/api/login/login'
$sessionVariable = $null
[hashtable]$authParams = @{
username = $vmwareCredentials.username
password = $vmwareCredentials.GetNetworkCredential().password.ToCharArray()
}
[hashtable]$domainParams = @{
username = $domainCredentials.username
password = $domainCredentials.GetNetworkCredential().password.ToCharArray()
}
[hashtable]$restParams = @{
'ContentType' = 'application/json'
'Method' = 'Post'
}
$restParams.Body = ($authParams | ConvertTo-Json).ToString()
$restParams.Uri = BuildURI $loginURL
# --| VMWare Cloud Login ------------------------
try {
$response = Invoke-RestMethod @restParams -SessionVariable sessionVariable
} catch {
$response = $null
$errorMessage = "Login failed: $($restParams.uri) as $($vmwareCredentials.username) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
if ( ! $response -or ! $response.PSObject.Properties[ 'authSession' ] ) {
$errorMessage = "Did not receive auth session token from $($restParams.uri) as $($vmwareCredentials.username)"
Log $errorMessage 'ERROR'
throw $errorMessage
}
$restParams.websession = $sessionVariable
[string]$domain = ($domainCredentials.username -split '\\')[0]
if ( $null -eq $response.PSObject.Properties[ 'credentialRequested' ] ) {
Log 'No credentialRequested returned from initial logon' 'ERROR'
}
if ( $domain -notin $response.domainNames ) {
Log "Domain name `"$domain`" in AD credential not in list of domains returned from Horizon - $($response.domainNames -join ',')" 'ERROR'
}
# --| Domain Login ------------------------------
$authParams.domain = $domain
$authParams.username = ($domainParams.username -split '\\')[-1]
$authParams.password = $domainParams.password
$authParams.credentialRequested = $response.credentialRequested
$authParams.authSession = $response.authSession
$restParams.Body = ($authParams | ConvertTo-Json).ToString()
try {
$authentication = Invoke-RestMethod @restParams
} catch {
$authentication = $null
$errorMessage = "Domain login failed: $($restParams.uri) as $($domainCredential.UserName) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
if ( ! $authentication -or $null -eq $authentication.PSObject.Properties['apiToken']) {
$errorMessage = 'No apiToken returned from domain login'
Log $errorMessage 'ERROR'
throw $errorMessage
}
$restParams.Headers = @{
'Authorization' = "Bearer $($authentication.apiToken)"
'Accept' = 'application/json'
'Content-Type' = 'application/json'
}
# --| Pool Data -------------------------------------------
# --| -----------------------------------------------------
function GetPoolData() {
$poolURI = '/dt-rest/v100/pool/manager/pools'
$restParams.Remove( 'Body' )
$restParams.Method = 'GET'
$restParams.uri = BuildURI $poolURI
try {
$poolData = Invoke-RestMethod @restParams
} catch {
$poolData = $null
$errorMessage = "Failed to get pool data from $($restParams.uri) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
if ( ($null -eq $poolData) -or ($poolData.Count -eq 0) ) {
$errorMessage = 'No pools returned from pool data'
Log $errorMessage 'ERROR'
throw $errorMessage
}
foreach ($p in $poolData) {
$properties = $p | Select-Object @poolDataProperties
[void]$poolDataList.Add($properties)
}
return $poolData
}
# --| Session Data ----------------------------------------
# --| -----------------------------------------------------
function GetSessionData([string]$poolId, [string]$poolName) {
[hashtable]$poolIdData = @{
'poolId' = $poolId
}
$sessionURI = '/dt-rest/v100/session/manager/session/active'
$restParams.Method = 'POST'
$restParams.uri = BuildURI $sessionURI
$restParams.Body = ($poolIdData | ConvertTo-Json).ToString()
try {
$sessionData = Invoke-RestMethod @restParams
} catch {
$sessionData = $null
$errorMessage = "Failed to get session data from $($restParams.uri) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
foreach ($session in $sessionData) {
$session | Add-Member -MemberType NoteProperty -Name poolName -Value $poolName
$sessions = $session | Select-Object @sessionProperties
[void]$sessionsList.Add($sessions)
$sessionId = $session.id
$appURI = "/dt-rest/v100/session/active/${sessionId}/processes"
$restParams.Remove( 'Body' )
$restParams.Method = 'GET'
$restParams.uri = BuildURI $appURI
try {
$appData = Invoke-RestMethod @restParams
} catch {
$appData = $null
$errorMessage = "Failed to get app data from $($restParams.uri) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
if ( ($null -eq $appData) -or ($appData.Count -eq 0) ) {
$errorMessage = 'No pools returned from pool data'
Log $errorMessage 'ERROR'
throw $errorMessage
}
foreach ($app in $appData) {
$appName = $app.name.Replace('.exe', '')
if ($appExclusion.Contains($appName)) { continue }
$app | Add-Member -MemberType NoteProperty -Name poolName -Value $poolName
$app = $app | Select-Object @appProperties
[void]$appsDataList.Add($app)
}
}
}
# --| Desktop Data ----------------------------------------
# --| -----------------------------------------------------
function GetDesktopData([string]$poolId, [string]$poolName) {
$desktopURI = "/dt-rest/v100/infrastructure/pool/desktop/${poolId}/vms"
$restParams.Remove('Body')
$restParams.Method = 'GET'
$restParams.uri = BuildURI $desktopURI
try {
$desktopData = Invoke-RestMethod @restParams
} catch {
$desktopData = $null
$errorMessage = "Failed to get desktop data from $($restParams.uri) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
foreach ($desktop in $desktopData) {
$desktop | Add-Member -MemberType NoteProperty -Name pool -Value $poolName
$desktop = $desktop | Select-Object @desktopProperties
[void]$desktopsList.Add($desktop)
}
}
# --| Utilization Data ------------------------------------
# --| -----------------------------------------------------
function GetUtilization() {
$utilizationURI = '/dt-rest/v100/session/manager/session/utilization'
$restParams.Remove( 'Body' )
$restParams.Method = 'GET'
$restParams.uri = BuildURI $utilizationURI
try {
$utilData = Invoke-RestMethod @restParams
} catch {
$utilData = $null
$errorMessage = "Failed to get utilization data from $($restParams.uri) : $_"
Log $errorMessage 'ERROR'
throw $errorMessage
}
if ( ($null -eq $utilData) -or ($utilData.Count -eq 0) ) {
$errorMessage = 'No utilization reporting data returned'
Log $errorMessage 'ERROR'
throw $errorMessage
}
foreach ($util in $utilData) {
$util = $util | Select-Object @utilizationProperties
[void]$utilDataList.Add($util)
}
}
# --| Primary Execution -----------------------------------
# --| -----------------------------------------------------
$pools = GetPoolData
foreach ($pool in $pools) {
GetSessionData $pool.id $pool.name
GetDesktopData $pool.id $pool.name
}
GetUtilization
# --| Output ------------------------------------
# --| Pool ---------------------------------
if ($poolDataList.Count -gt 0) {
'' > $poolDataOutput
foreach ($poolData in $poolDataList) {
$poolData.dateCreated = ConvertTime $poolData.dateCreated
$poolData.lastUpdated = ConvertTime $poolData.lastUpdated
$protocols = [system.String]::Join(",", $poolData.supportedPoolProtocols)
$poolData.supportedPoolProtocols = $protocols
}
$csvData = $poolDataList | ConvertTo-Csv
$csvData | Out-File $poolDataOutput -Force
}
# --| Session ------------------------------
if ($sessionsList.Count -gt 0) {
'' > $sessionsOutput
foreach ($session in $sessionsList) {
$session.lastActiveTime = ConvertTime $session.lastActiveTime
$session.loginTime = ConvertTime $session.loginTime
}
$csvData = $sessionsList | ConvertTo-Csv
$csvData | Out-File $sessionsOutput -Force
}
# --| Desktop ------------------------------
if ($desktopsList.Count -gt 0) {
'' > $desktopsOutput
foreach ($desktop in $desktopsList) {
$desktop.powerOnDate = ConvertTime $desktop.powerOnDate
}
$csvData = $desktopsList | ConvertTo-Csv
$csvData | Out-File $desktopsOutput -Force
}
# --| Applications -------------------------
if ($appsDataList.Count -gt 0) {
'' > $appsDataOutput
foreach ($app in $appsDataList) {
$app.createdTimestampInMS = ConvertTime $app.createdTimestampInMS
}
$csvData = $appsDataList | ConvertTo-Csv
$csvData | Out-File $appsDataOutput -Force
}
# --| Utilization --------------------------
if ($utilDataList.Count -gt 0) {
'' > $utilDataOutput
$csvData = $utilDataList | ConvertTo-Csv
$csvData | Out-File $utilDataOutput -Force
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment