One of the challenges with the Horizon REST API’s is that they are not feature complete yet and if you ain’t on the latest version you need to scroll trough the api explorer or Swagger UI to find if the URL you need is available. I have created a short script for both python and powershell that will show all the available urls.
If you’ve taken a good look at the Swagger page you’ll see there’s a link to the api docs almost at the top
If you open this you get something that looks like a json but it’s not readable (yet!)
and with another select -expandproperty you see all the details
$json.paths | select -expandproperty "/inventory/v1/rds-servers/{id}" | select -ExpandProperty get
With Python you can start with something similar
import json,requests,urllib
requests.packages.urllib3.disable_warnings()
response = requests.get("https://pod2cbr1.loft.lab/rest/v1/api-docs?group=Default" , verify=False)
data = response.json()
for i in data["paths"]:
print(i)
but this will just give the url’s
To be able to drill down I decided to bring the url, method and the description into a list and print that if needed. This example is just with the method and url but you can add the description as well. The list is to make it easier to filter on.
import json,requests,urllib
requests.packages.urllib3.disable_warnings()
response = requests.get("https://pod2cbr1.loft.lab/rest/v1/api-docs?group=Default" , verify=False)
data = response.json()
list=[]
paths=data["paths"]
for i in paths:
for method in paths[i]:
obj = {}
obj["method"] = method
obj["url"] = i
obj["description"] = paths[i][method]
list.append(obj)
for i in list:
print(i["method"], i["url"])
Earlier this week I added several methods to the VMware Horizon Python Module that are centered about application pools and I promised a blog post so here it is 🙂 In the module we have the following methods in the Inventory about Application Pools:
Halfway at day 50 for my #100DaysOfCode Challenge. Rolled back some name changes and added a couple new methods to the @VMwareHorizon#Python Module. A blogpost will follow on how to use new_application_pool and update_application_pool. pic.twitter.com/nJOywEzUDj
All of the connects at the bottom is so I don’t need to think to do those if I need them when testing.
I end with
end=hvconnectionobj.hv_disconnect()
print(end)
Both the connected and end prints aren’t required at all but give me feedback about the status of the connection.
get_application_pools
This is the easiest method to use as it doesn’t require anything. It does allow for setting page sizes and filtering if needed. See this article if you want to know more about filtering: https://www.retouw.nl/2021/02/14/filtering-searching-and-pagination-with-the-python-module-for-vmware-horizon/ The method will return a list of dicts, for the first example I will show only the names of the items.
ap = inventory.get_application_pools(maxpagesize=100)
for i in ap:
print(i["name"])
Or just with the entire list returned
ap = inventory.get_application_pools(maxpagesize=100)
print(ap)
get_application_pool
To get a single application pool you can use get_application_pool and it requires an application_pool_id, I will use the first one of the list of application to show it.
ap = inventory.get_application_pools(maxpagesize=100)
firstap=ap[0]
print(inventory.get_application_pool(application_pool_id=firstap["id"]))
delete_application_pool
To delete an application pool we again only need the application_pool_id I will combine both the get methods to show all application pools before and after the deletion. (with some prints not relevant for the code so I won’t show them below)
ap = inventory.get_application_pools(maxpagesize=100)
for i in ap:
print(i["name"])
firstap=ap[0]
print(inventory.get_application_pool(application_pool_id=firstap["id"]))
inventory.delete_application_pool(application_pool_id=firstap["id"])
ap = inventory.get_application_pools(maxpagesize=100)
for i in ap:
print(i["name"])
new_application_pool
Since I just deleted my firefox pool I will need to recreate it. The new_application_pool method requires a dict with quite a lof of values. This is the standard list that the swagger-ui gives you
This does not say that all of these are required, what I have found to be an easy way to find what the minimums are is to create an application pool with a single key value pair. display_name is always required so I will use that one. Experience has learned that this might require several tries so let’s go.
It looks like we actually need some more: at least desktop_pool_id or farm_id since I am doing this against a connection server with no farms I’ll use a desktop pool.
No errors and a peak in the admin console shows me that I again have a firefox application
update_application_pool
To update the pools we need the application_pool_id and again a dict, this time the dict needs things we want to update. Experience again learned me there are a few required key value pairs while the example in the swagger-ui shows lots, so let’s find those. I am going to use my new firefox app as the source for this. What I actually am going to try to change is the display_name so I will use that as the first key value pair.
So here different key value pairs are required than when creating a new application pool, strange but there is nothing I can do about it! I will add these from the ap object I retrieve earlier in the script.
aps = inventory.get_application_pools(maxpagesize=100)
for i in aps:
print(i["display_name"])
filter = {}
filter["type"] = "And"
filter["filters"] = []
filter1={}
filter1["type"] = "Equals"
filter1["name"] = "name"
filter1["value"] = "Firefox"
filter["filters"].append(filter1)
ap = (inventory.get_application_pools(filter=filter))[0]
appid = ap["id"]
update_app = {}
update_app["display_name"] = "FF2"
update_app["executable_path"] = ap["executable_path"]
update_app["multi_session_mode"] = ap["multi_session_mode"]
update_app["enable_pre_launch"] = ap["enable_pre_launch"]
inventory.update_application_pool(application_pool_id=appid, application_pool_data=update_app)
aps = inventory.get_application_pools(maxpagesize=100)
for i in aps:
print(i["display_name"])
So with that you have the basics to retrieve, create, update and delete application pools using python
Yesterday I added the first method to the VMware HorizonPython module that makes use of filtering while the day before that I added pagination. VMware{Code} has a document describing available options for both but let me give some explanation.
Pagination
Pagination is where you perform a query but only get an x amount of objects returned by default. The rest of the objects are available on the next page or pages. This is exactly what I ran into with the vmware.hv.helper Powershell module a long time ago. With the REST api’s this is rather easy to add since if there are more pages/objects left the headers will contain a key named HAS_MORE_RECORDS. For all the methods that I add where pagination is supported you don’t need to handle this though as I have added it to the method itself. What I did add was the option the change the maximum page size. I default to 100 and the maximum is 1000, if you supply an interrupt higher than 1000 this will be corrected to 1000.
Filtering
Filtering needs some more work from the user of the module to be able to use it.
What options are there for filtering?
For the type we have: And, Or and Not
For the filters themselves there are: Equals, NotEquals, Contains, StartsWith and Between.
The formula is you pick one from the first row and combine that with one or more from the second row.
To apply these the document describes the base schema like this:
This all looks like a dictionary with a nested dictionary when translating it to Python but when you have multiple filters it suddenly looks like this:
otherwise know as a dictionary with a list of dictionaries in it and since the latter also works with a single dict inside the list I have taken that route. The document also describes encoding and minifying the code to it works for a REST api call but I have done all of that for you so no need to worry about it, just build the dictionary and you are good!
Now let’s actually perform a search
First I create my base object with the type AND and a list for the filters key
So after 5 weeks of following the #Python training for my 100DaysOfCode challenge I have decided that my main goal for the challenge itself will be to work on the Horizon Python Module. With the course some things I find really boring and I need a real target to really learn things instead of just repeating someone else is doing as well.
I will still do some of the fun parts of it in time like databases and such when I need it but for now I will focus on the module. This weekend I added handling of the Instant Clone domain accounts to the module and also added documentation both in the module and the github repository. I know I will still learn heaps because almost all of it is still rather new and repetition works best for me.
I have just pushed some changes to the Horizon Python module. With these changes I am more complying with the Python coding standards by initiating an object before being able to use the functions inside a class. Also I added a bunch of the api calls available in the monitor parts.
so technically you first initiate a Connection class object and than you use the hv_connect function inside that class after which the access token is stored inside the object itself.
Now to use the monitors for example you create an object for this.
The first call I will show is immediately one of the more important ones: session information. Currently only local sessions seem to be available so we’ll have to wait for global session information. This is the call I will use:
So s lot of data but less directly readable results than the soap api’s but we do see all of it including deeper levels. For applications we do see something new though.
Yes that’s actually the local application that the user launched. In this case it was through a Global Entitlement named Global_Notepad so it’s not showing that. According to the soap api’s this should also be shown there but they never do as far as I know.
Messages
One of the other things that we can do is send messages. For this we need to create an variable with the following information:
Not a lot of new data, just less things we don’t need.
Reset
If you look good you’ll see that the machine I was showing is in the already used state. In my lab this happens because often I power down the lab while I still have some sessions running. Let’s reset this machine. What do we need for this first the api method and that’s /inventory/v1/machines/action/reset for requires:
Since I am far from fluent in REST api’s and json this took me a while to find out but I did it like this
so I use the method to pull the machines, filter on the state being “ALREADY_USED”, take the id of this as a string and convert that to json. When select the body I need to add the quotes and straight brackets because if it is a single string the json won’t be usable json. I will show it later with multiple systems that it’s not needed with multiples.
One of the options already available using the Horizon REST API‘s is working with Instant Clone Administrators. In total there are 5 API calls available and I will give an explanation for al 5 on how to use them. As you can see you’ll run all of them against /rest/config/v1/ic-domain-accounts.
As you can see I currently have 2 accounts configured.
POST
With post we can configure a new Instant Clone Domain account. Let’s see what we need. According to the API explorer it looks like we need to supply a domain ID, password and account.
To get the domain ID we’ll actually need to do a GET against another url:
Now I will create the json that we’ll need to configure the account. The $data variable is just a regular powershell array that afterwards convert to the actual json
There are a few remarks about this: no propper error is returned when a wrong username and password is used. Wen you try to create an account that already exists it will return a 409 conflict.
GET with ID
This is straightforward again, just extend the url for the get with the ID of the account you want to get. I grabbed this from the regular pul request and filtered on the user account I just created
$icaccounts= Invoke-RestMethod -Method Get -uri "$url/rest/config/v1/ic-domain-accounts" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
$accountid=($icaccounts | where {$_.username -eq "username"}).id
Invoke-RestMethod -Method Get -uri "$url/rest/config/v1/ic-domain-accounts/$accountid" -ContentType "application/json" -Headers (Get-HRHeader -accessToken $accessToken)
PUT
Put can be used to change a users password. It’s requires a combination of the url with the ID from the get with id and a body like in the Post.
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here:
Cookie Policy
You must be logged in to post a comment.