VMware’s Identity Manager. 🙁
A product which causes me more hassle than it’s worth (in my opinion!). I get why it’s there and when you have a mix of SAML / passwords / certs etc then its fine, but for simple AD/LDAP authentication it really comes unstuck.
I have encountered problems in both vRA 7.x and now with vRA 8.x where I have custom actions (XaaS / ABX ) which build out infrastructure – these being new business groups in 7.x and new project constructs in 8.x. As part of these XaaS functions new AD groups are built and mapped as part of the flow in order to control access to these elements.
Now here’s the problem. You have just created a new security group in AD and mapped it to a business group or project – but the vIDM has no clue this group exists. The result is that these groups / projects will be unusable until the vIDM does its next directory sync which, if the sync schedule is at the shortest interval, could be an hour away. This isn’t great if you want to provide customers instant access to their groups and generally a good product flow.
To get around this within code I build out all AD constructs as the first components (in vRA 8.x I use ldap3 to do this, see my other post for details on this – https://tg-test100.com/using-ldap3-python-module-to-manage-active-directory )
Once these are built, it is possible to login to the vIDM via the API and fire off a sync cycle which will then pull in these new groups so they can be mapped to vRA components.
Login to vIDM
The first step is to connect to the Identity Manager and grab an authentication token back from the response.
import requests import json import time session = requests.Session() session.headers.update({"content-type" : "application/json"}) session.headers.update({"accept" : "application/json"}) idm_login_url = 'https://<vIDM_IP_ADDRESS>/SAAS/API/1.0/REST/auth/system/login' idm_login_body = { 'username': 'tgadmin', 'password': 'password_placeholder', 'issueToken': 'true' } idm_login_body_json = json.dumps(idm_login_body) response = session.post(idm_login_url, data = idm_login_body_json, verify=False) print("Logging into IDM to obtain idm_token") print(response.status_code) print(response.content) if response.status_code == 200: json_response = json.loads(response.content) idm_token = json_response['sessionToken'] if idm_token: print("Token obtained")
The output….
>> Logging into IDM to obtain idm_token >> 200 >> {"id":null,"sessionToken":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJqdGkiOiI5ODE0OTAxMC1hMzRkLTRmM2QtOTk0My1hYmM3ZDczNGQ3ODgiLCJwcm4iOiJ0Z2FkbWluQFRHVklETTAyIiwiZG9tYWluIjoiU3lzdGVtIERvbWFpbiIsInVzZXJfaWQiOiI4IiwiYXV0aF90aW1lIjoxNjA4MjA3Mzg2LCJpc3MiOiJodHRwczovLzE3Mi4xNi4xMC4xMTcvU0FBUy9hdXRoIiwiYXVkIjoiaHR0cHM6Ly8xNzIuMTYuMTAuMTE3L1NBQVMvYXV0aC9vYXV0aHRva2VuIiwiY3R4IjoiW3tcIm10ZFwiOlwidXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnRcIixcImlhdFwiOjE2MDgyMDczODYsXCJpZFwiOjE1fV0iLCJzY3AiOiJwcm9maWxlIGFkbWluIHVzZXIgZW1haWwiLCJpZHAiOiIwIiwiZW1sIjoiY29uZmlndXNlckB2bXdhcmUuY29tIiwiY2lkIjoiIiwiZGlkIjoiIiwid2lkIjoiIiwicnVsZXMiOnsiZXhwaXJ5IjoxNjA4MjA5MTg2LCJydWxlcyI6W3sicmVzb3VyY2VzIjpbIioiXSwiYWN0aW9ucyI6WyJhY3M6cmVhZFJ1bGVTZXRzIiwiZG06cmVhZCIsInVnOnJlYWQiLCJlbnQ6cmVhZCIsImN0ZzpyZWFkIiwidG50czpyZWFkIiwicnB0OioiXSwiY29uZGl0aW9ucyI6bnVsbH0seyJyZXNvdXJjZXMiOlsiKiJdLCJhY3Rpb25zIjpbIioiXSwiY29uZGl0aW9ucyI6bnVsbH1dLCJsaW5rIjoiaHR0cHM6Ly90Z3ZpZG0wMi50Zy5sb2NhbDo0NDMvYWNzL3J1bGVzL21lIn0sImV4cCI6MTYwODIzNjE4NiwiaWF0IjoxNjA4MjA3Mzg2LCJzdWIiOiI3NjU2ODUxNi03NzJmLTRmYWEtYmVjYi04OGI2MTM3ZWE3NGMiLCJwcm5fdHlwZSI6IlVTRVIifQ.AzaFB02dTDNAW4oUNlBrXGQUF1IUqwbo62PArxCj_xv9MLNbnrNPZcPoFj74r_NT-pyrRXkmOTPxPFImG4Pac5VoJJFYxdx0bOZujJu2H645Yx7biXUFAH36mej2fZX2lKi0nHMcyCr62B-Sjeml8S8HzgpLrAU3SR0LLxyHMlQYOZ143nBeG4YfBkEYWmY93Bc3_cjiuCFgG9WfTZA8RTjPkGlkChl9Bk9vExl5H2fCgr_SJJFp40myLgurbTdZYc4LMUIfBksoz4RmazHQVE3gjkR91j5n5HXqdfjn9S1_9JcnokN9qYiMdY95PJ5Az96xNbLlhJpPMqL8o3J0Pw" ,"firstName":null,"lastName":null,"admin":false,"serverUrl":null,"signingCert":null} >> Token obtained
Obtaining your directory ID
To execute against your configured directory within the vIDM you will need its ID.
If you are looking for a dirty shortcut, just grab the directory ID from the browser using developer tools.
However, the better way to get this ID is programatically, especially if you have multiple directories and tennants configured within the IDM. This is achieved as follows:
# Setup the auth header using the token found above. # Setup the accept header to be very specific to allow listing of directories session.headers.update({"accept" : "application/vnd.vmware.horizon.manager.connector.management.directory.list+json"}) basic_auth_header = "Bearer " + idm_token session.headers.update({"authorization" : basic_auth_header}) # Setup the URL for querying # Run a get request idm_dirquery_url = 'https://<VIDM_ADDRESS>/SAAS/jersey/manager/api/connectormanagement/directoryconfigs' response = session.get(idm_dirquery_url) print(response.status_code) print(response.content) ## Output. Iterate through the directories and extract the directoryId ## of interest for the next step >> {"items":[{"type":"LOCAL_DIRECTORY","name":"System Directory","directoryId": "6c161620-ec09-4406-aeca-8cf37184f2a3","userstoreId":"369df052-d38a-4783-9182-df dd6695795a","countDomains":1,"deleteInProgress":false,"syncConfigurationEnabled" :false,"_links":{"hw-dir-connectors":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/6c161620-ec09-4406-aeca-8cf37184f2a3/connectors"}, "self":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/6c161620-ec09-4406-aeca-8cf37184f2a3"}}}, {"type":"ACTIVE_DIRECTORY_LDAP","name":"TG.LOCAL","directoryId":"1f593d6b-134b-42a6-aeb5-f817f0e2919d", "userstoreId":"1fcebfd0-a0b0-4b2c-b617-3000327b6a20","countDomains":1,"deleteInProgress": false,"syncConfigurationEnabled":true,"_links":{"hw-sync": {"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/1f593d6b-134b-42a6-aeb5-f817f0e2919d/syncprofile/sync"}, "hw-dir-connectors":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/1f593d6b-134b-42a6-aeb5-f817f0e2919d/connectors"}, "hw-dir-sync-executions":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/1f593d6b-134b-42a6-aeb5-f817f0e2919d/syncexecutions"}, "self":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/1f593d6b-134b-42a6-aeb5-f817f0e2919d"}}}], "_links":{"self":{"href":"/SAAS/jersey/manager/api/connectormanagement/directoryconfigs"}}}
Running the Sync Job
Now I have the directory ID along with a valid login I can execute the sync:
Update your headers again before running the HTTP Post.
“accept” : “application/vnd.vmware.horizon.v1.0+json”
“content-type” : “application/vnd.vmware.horizon.manager.connector.management.directory.sync.profile.sync+json”
# Custom headers for the IDM session.headers.update({"accept" : "application/vnd.vmware.horizon.v1.0+json"}) session.headers.update({"content-type" : "application/vnd.vmware.horizon.manager.connector.management.directory.sync.profile.sync+json"}) basic_auth_header = "Bearer " + idm_token session.headers.update({"authorization" : basic_auth_header}) #Kick of IDM Sync, then sleep to let it complete. This depends how big your #Directory is as to how long it will take idm_sync_url = 'https://<VIDM_IP_ADDRESS>/SAAS/jersey/manager/api/connectormanagement/directoryconfigs/<DIRECTORY_ID>/syncprofile/sync' idm_safeguard_body = { 'ignoreSafeguards':'false' } idm_safeguard_body_json = json.dumps(idm_safeguard_body) response = session.post(idm_sync_url, data = idm_safeguard_body_json) if response.status_code == 200: print("Sync kicked off successfully, sleeping for 60 seconds") time.sleep(60)
And that is it.
I have found this to be a vital process when starting to layer extensibility into the vRA suite.
Hopefully it helps someone.
Side Note…vRA Cloud
Before I forget. If you are using vRA Cloud rather than an on-premise installation, this is still possible. If you have a locally installed vIDM connector which is mapped back to VMware Cloud Services so to allow centralised authentication, this same problem exists but can be overcome the same way…just adjust your URL in the code as follows:
HTTPS://<DOMAIN-IDENTIFIER>-CSP.WORKSPACEONEACCESS.COM
ie https://tg-local-csp.worksapceoneaccess.com/SAAS/API/1.0/REST/auth/system/login
References:
If you need to monitor the status of the update you will have to implement a polling mechanism to the same URI as the for the kick off sync but with GET request, Example:
If you are using :
idm_sync_url = ‘https:///SAAS/jersey/manager/api/connectormanagement/directoryconfigs//syncprofile/sync’
idm_safeguard_body = {
‘ignoreSafeguards’:’false’
}
idm_safeguard_body_json = json.dumps(idm_safeguard_body)
response = session.post(idm_sync_url, data = idm_safeguard_body_json)
then after you see that the response status is 200 you can start polling at
response = session.get(‘https:///SAAS/jersey/manager/api/connectormanagement/directoryconfigs//syncprofile/sync’)