vOperator.psm1

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

############## Obsolete functions, will be replaced with new functions later on
Function Convert-hvAPIFilter {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, HelpMessage = "Your accesstoken to Horizon Connetion Server")]
        [ValidateNotNullOrEmpty()]
        [securestring]$accessToken,
        [Parameter(Mandatory = $true, HelpMessage = "Horizon Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, HelpMessage = "Enter the restmethod you want to use for example /external/v1/audit-events")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ $_ -like "/*" })]
        [String]$restMethod,
        [Parameter(Mandatory = $false, HelpMessage = "Used for URI query for example some rest method are /inventory/v1/desktop-pools/{id} then you will enter the /ID here")]
        #[ValidateScript({ $_ -like "/*" })]
        [String]$ID,
        [Parameter(Mandatory = $false, HelpMessage = "Used when some restmethods adds extra url at the end for example of the URI string /inventory/v2/desktop-pools/{id}/action/schedule-push-image")]
        [ValidateScript({ $_ -like "/*" })]
        [String]$urlExtra,
        [Parameter(Mandatory = $false, HelpMessage = "Pass your filter or filters here")]
        [Array]$Filter,
        [Parameter(Mandatory = $false, HelpMessage = "Enter filter type, for example And. This is case sensitiv")]
        [ValidateSet("Equals", "NotEquals", "Contains", "StartsWith", "Between", "Not", "And", "Or")]
        [String]$FilterType,
        [Parameter(Mandatory = $false, HelpMessage = "How you want to sort something either ASC or DESC (Case sensitiv), SortBy must be used combined with this")]
        [ValidateSet("ASC", "DESC")]
        [String]$OrderBy = "ASC",
        [Parameter(Mandatory = $false, HelpMessage = "Sort by property in return data from the REST API call")]
        [String]$SortBy,
        [Parameter(Mandatory = $false, HelpMessage = "How many result you want to show on each page, default 500")]
        [int]$PageSize = 500,
        [Parameter(Mandatory = $false, HelpMessage = "Use this if you want to use pagination")]
        [Switch]$Pagination = $false,
        [Parameter(Mandatory = $false, HelpMessage = "What type of filter you want to use")]
        [ValidateSet("ID", "IDFilter", "Filter", "uriFilter")]
        [String]$Type,
        [Parameter(Mandatory = $false, HelpMessage = "If you want to use filter in the URI example https://FQDN/rest/inventory/v1/global-sessions?user_id")]
        [ValidateScript({ $_ -notlike "*=*" })]
        [String]$uriFilterName,
        [Parameter(Mandatory = $false, HelpMessage = "If you want to use filter in the URI, enter value here example https://FQDN/rest/inventory/v1/global-sessions?user_id=YOURVALUE")]
        [String]$uriFilterValue
    )

    try {
        # Importing translation of the returncodes from the rest API
        #$ReturnCodeTranslation = (Import-tSetting -Category "Horizon" -Setting "API_ReturnCode" -All).ReturnValue

        #Building first state or URI for rest
        $firstURI = "https://$hvURI/rest" + $restMethod

        # If SortBy are populated it will add it to the URI you also need to use order_by. This should be last in the URI
        if (-Not([string]::IsNullOrEmpty($SortBy))) {
            $SortOrder = "&sort_by=$SortBy&order_by=$OrderBy"
        }

        # If filter type is used then it will format URI to work with filter
        if (-Not([string]::IsNullOrEmpty($FilterType))) {
            # Adding filter to API Call
            $FilterHashtable = [ordered]@{
                'type'    = $FilterType
                'filters' = [array]$Filter
            }
        
            # Making sure that the depth of the filter is correct
            $FilterJson = $FilterHashtable | ConvertTo-Json -Compress -Depth 5 -WarningVariable FilterJsonWarning
        
            # Making sure that the JSON depth are right, if not it will increase the depth to be correct
            if ($FilterJsonWarning) {
                $Depth = 6
                while ($FilterJsonWarning) {
                    $Depth++
                    $FilterJson = $FilterHashtable | ConvertTo-Json -Depth $Depth -Compress -WarningVariable FilterJsonWarning
                }
            }
            
            # Building filter string to URI
            $uriFilter = "filter=" + $FilterJson
        }
        # If ID is used it will format URI to work with ID
        elseif ($Type -eq "ID") {
            # If somethings is in $ID it will convert the secondURI in the correct way
            $secondURI = $firstURI + $ID + "?"
        }
        elseif ($Type -eq "uriFilter") {
            $secondURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue
        }
        else {
            # If ID is empty this will be the secondURI convertation
            $secondURI = $firstURI + "?"
        }

        if ($Pagination -eq $true) {
            Switch ($Type) {
                uriFilter {
                    $finalURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + "page="
                }
                Filter {
                    $finalURI = $firstURI + "?" + $uriFilter + "&" + "page="
                }
                IDFilter {
                    $finalURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + "page="
                }
                ID {
                    $finalURI = $secondURI + "page="
                }
                default {
                    $finalURI = $secondURI + "page="
                }
            }

            # Looking how many pages it should be and making sure that it search trough each page
            $Page = 0
            do {
                $Page++
                if ($Type -eq "IDFilter") {
                    $pageURI = $finalURI + $Page + "&size=$PageSize" + "&" + $uriFilter
                }
                else {
                    $pageURI = $finalURI + $Page + "&size=$PageSize" + $SortOrder
                }
                $APICall = Invoke-RestMethod -Uri $pageURI -Method Get -ContentType "application/json" -Authentication Bearer -Token $accessToken -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "responsHeader" -HttpVersion 3.0
            } while ($responsHeader.HAS_MORE_RECORDS -eq $true)
        }
        else {
            $finalURI = $secondURI + $urlExtra + $SortOrder
            $APICall = Invoke-RestMethod -Uri $finalURI -Method Get -ContentType "application/json" -Authentication Bearer -Token $accessToken -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "responsHeader" -HttpVersion 3.0
        }

        # Retriving the results
        if ($StatusCode -eq 200 -and $null -ne $APICall) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "Success, did retrive all data from REST API Call against $finalURI" -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "$($ReturnCodeTranslation.$($StatusCode).translate), ErrorCode: $StatusCode" -ReturnValue $StatusCode
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)"
        break
    }
}
################################################################################

Function Get-ReturnMessageTemplate {
    <#
        .SYNOPSIS
        Return messages and value in the correct format

        .DESCRIPTION
        This function will return value and messages in the correct format for vOperator to use.

        .PARAMETER ReturnType

        .PARAMETER Message

        .PARAMETER ReturnValue

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, HelpMessage = "What kind of message do you want template for")]
        [ValidateSet("Error", "Success", "Warning", "Information")]
        [string]$ReturnType,
        [Parameter(Mandatory = $false, HelpMessage = "Message that you want to return")]
        $Message = "N/A",
        [Parameter(Mandatory = $false, HelpMessage = "Return value that you want to present for the user, not return message.")]
        $ReturnValue = "N/A"
    )

    Switch ($ReturnType) {
        Error {
            return [PSCustomObject]@{
                ReturnCode  = 1
                Severity    = "Error"
                Icon        = "xmark"
                IconColor   = "red"
                Color       = 'red'
                Duration    = "4000"
                Message     = $Message
                ReturnValue = $ReturnValue
            }
            Break
        }
        Success {
            return [PSCustomObject]@{
                ReturnCode  = 0
                Severity    = "Success"
                Icon        = "Check"
                IconColor   = "green"
                Color       = 'green'
                Duration    = "4000"
                Message     = $Message
                ReturnValue = $ReturnValue
            }
            Break
        }
        Warning {
            return [PSCustomObject]@{
                ReturnCode  = 2
                Severity    = "Warning"
                Icon        = "TriangleExclamation"
                IconColor   = "yellow"
                Color       = 'yellow'
                Duration    = "4000"
                Message     = $Message
                ReturnValue = $ReturnValue
            }
            Break
        }
        Information {
            return [PSCustomObject]@{
                ReturnCode  = 3
                Severity    = "Info"
                Icon        = "CircleInfo"
                IconColor   = "blue"
                Color       = 'blue'
                Duration    = "4000"
                Message     = $Message
                ReturnValue = $ReturnValue
            }
            Break
        }
    }
}
Function Disconnect-hvSrv {
    <#
        .SYNOPSIS
        Disconnect Horizon connection against the API on the Connection Server

        .DESCRIPTION

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER refreshToken

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "AccessToken to Horizon")]
        [ValidateNotNullOrEmpty()]
        [securestring]$accessToken,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Refresh token from Horizon")]
        [ValidateNotNullOrEmpty()]
        $refreshToken
    )

    try {
        $Body = [ordered]@{
            'refresh_token' = $refreshToken
        }

        $Connection = Invoke-RestMethod -Uri "https://$hvURI/rest/logout" -Method Post -Body ($Body | ConvertTo-Json) -ContentType "application/json" -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "ResponseHeaders" -HttpVersion 3.0
        return Get-ReturnMessageTemplate -ReturnType Success -Message "Disconnected from VMWare Horizon $hvURI"
        Break
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception.Message)"
        Break
    }
}
Function Get-hvVM {
    <#
        .SYNOPSIS
        This function will return either all VMs that exists in the Horizon POD or a singel machine.

        .DESCRIPTION
        If you don't use -VM "MACHINENAME" you will get all machines

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER FilterName

        .PARAMETER FilterValue

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "AccessToken to Horizon")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, HelpMessage = "Filter on")]
        [string]$FilterName = "name",
        [Parameter(Mandatory = $false, HelpMessage = "Value for filter")]
        [string]$FilterValue
    )

    try {
        if ([string]::IsNullOrEmpty($FilterValue)) {
            $APICall = Convert-hvAPIFilter -hvURI $hvURI -accessToken $accessToken -SortBy "name" -restMethod "/inventory/v5/machines" -Pagination
            $LogText = "all VMs"
        }
        else {
            [array]$filtersAPI = [ordered]@{
                'type'  = 'Equals'
                'name'  = $FilterName
                'value' = $FilterValue
            }

            $APICall = Convert-hvAPIFilter -Filter $filtersAPI -FilterType And -hvURI $hvURI -accessToken $accessToken -SortBy "name" -restMethod "/inventory/v5/machines" -Pagination -Type Filter
            $LogText = "VM $FilterName $FilterValue"
        }
        if ($APICall.ReturnCode -eq 0 -and $APICall.ReturnValue.Count -gt 0 -and $null -ne $APICall.ReturnValue.id) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "Information about $LogText have been collected." -ReturnValue $($APICall.ReturnValue)
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Information about $LogText could not be collected." -ReturnValue $($APICall.ReturnValue)
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception)"
        break
    }
}
Function Set-hvVMUserAssignment {
    <#
        .SYNOPSIS
        Assign or Unassign user to persistent VM in Horizon

        .DESCRIPTION

        .PARAMETER accessToken
        Horizon Access token

        .PARAMETER UserName
        Enter Username of the user that you want to change assignment for, if you running this script on a non Windows machine please enter SID instead of username.

        .PARAMETER hvURI
        Enter URI for the Horizon Connection Server, the hole FQDN. Don't use https://

        .PARAMETER VM
        Plain name of the VM you want to assign or unassign user on.

        .PARAMETER Action
        Enter what kind of action you want to execute. Valide inputs are "unassign", "assign"

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName, HelpMessage = "AccessToken to Horizon")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName, HelpMessage = "Connection Server FQDN, don't enter https://")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [String]$hvURI,
        [Parameter(Mandatory = $true, HelpMessage = "Name of the VM you want to get assign or unassign user against")]
        [ValidateNotNullOrEmpty()]
        [String]$VM,
        [Parameter(Mandatory = $true, HelpMessage = "Enter what you want to do here, valid input is unassign or assign")]
        [ValidateSet("unassign", "assign")]
        [String]$Action,
        [Parameter(Mandatory = $true, HelpMessage = "Enter username of the user or users you want to make changes for, if your running this script on no Windows machine please enter SID instead of username.")]
        [ValidateNotNullOrEmpty()]
        [String]$UserName
    )
    
    try {
        Switch ($Action) {
            assign {
                [string]$urlAction = "assign-users"
            }
            unassign {
                [string]$urlAction = "unassign-users"
            }
        }

        # Get VM information from Horizon
        $GetVMInfo = Get-hvVM -FilterName name -FilterValue $VM -accessToken $accessToken -hvURI $hvURI
        if ($GetVMInfo.ReturnCode -eq 0) {
            $VMInfo = $GetVMInfo.ReturnValue
            [String]$VMID = "$($VMInfo.Id)"
        }

        if ($PSVersionTable.Platform -like "Win32NT") {
            # Convert UserName to SID
            $UserSID = $(Get-ADUser -Filter "sAMAccountName -eq '$UserName'" | select-object -ExpandProperty SID).value
        }
        else {
            $UserSID = $UserName 
        }
        
        # Creating Body
        [string]$RAWBody = $UserSID
        $Body = "[$($RAWBody | ConvertTo-Json)]"
    
        $APICall = Invoke-RestMethod -Uri "https://$hvURI/rest/inventory/v1/machines/$VMID/action/$urlAction" -Method POST -Body $Body -Authentication Bearer -Token $accessToken -ContentType "application/json" -StatusCodeVariable "StatusCode" -HttpVersion 3.0
            
        if ($StatusCode -eq 200 -and $APICall.Status_Code -eq 200) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "$UserName are now $Action to $VM" -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not $Action on VM $VM for user $UserName, API Status Code: $StatusCode Call Status Code: $($APICall.Status_Code)" -ReturnValue "API Status Code: $StatusCode Call Status Code: $($APICall.Status_Code)"
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception.Message)"
        Break
    }
}
Function Get-hvDesktopPool {
    <#
        .SYNOPSIS
        This function let you get a specific Desktop Pool

        .DESCRIPTION

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER FilterValue

        .PARAMETER FilterName

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>


    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "AccessToken to Horizon")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, HelpMessage = "Name of the pool")]
        [ValidateNotNullOrEmpty()]
        [string]$FilterValue,
        [Parameter(Mandatory = $false, HelpMessage = "What you want to search for")]
        [ValidateSet("name", "display_name", "id", "global_desktop_entitlement_id")]
        [string]$FilterName = "name"
    )

    try {
        if ($ID -eq $false) {
            #/inventory/v7/desktop-pools/{id}
        }
        elseif ([string]::IsNullOrEmpty($FilterValue)) {
            $APICall = Convert-hvAPIFilter -hvURI $hvURI -accessToken $accessToken -SortBy "name" -restMethod "/inventory/v7/desktop-pools" -Pagination
        }
        else {
            [array]$filtersAPI = [ordered]@{
                'type'  = 'Equals'
                'name'  = $FilterName
                'value' = $FilterValue
            }
            $APICall = Convert-hvAPIFilter -Filter $filtersAPI -FilterType And -hvURI $hvURI -accessToken $accessToken -SortBy "name" -restMethod "/inventory/v7/desktop-pools" -Pagination -Type Filter
        }

        if ($APICall.ReturnCode -eq 0 -and $APICall.ReturnValue.Count -gt 0) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "All information about Desktop Pool $FilterValue has been collected" -ReturnValue $APICall.ReturnValue
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not find any Desktop Pool with the $FilterName $FilterValue" -ReturnValue $APICall.ReturnValue
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "Get-NewhvDesktopPool $($PSItem.Exception.Message)"
        break
    }
}
Function Set-hvVMPool {
    <#
        .SYNOPSIS

        .DESCRIPTION

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER VM

        .PARAMETER DesktopPool

        .PARAMETER Action

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $true, HelpMessage = "Name of the VM")]
        [ValidateNotNullOrEmpty()]
        [string]$VM,
        [Parameter(Mandatory = $true, HelpMessage = "Name of Horizon Pool")]
        [ValidateNotNullOrEmpty()]
        [string]$DesktopPool,
        [Parameter(Mandatory = $true, HelpMessage = "Select what you want to do, add or remove machine from pool")]
        [ValidateSet("Add", "Remove")]
        [string]$Action,
        [Parameter(Mandatory = $false, HelpMessage = "if you use this you need to enter VM ID in VM parameter and Pool ID in DesktopPool parameter")]
        [Switch]$UseID = $false
    )

    try {
        if ($UseID -eq $false) {
            # Check so the Desktop Pool exists and extract the ID
            $CheckDesktopPool = Get-hvDesktopPool -FilterValue $DesktopPool -hvURI $hvURI -accessToken $accessToken
            if ($CheckDesktopPool.ReturnCode -eq 0) {
                if ($Null -ne $CheckDesktopPool) {
                    [String]$DesktopPoolID = $($CheckDesktopPool.ReturnValue).Id
                }
                else {
                    return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not find any Desktop Pool with the name $DesktopPool"
                    Break
                }
            }
            else {
                return $CheckDesktopPool
                Break
            }

            # Collecting virutal center ID
            $GetVirtualCenter = Get-hvVirtualCenter -hvURI $hvURI -accessToken $accessToken
            if ($GetVirtualCenter.ReturnCode -eq 0) {
                [String]$vCenterID = $($GetVirtualCenter.ReturnValue.id -as [string])
            }
            else {
                return $GetVirtualCenter
                Break
            }

            # Collecting all VMs from virtual center and grabbing the correct one.
            $GetVirtualCenterVMs = Get-hvVirtualCenterVM -VM $VM -VirtualCenterID $vCenterID -hvURI $hvURI -accessToken $accessToken
            if ($GetVirtualCenterVMs.ReturnCode -eq 0) {
                [String]$VMID = $($GetVirtualCenterVMs.ReturnValue).Id
            }
            else {
                return $GetVirtualCenterVMs
                Break
            }
        }
        else {
            $DesktopPoolID = $DesktopPool
            $VMID = $VM
        }


        # Adding VM to pool
        $Body = "[$($VMID | ConvertTo-Json)]"
        if ($Action -like "Add") {
            $APICall = Invoke-RestMethod -Uri "https://$hvURI/rest/inventory/v1/desktop-pools/$DesktopPoolID/action/add-machines" -Method Post -Body $Body -ContentType "application/json" -Authentication Bearer -Token $accessToken -HttpVersion 3.0 -StatusCodeVariable "StatusCode"
        }
        else {
            $APICall = Invoke-RestMethod -Uri "https://$hvURI/rest/inventory/v1/desktop-pools/$DesktopPoolID/action/remove-machines" -Method Post -Body $Body -ContentType "application/json" -Authentication Bearer -Token $accessToken -HttpVersion 3.0 -StatusCodeVariable "StatusCode"
        }

        if ($APICall.status_code -notlike "4*") {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "VM $VM is now $($Action) to/from VMWare Horizon pool $DesktopPool" -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not $($Action) VM $VM to/from VMWare Horizon pool $DesktopPool, StatusCode: $($APICall.status_code)" -ReturnValue $APICall
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception.Message)"
        Break
    }
}
Function Convert-hvAPI {
    <#
    This function will format API url for Horizon to the proper format.
    It's for calls with two filters in the url string

    ### Type IDurlfilter ###
    With one urlfilter parameter used -ID , -urlfilter , urlvalue
    https://FQDN/rest/external/v2/base-vms/YOURID?vcenter_id=YOURVALUE

    With two urlfilter, parameter used -ID , -urlfilter , urlvalue -urlfilter2 -urlvalue2
    https://FQDN/rest/external/v2/base-vms/YOURID?vcenter_id=YOURVALUE&datacenter_id=YOURVALUE

    ### Type urlfilter ###
    With one urlfilter
    https://FQDN/rest/external/v2/base-snapshots?vcenter_id=YOURVALUE&page=1&size=10&filter=FILTER&sort_by=id&order_by=ASC

    With two urlfilter
    https://FQDN/rest/external/v2/base-snapshots?vcenter_id=YOURVALUE&base_vm_id=YOURVALUE&page=1&size=10&filter=FILTER&sort_by=id&order_by=ASC

    And if it's not with a filter
    Example command:
    Convert-hvAPI -hvURI FQDN -accessToken ACCESSTOKEN -restMethod "/external/v1/virtual-machines" -uriFilterName "vcenter_id" -uriFilterValue $VirtualCenterID -Type urlfilter -Pagination
    Example url:
    https://FQDN/rest/external/v1/virtual-machines?vcenter_id=VIRTUALCENTERID&page=1&size=10&sort_by=id&order_by=ASC
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, HelpMessage = "Your accesstoken to Horizon Connetion Server")]
        [ValidateNotNullOrEmpty()]
        [securestring]$accessToken,
        [Parameter(Mandatory = $true, HelpMessage = "Horizon Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, HelpMessage = "Enter the restmethod you want to use for example /external/v1/audit-events")]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ $_ -like "/*" })]
        [String]$restMethod,
        [Parameter(Mandatory = $false, HelpMessage = "What type of url you want to convert it to")]
        [ValidateSet("UrlFilter", "IDUrlFilter", "ID")]
        [String]$Type,
        [Parameter(Mandatory = $false, HelpMessage = "Pass your filter or filters here")]
        [Array]$Filter,
        [Parameter(Mandatory = $false, HelpMessage = "Enter filter type, for example And. This is case sensitiv")]
        [ValidateSet("Equals", "NotEquals", "Contains", "StartsWith", "Between", "Not", "And", "Or")]
        [String]$FilterType,
        [Parameter(Mandatory = $false, HelpMessage = "How you want to sort something either ASC or DESC (Case sensitiv), SortBy must be used combined with this")]
        [ValidateSet("ASC", "DESC")]
        [String]$OrderBy = "ASC",
        [Parameter(Mandatory = $false, HelpMessage = "Sort by property in return data from the REST API call")]
        [String]$SortBy,
        [Parameter(Mandatory = $false, HelpMessage = "IDUrlFilter, only used")]
        [String]$ID,
        [Parameter(Mandatory = $false, HelpMessage = "How many result you want to show on each page, default 500")]
        [int]$PageSize = 100,
        [Parameter(Mandatory = $false, HelpMessage = "Use this if you want to use pagination")]
        [Switch]$Pagination = $false,
        [Parameter(Mandatory = $false, HelpMessage = "If you want to use filter in the URI example https://FQDN/rest/inventory/v1/global-sessions?user_id it's the user_id")]
        [ValidateScript({ $_ -notlike "*=*" })]
        [String]$uriFilterName,
        [Parameter(Mandatory = $false, HelpMessage = "If you want to use filter in the URI, enter value here example https://FQDN/rest/inventory/v1/global-sessions?user_id=YOURVALUE")]
        [String]$uriFilterValue,
        [Parameter(Mandatory = $false, HelpMessage = "Second URL filter, If you want to use filter in the URI example https://FQDN/rest/inventory/v1/global-sessions?user_id it's the user_id")]
        [ValidateScript({ $_ -notlike "*=*" })]
        [String]$uriFilterName2,
        [Parameter(Mandatory = $false, HelpMessage = "Second URL filter, If you want to use filter in the URI, enter value here example https://FQDN/rest/inventory/v1/global-sessions?user_id=YOURVALUE")]
        [String]$uriFilterValue2
    )

    try {
        # Importing translation of the returncodes from the rest API
        #$ReturnCodeTranslation = (Import-tSetting -Category "Horizon" -Setting "Return_Code" -All).ReturnValue

        #Building first state or URI for rest
        $firstURI = "https://$hvURI/rest" + $restMethod

        # If SortBy are populated it will add it to the URI you also need to use order_by. This should be last in the URI
        if (-Not([string]::IsNullOrEmpty($SortBy))) {
            $SortOrder = "&sort_by=$SortBy&order_by=$OrderBy"
        }

        # If filter type is used then it will format URI to work with filter
        if (-Not([string]::IsNullOrEmpty($FilterType))) {
            # Adding filter to API Call
            $FilterHashtable = [ordered]@{
                'type'    = $FilterType
                'filters' = [array]$Filter
            }
        
            # Making sure that the depth of the filter is correct
            $FilterJson = $FilterHashtable | ConvertTo-Json -Compress -Depth 5 -WarningVariable FilterJsonWarning
        
            # Making sure that the JSON depth are right, if not it will increase the depth to be correct
            if ($FilterJsonWarning) {
                $Depth = 6
                while ($FilterJsonWarning) {
                    $Depth++
                    $FilterJson = $FilterHashtable | ConvertTo-Json -Depth $Depth -Compress -WarningVariable FilterJsonWarning
                }
            }
            
            # Building filter string to URI
            $uriFilter = "filter=" + $FilterJson
        }
        else {
            Switch ($Type) {
                urlfilter {
                    if (-Not([string]::IsNullOrEmpty($uriFilterName2))) {
                        $secondURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + $uriFilterName2 + "=" + $uriFilterValue2
                    }
                    else {
                        $secondURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue
                    }
                }
                IDUrlFilter {
                    if (-Not([string]::IsNullOrEmpty($uriFilterName2))) {
                        $secondURI = $firstURI + "/$($ID)" + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + $uriFilterName2 + "=" + $uriFilterValue2
                    }
                    else {
                        $secondURI = $firstURI + "/$($ID)" + "?" + $uriFilterName + "=" + $uriFilterValue
                    }
                }
            }
        }

        if ($Pagination -eq $true) {
            Switch ($Type) {
                urlfilter {
                    if (-Not([string]::IsNullOrEmpty($uriFilterName2))) {
                        $finalURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + $uriFilterName2 + "=" + $uriFilterValue2 + "&" + "page="
                    }
                    else {
                        $finalURI = $firstURI + "?" + $uriFilterName + "=" + $uriFilterValue + "&" + "page="
                    }
                }
            }

            # Looking how many pages it should be and making sure that it search trough each page
            $Page = 0
            do {
                $Page++
                if (-Not([string]::IsNullOrEmpty($FilterType))) {
                    $pageURI = $finalURI + $Page + "&size=$PageSize" + "&" + $uriFilter + $SortOrder
                }
                else {
                    $pageURI = $finalURI + $Page + "&size=$PageSize" + $SortOrder
                }

                $APICall = Invoke-RestMethod -Uri $pageURI -Method Get -ContentType "application/json" -Authentication Bearer -Token $accessToken -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "responsHeader" -HttpVersion 3.0
            } while ($responsHeader.HAS_MORE_RECORDS -eq $true)
        }
        else {
            $finalURI = $secondURI + $SortOrder
            $APICall = Invoke-RestMethod -Uri $finalURI -Method Get -ContentType "application/json" -Authentication Bearer -Token $accessToken -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "responsHeader" -HttpVersion 3.0
        }

        # Retriving the results
        if ($StatusCode -eq 200 -and $null -ne $APICall) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "Success, did retrive all data from REST API Call against $finalURI" -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "$($ReturnCodeTranslation.$($StatusCode).translate), ErrorCode: $StatusCode" -ReturnValue $StatusCode
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)"
        break
    }
}
Function Connect-hvSrv {
    <#
        .SYNOPSIS
        Connect to Horizon Connection Server

        .DESCRIPTION
        With this function you can connect to Horizon Connection Servers and it will return the accessToken and refresh token.
        If the connection server that this function trys to connect to is down, maintaince or similar it will try to connect to an other Connection server at the same POD.
        You can either connect to one specific POD or set All parameter to true then it will randomly connect to some connection server at any of your PODs.

        .PARAMETER POD
        Specify what POD you want to connect to.

        .PARAMETER All
        You can set this to either $true or $false, if you set this to $true it will try to connect to a random connection server at a random POD.

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>


    [CmdletBinding()]
    Param(
        <#[Parameter(Mandatory = $false, HelpMessage = "What POD you want to get connection servers from, don't combine this with All bool")]
        [String]$POD,
        [Parameter(Mandatory = $false, HelpMessage = "Use this if you want to get try to connect to any one of your connection servers in all of your PODs")]
        [bool]$All = $false,#>

        [Parameter(Mandatory = $true, HelpMessage = "This should only be used if it's a new setup with the JSONFile hvEnvironment script")]
        [String]$hvFQDN,
        [Parameter(Mandatory = $true, HelpMessage = "Credentials to connect to Horizon Connection Server")]
        [pscredential]$Cred,
        [Parameter(Mandatory = $true, HelpMessage = "Domain name, not doman.xx it should only be domain")]
        [String]$Domain
    )

    try {
        # Importing settings
        #$SystemSettings = (Import-tSetting -Category "System" -Setting "Settings" -All).ReturnValue
       
        <#
        # Get a horizon connection server that's working
        if ([String]::IsNullOrEmpty($hvFQDN)) {
            $hvServer = Get-hvServer -POD $POD -All $All
        }
        #>


        if ($hvServer.ReturnCode -eq 0 -and [String]::IsNullOrEmpty($hvFQDN) -or (-Not[String]::IsNullOrEmpty($hvFQDN))) {
            <#if (-Not[String]::IsNullOrEmpty($hvFQDN)) {
            $hvInfo = "N/A"
            $hvFQDN = $hvFQDN
            }
            else {
                $hvInfo = $hvServer.ReturnValue
                [String]$hvFQDN = "$($hvInfo.fqdn)"
            }#>


            $Body = [ordered]@{
                'domain'   = $($Domain)
                'username' = $($Cred.UserName)
                'password' = $($Cred.GetNetworkCredential().Password)
            }

            $APICall = Invoke-RestMethod -Uri "https://$hvFQDN/rest/login" -Method Post -Body ($Body | ConvertTo-Json) -ContentType "application/json" -StatusCodeVariable "StatusCode" -ResponseHeadersVariable "ResponseHeader" -HttpVersion 3.0

            if ($StatusCode -eq 200) {
                #$Pod_Name = $(Convert-WhiteSpaceToDot -String $hvInfo.pod_name)
                #$hvName = $(Convert-WhiteSpaceToDot -String $hvInfo.name)
                return [PSCustomObject]@{
                    ReturnCode   = 0
                    accessToken  = ConvertTo-SecureString $APICall.access_token -AsPlainText -Force
                    refreshToken = $APICall.refresh_token
                    #hvName = $hvName
                    hvURI        = $hvFQDN
                    #POD_Name = $Pod_Name
                    connectTime  = Get-Date
                    Message      = "Connection established to Horizon Connection Server $hvName with FQDN $hvFQDN in the POD $Pod_Name"
                }
                Break
            }
            else {
                return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not connect to the Horizon Connection Server, ErrorCode: $StatusCode" -ReturnValue "$($StatusCode)"
                Break
            }
        }
        else {
            return $hvServer
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception.Message)"
        Break
    }
}
Function Get-hvVirtualCenter {
    <#
    This function will collect information about all Virtual Centers from the POD
    If you fill out $VirtualCenterID you will only get information about that specific Virtual Center
    if you don't fill it out you will get information about all Virtual Centers
    #>

    <#
        .SYNOPSIS

        .DESCRIPTION

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER VirtualCenterID

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, HelpMessage = "What Virtual Center you want to get information about")]
        [string]$VirtualCenterID
    )

    try {
        if ([string]::IsNullOrEmpty($VirtualCenterID)) {
            $APICall = Invoke-RestMethod -Uri "https://$hvURI/rest/config/v4/virtual-centers" -Method Get -Body $Body -Authentication Bearer -Token $accessToken -ContentType "application/json" -StatusCodeVariable "StatusCode" -HttpVersion 3.0
            $LogText = "all Virtual Centers"
        }
        else {
            $APICall = Invoke-RestMethod -Uri "https://$hvURI/rest/config/v4/virtual-centers/$VirtualCenterID" -Method Get -Body $Body -Authentication Bearer -Token $accessToken -ContentType "application/json" -StatusCodeVariable "StatusCode" -HttpVersion 3.0
            $LogText = "Virtual Center with ID $VirtualCenterID"
        }
        if ($StatusCode -eq 200) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "Information about$LogText has been collected." -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not collect information about $LogText from VMWare Horizon Connection Server $hvURI, StatusCode: $StatusCode" -ReturnValue "StatusCode: $StatusCode"
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)"
        break
    }
}
Function Get-hvVirtualCenterVM {
    <#
        .SYNOPSIS
        This function will get all virtual centers for the POD

        .DESCRIPTION

        .PARAMETER hvURI

        .PARAMETER accessToken

        .PARAMETER VM

        .PARAMETER VirtualCenterID

        .EXAMPLE

        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, HelpMessage = "Name of the VM")]
        [String]$VM,
        [Parameter(Mandatory = $true, HelpMessage = "ID for the virtual center")]
        [String]$VirtualCenterID
    )

    try {

        $APICall = Convert-hvAPI -hvURI $hvURI -accessToken $accessToken -restMethod "/external/v1/virtual-machines" -uriFilterName "vcenter_id" -uriFilterValue $VirtualCenterID -Type urlfilter -Pagination

        if ($APICall.ReturnCode -eq 0 -and $null -ne $APICall.ReturnValue) {
            $VerifyVM = $($APICall.ReturnValue | Where-Object { $_.name -like "$($VM)" })
            if ($Null -ne $VerifyVM) {
                return Get-ReturnMessageTemplate -ReturnType Success -Message "Information about VM $VM have been collected from Virtual Center ID $VirtualCenterID" -ReturnValue $VerifyVM
                Break
            }
            else {
                return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not find any VM with the name $VM in Virtual Center ID $VirtualCenterID" -ReturnValue $APICall.ReturnValue
                Break
            }
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not collect VMs from Vritual Center ID $VirtualCenterID, StatusCode: $StatusCode" -ReturnValue "StatusCode: $StatusCode"
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception)"
        Break
    }
}
Function Send-hvVMAction {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName, HelpMessage = "AccessToken to Horizon")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName, HelpMessage = "Connection Server FQDN")]
        [ValidateScript({ $_ -notlike "https://*" })]
        [string]$hvURI,
        [Parameter(Mandatory = $true, HelpMessage = "Name of the VM")]
        [ValidateNotNullOrEmpty()]
        [string]$VM,
        [Parameter(Mandatory = $true, HelpMessage = "What you want to do")]
        [ValidateSet("enter-maintenance", "exit-maintenance", "reset", "recover", "rebuild", "restart", "archive")]
        [string]$Action,
        [Parameter(Mandatory = $false, HelpMessage = "Force action, only works with enter-maintenance, restart, reset and recover")]
        [switch]$Force = $false
    )

    try {
        $GetVM = Get-hvVM -accessToken $accessToken -hvURI $hvURI -FilterValue "$($VM)"
        if ($GetVM.ReturnCode -eq 0) {
            [String]$VMID = $($GetVM.ReturnValue.Id)
        }
        else {
            return $GetVM
            Break
        }

        if ($Force -eq $true -and $Action -notlike "exit-maintenance" -and $Action -notlike "archive" -and $Action -notlike "rebuild") {
            $endUrl = "?force=true"
        }
        else {
            $endUrl = ""
        }

        $Body = "[$($VMID | ConvertTo-Json -Compress)]"

        $APICall = Invoke-RestMethod -Uri "https://$($hvURI)/rest/inventory/v1/machines/action/$($Action)/$($endUrl)" -Method POST -Body $Body -Authentication Bearer -Token $accessToken -ContentType "application/json" -StatusCodeVariable "StatusCode" -HttpVersion 3.0
        if ($StatusCode -eq 200 -and $APICall.Status_Code -eq 200) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "VM $VM has now $Action"
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not $Action for VM $VM, API Status Code: $StatusCode Call Status Code: $($APICall.Status_Code)" -ReturnValue $APICall
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)"
        break
    }
}
Function Get-hvVirtualMachineAge {
    <#
        .SYNOPSIS
        Collect all VMs from a specific pool that are available and older than a specific time

        .DESCRIPTION


        .PARAMETER hvURI
        FQDN to one Connection Server in the POD

        .PARAMETER accessToken
        Access token for authentication.
        You can collect it by using Connect-hvSrv

        .PARAMETER State
        What state you want to filter VM on, if you don't use this parameter it will filter on AVAILABLE

        .PARAMETER PoolName
        Name of the pool you want to collect VMs from

        .PARAMETER Days
        Collect VMs that are older than this amount of days, default is 0.
        You can combined this with Hours and Minutes, at least one of the parameters days, hours or minutes need to be used.

        .PARAMETER Hours
        Collect VMs that are older than this amount of hours, default is 0.
        You can combined this with Days and Minutes, at least one of the parameters days, hours or minutes need to be used.

        .PARAMETER Minutes
        Collect VMs that are older than this amount of minutes, default is 0.
        You can combined this with Days and Hours, at least one of the parameters days, hours or minutes need to be used.

        .PARAMETER Collect
        If you use this switch it will collect all VMs that are older than the time you have specified and return them as a object.
        Collect switch can't be used together with Confirm switch.

        .PARAMETER Confirm
        If you use this switch it will ask you if you really want to delete the VMs that are older than the time you have specified before it deletes them.
        Confirm switch can't be used together with Collect switch.

        .EXAMPLE
        .LINK

        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $false, HelpMessage = "Filter on state of the VM")]
        [ValidateSet("AVAILABLE", "ASSIGNED", "UNASSIGNED", "ARCHIVED")]
        [string]$State = "AVAILABLE",
        [Parameter(Mandatory = $false, HelpMessage = "Name of the pool")]
        [string]$PoolName,
        [Parameter(Mandatory = $false, HelpMessage = "How old in days should the VM be")]
        [int]$Days = 0,
        [Parameter(Mandatory = $false, HelpMessage = "How old in hours should the VM be")]
        [int]$Hours = 0,
        [Parameter(Mandatory = $false, HelpMessage = "How old in minutes should the VM be")]
        [int]$Minutes = 0,
        [Parameter(Mandatory = $false, HelpMessage = "Collecting all VMs, not deleting them")]
        [switch]$Collect = $false,
        [Parameter(Mandatory = $false, HelpMessage = "Make sure that you really want to delete the VMs before it deletes them")]
        [switch]$Confirm = $false
    )

    try {
        if ($Days -eq 0 -and $Hours -eq 0 -and $Minutes -eq 0) {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "You need to enter a value for Days, Hours or Minutes"
            Break
        }

        if ($Confirm -eq $true -and $Collect -eq $true) {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "You can't use Confirm and Delete at the same time"
            Break
        }

        $textTimePeriod = "$(if ($Days -gt 0) { "$($Days) days " })$(if ($Hours -gt 0) { "$($Hours) hours " })$(if ($Minutes -gt 0) { "$($Minutes) minutes" })"

        # Collecting all information about the pool
        $GetPoolInfo = Get-hvDesktopPool -hvURI $hvURI -accessToken $accessToken -FilterValue $PoolName
        if ($GetPoolInfo.ReturnCode -eq 0) {
            # Collecting all VMs from the pool
            $GetPoolVM = Get-hvVM -hvURI $hvURI -accessToken $accessToken -FilterName "desktop_pool_id" -FilterValue $($GetPoolInfo.ReturnValue.Id)

            # Filter VMs to the on that are not in use by any user
            $AvailableVMs = $GetPoolVM.ReturnValue | Where-Object { $_.state -eq $State }

            # Get todays date
            $Today = Get-Date

            # Filter out VMs according to the parameters
            $FilterAge = foreach ($_vm in $AvailableVMs) {
                $VMAge = [datetimeoffset]::FromUnixTimeMilliseconds($_vm.managed_machine_data.create_time).DateTime
                $ConvertAge = New-TimeSpan -Start $VMAge -End $Today
                if ($ConvertAge.Days -ge $Days -and $ConvertAge.Hours -ge $Hours -and $ConvertAge.Minutes -ge $Minutes) {
                    $_vm
                }
            }

            if ($FilterAge.Count -eq 0) {
                return Get-ReturnMessageTemplate -ReturnType Success -Message "There are no VMs that are older than $($textTimePeriod)in pool $($PoolName)" -ReturnValue $FilterAge
                Break
            }
            else {
                if ($Collect -eq $true) {
                    return Get-ReturnMessageTemplate -ReturnType Success -Message "All VMs that are older than $($textTimePeriod)from $($PoolName) have been collected" -ReturnValue $FilterAge
                    Break
                }
                elseif ($Confirm -eq $true) {
                    $UserAnswer = Read-Host "Do you really want to delete all VMs that are older than $($textTimePeriod)from $($PoolName)? (y/n)"
                    if ($UserAnswer -eq "y") {
                        $ReturnValue = foreach ($_vm in $FilterAge) {
                            $DeleteVM = Remove-hvVM -hvURI $hvURI -accessToken $accessToken -VM $_vm.name
                            if ($DeleteVM.ReturnCode -eq 0) {
                                $DeleteVM.Message
                            }
                            else {
                                $DeleteVM.ReturnValue
                            }
                        }
                        return Get-ReturnMessageTemplate -ReturnType Success -Message "All VMs that was older then $($textTimePeriod)have been deleted from $($PoolName)" -ReturnValue $ReturnValue
                    }
                    elseif ($UserAnswer -eq "n") {
                        return Get-ReturnMessageTemplate -ReturnType Success -Message "You have chosen not to delete any VMs"
                        Break
                    }
                    else {
                        return Get-ReturnMessageTemplate -ReturnType Error -Message "You need to enter y or n"
                        Break
                    }

                }
                else {
                    $ReturnValue = foreach ($_vm in $FilterAge) {
                        $DeleteVM = Remove-hvVM -hvURI $hvURI -accessToken $accessToken -VM $_vm.name
                        if ($DeleteVM.ReturnCode -eq 0) {
                            $DeleteVM.Message
                        }
                        else {
                            $DeleteVM.ReturnValue
                        }
                    }
                    return Get-ReturnMessageTemplate -ReturnType Success -Message "All VMs that was older then $($textTimePeriod)have been deleted from $($PoolName)" -ReturnValue $ReturnValue
                }
            }
        }
        else {
            return $GetPoolInfo
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception)"
        Break
    }
}
Function Get-hvDataStorePath {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $true, HelpMessage = "Virtual Center ID")]
        [ValidateNotNullOrEmpty()]
        [string]$vCenterID,
        [Parameter(Mandatory = $true, HelpMessage = "Datastore ID")]
        [ValidateNotNullOrEmpty()]
        [string]$DatastoreID
    )

    try {
        $APICall = Invoke-RestMethod -Uri "https://$($hvURI)/rest/external/v1/datastore-paths?vcenter_id=$($vCenterID)&datastore_id=$($DatastoreID)" -Method Get -Authentication Bearer -Token $accessToken -ContentType "application/json" -StatusCodeVariable "StatusCode" -HttpVersion 3.0
        if ($StatusCode -eq 200) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "Information about datastore path has been collected." -ReturnValue $APICall
            Break
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Could not collect information about Datastore Path, StatusCode: $StatusCode"
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception)"
        Break    
    }
}
Function Remove-hvVM {
    <#
        .SYNOPSIS
        .DESCRIPTION
        .PARAMETER hvURI
        .PARAMETER accessToken
        .PARAMETER VM
        .PARAMETER VirtualCenterID
        .EXAMPLE
        .LINK
        .NOTES
        Author: Robin Stolpe
        Private: robin@stolpe.io
        Twitter / X: https://twitter.com/rstolpes
        Website: https://stolpe.io
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Enter FQDN to one Connection Server")]
        [ValidateNotNullOrEmpty()]
        [string]$hvURI,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Access token for authentication")]
        [ValidateNotNullOrEmpty()]
        [SecureString]$accessToken,
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName, HelpMessage = "Name of the VM")]
        [ValidateNotNullOrEmpty()]
        [string]$VM
    )

    try {

        $VerifyVM = Get-hvVM -hvURI $hvURI -accessToken $accessToken -FilterValue $VM
        if ($VerifyVM.ReturnCode -eq 0) {
            $VMID = $($VerifyVM.ReturnValue.id -as [string])
        }
        else {
            return $VerifyVM
            Break
        }

        $Body = [ordered]@{
            'allow_delete_from_multi_desktop_pools' = $false
            "delete_from_disk"                      = $true
            "force_logoff_session"                  = $true
        }

        $APICall = Invoke-RestMethod -Uri "https://$($hvURI)/rest/inventory/v1/machines/$($VMID)" -Method Delete -Body ($Body | ConvertTO-JSON) -ContentType "application/json" -Authentication Bearer -Token $accessToken -HttpVersion 3.0 -StatusCodeVariable "StatusCode"

        if ($StatusCode -eq 204) {
            return Get-ReturnMessageTemplate -ReturnType Success -Message "VM $($VM) have been deleted from VMWare Horizon, this can take some time."
            Break 
        }
        else {
            return Get-ReturnMessageTemplate -ReturnType Error -Message "Something went wrong when trying to delete VM $($VM), Status Code $($StatusCode)" -ReturnValue $APICall
            Break
        }
    }
    catch {
        return Get-ReturnMessageTemplate -ReturnType Error -Message "$($PSItem.Exception.Message)" -ReturnValue "$($PSItem.Exception)"
        Break
    }
}