vRealize Automation – Adding Groups to an Existing Identity Directory using vRO

I work with alot of customers in VMware from small to big multi-nationals and everything in-between.  One of my current large retail customers has been developing a business group onboarding service for vRealize Automation 7.5 which involves automating:

  • Active Directory group creation and population
  • modification of the vRA directory configuration
  • directory synchronisation
  • business group creation
  • entitlement creation

Most of these are not too hard to achieve within vRealize Orchestrator however the modification of the vRA directory configuration had them stumped.  I am going to devote this article to showing you how to automate the directory configuration modification and synchronisation using vRO as well as other ways of looking at this problem.

 

1.  Different Approaches

There are several ways to approach this problem and not all of them actually require us to manipulate the identity management within vRA.

The first option is to configure the directory within vRA so that it points to a single OU in combination with the “Select All” option.  The idea behind this is that all new user groups created would be placed inside this OU and be automatically picked up by a directory sync operation without ever having to update the group configuration for the directory.  This would look something like the following in vRA.

Screenshot 2019-03-13 at 17.10.09.png

The second approach is to create a domain local active directory group (we’ll call this the “sync” group) and add that explicitly to the directory within vRA alongside the “Sync nested group members” option.  Any new active directory groups can then be placed inside the sync group using an orchestrator workflow, again with no required changes being needed within vRA.

The third approach is to use the REST client and the identity API to update the directory configuration.

 

2.  Which Approach is Correct?

The answer to this question is not straight forward and it is largely dependent on what the customer will allow you to do within the Active Directory.  For example, for my customer the first approach would not work as the directory team would not allow product specific OU’s to be created and there was no other existing OU that was suitable and could be guaranteed to only contain the groups that should be sync’d into vRA.

The second approach technically works and gets around the restrictions of approach 1 however it also introduces an issue with the objects that are visible within vRA.  When you sync a group and it has one or more nested groups within it the nested groups do not get sync’d themselves, only the groups users do.  For example, consider the following nested relationship:

Sync AD Group “contains” BusinessGroup1 AD Group “contains” UserABC

In this example the vRA directory config has the group “Sync” added to it however when we try and locate the group “BusinessGroup1” within “Directory Users and Groups” it cannot be found.  However “UserABC” can be found without any issues.  If you only need the users then this works fine however in an automated service my customer needs to be able to assign individual groups to business groups so without the actual new active directory group visible within vRA it is not possible to use this method.

This leaves approach number 3 where we are automating the creation of a new AD group (together with populating the users – all standard stuff) and then amending the vRA directory configuration.  Now this has potential issues such as what happens if multiple requests are updating the directory configuration at the same time, however it is possible to use this method with the necessary guard rails in place.

 

3.  Updating the Groups in a vRA Directory

If you have worked with vRO in combination with vRA then you will most likely know that the plugin methods for manipulating the directory are not exactly plentiful.  However API’s for the identity service do exist, they jusy are not exposed via the vRA plugin.

The first step is to see what API calls are available.  These can be seen on the VMware Code website (https://code.vmware.com) using the “VMware API Explorer”.  Here I have located the identity API for vRA 7.5 and filtered the methods for anything with “directories”.

Screenshot 2019-03-13 at 17.34.03.png

From the methods returned I can see a few that have potential (the top 3) so lets look at those.  We don’t want to delete the directory so we can exclude the first one.  The “GET” operation might show the data we have to play with so our first step is to look at that.  By expanding the method we can see what the returned payload will contain as well as the input parameters.

Screenshot 2019-03-13 at 17.41.23

The field highlighted looks like it might contain the configuration we want to update so the next step is to assemble a live call to a directory within vRA and see what is returned.  There are two ways to do this in vRO.  The first is to setup a REST host and add the code to get a bearer token and use that authenticate in order to make the REST call.  The second is to initiate the REST call via the vRA Cafe Rest Client which uses the authentication already specified when the vRA host is added in the vRA plugin.  I will use this second method (note that I have to specify the name of the endpoint API I want to use).

var id_endpoint = "com.vmware.csp.core.cafe.identity.api";
var uri_id_store = "/tenants/" + "vsphere.local" + "/directories/" + "corp.local";
var rest_client = cafeHost.createRestClient(id_endpoint);

var response = rest_client.get(uri_id_store);
System.log(response);

“cafeHost” in the above code is the cafe host object for the default tenant that I am passing into my workflow.  The output of this execution looks like:

[2019-03-13 17:54:15.276] [I] DynamicWrapper (Instance) : [vCACCAFEServiceResponse]-[class com.vmware.o11n.plugin.vcac.model.cafe.CAFEServiceResponse] — VALUE : HEADERS: {Cache-Control=[no-cache, no-store], Pragma=[no-cache], Expires=[Wed, 31 Dec 1969 23:59:59 GMT], Content-Disposition=[inline;filename=f.txt], Strict-Transport-Security=[max-age=31536000 ; includeSubDomains], X-XSS-Protection=[1; mode=block], X-Frame-Options=[DENY, SAMEORIGIN], X-Content-Type-Options=[nosniff], Content-Type=[application/json;charset=UTF-8], Content-Length=[885], Date=[Wed, 13 Mar 2019 17:54:15 GMT]}, BODY:
{
“domain”: “corp.local”,
“name”: “corp.local”,
“description”: “corp.local”,
“alias”: “”,
“type”: “NATIVE_AD”,
“userNameDn”: “administrator”,
“password”: null,
“url”: null,
“groupBaseSearchDn”: “ou=vRA Groups,dc=corp,dc=local”,
“userBaseSearchDn”: “ou=vRA Groups,dc=corp,dc=local”,
“subdomains”: [
],
“groupBaseSearchDns”: [
“ou=vRA Groups,dc=corp,dc=local”,
“dc=corp,dc=local”,
“CN=Public Cloud Developers,CN=Users,DC=corp,DC=local”
],
“userBaseSearchDns”: [
“ou=vRA Groups,dc=corp,dc=local”,
“dc=corp,dc=local”
],
“domainAdminUsername”: “administrator”,
“domainAdminPassword”: null,
“useGlobalCatalog”: false,
“groupObjectQuery”: null,
“bindUserObjectQuery”: null,
“userObjectQuery”: null,
“customDirectorySearchAttribute”: null,
“customDirectorySearchAttributeForGroups”: null,
“membershipAttribute”: null,
“objectUuidAttribute”: null,
“distinguishedNameAttribute”: null,
“new”: false
}

This is looking promising as the field we are interested in “groupBaseSearchDns” has DN’s in it for the groups in my configuration.  So the next step is to see if we can amend the data in the response payload and submit it back.

The payload that we are going to send back will update the entire directory configuration for the specified directory so we need to make sure it contains the correct information otherwise the directory may not function after the update.  The “password” and the “URL” fields in the response are showing as null and it is likely that these will need to updated as well before we can push the configuration back to vRA.  Lets try anyway and see what happens.

In this example I am converting the response to JSON and then updating the attribute for the groups (which is an array) by pushing a new group DN into it.  Once that is done we use the “PUT” method to push the updated configuration back.

var id_endpoint = "com.vmware.csp.core.cafe.identity.api";
var uri_id_store = "/tenants/" + "vsphere.local" + "/directories/" + "corp.local";
var rest_client = cafeHost.createRestClient(id_endpoint);
var response = rest_client.get(uri_id_store);
var payload = response.getBodyAsJson();
payload.groupBaseSearchDns.push("CN=Public Cloud Developers,CN=Users,DC=corp,DC=local");
var update = rest_client.put(uri_id_store, payload);

 

When I execute this I return a number of exceptions.

[2019-03-13 18:07:03.258] [E] Error in (Workflow:directory / Scriptable task (item1)#10) [Rest Composite Exception]: ( [Rest Error]: {Status code: 400}, {Error code: 10103} , {Error Source: field:password}, {Error Msg: Property identityStore.password cannot be null.}, {System Msg: identityStore.password may not be null.} ) ( [Rest Error]: {Status code: 400}, {Error code: 10103} , {Error Source: field:url}, {Error Msg: Property identityStore.url cannot be null.}, {System Msg: identityStore.url may not be null.} )
[2019-03-13 18:07:03.300] [E] Workflow execution stack:
***
item: ‘directory/item1’, state: ‘failed’, business state: ‘null’, exception: ‘[Rest Composite Exception]: ( [Rest Error]: {Status code: 400}, {Error code: 10103} , {Error Source: field:password}, {Error Msg: Property identityStore.password cannot be null.}, {System Msg: identityStore.password may not be null.} ) ( [Rest Error]: {Status code: 400}, {Error code: 10103} , {Error Source: field:url}, {Error Msg: Property identityStore.url cannot be null.}, {System Msg: identityStore.url may not be null.} ) (Workflow:directory / Scriptable task (item1)#10)’

As expected the errors relate to the two empty properties for the URL and password.  So if I update those our code now looks as follows:

var id_endpoint = "com.vmware.csp.core.cafe.identity.api";
var uri_id_store = "/tenants/" + "vsphere.local" + "/directories/" + "corp.local";
var rest_client = cafeHost.createRestClient(id_endpoint);
var response = rest_client.get(uri_id_store);
var payload = response.getBodyAsJson();
payload.password = "VMware1!";
payload.url = "ldap://controlcenter.corp.local:389";
payload.groupBaseSearchDns.push("CN=Public Cloud Developers,CN=Users,DC=corp,DC=local");
var update = rest_client.put(uri_id_store, payload);

Submitting this code now works without exceptions and our directory configuration now has a new AD group within it.

Hopefull this article has given you some insight into what can be achieved.  Just remember that updating directory configuration should be done with caution and the appropriate measures to ensure only one update is done at a time and the directory is not corrupted.