Write to a vRO Configuration Element Attribute

You can store configuration values in vRealize Orchestrator with configuration elements.  VMware’s website states a configuration element is:

a list of attributes you can use to configure constants across a whole Orchestrator server deployment.

Configuration elements have attributes that store values which you can read from and write to in your scripts.  In this example, we will have a single configuration element called “testElement” that contains an attribute called “testAttrib”.  This attribute is an array of string values in which we will add entries using an action.

In the picture above, we have a folder called “Blog” that contains the configuration element “testElement”. It is important to know what the path is to the configuration element, because we will be searching in the folder for that specific name. Within the “testElement” we have the “testAttrib” that contains an empty array at this point.

We will use an action to find the configuration element, locate the attribute we want, and finally read the data into a variable. Once we have the data, we will have to save off the current data to a temporary variable, push the new values we want, and overwrite the data currently saved in the attribute. This action has one input variable that is a string called “testEntry”. This is the value we will add to the attribute array stored in the configuration element.


I created a brand new action called “blogConfigRead” for this article. This contains all of the code needed to read the data from the configuration elements. We can then insert this into a vRO workflow execute the action and print us the data we want.

Finding the Data:

First, we open the folder that contains the configuration element we want to read. If we don’t find it, we throw an exception:
var pgconfig = Server.getConfigurationElementCategoryWithPath('Blog');
if (pgconfig == null) {
throw "Configuration element path not found!!";
}

From there, we search for the config element by name:

for (var x=0;x<pgconfig.configurationElements.length;x++) {
//if we find one that matches the name of the testElement
if (pgconfig.configurationElements[x].name == 'testElement') {
//Save it off in a variable
var configElem = pgconfig.configurationElements[x];
System.debug("Found the testElement config element!");
break;
}
}

If we find the config element, then we save it off to a variable and break from the loop. If we get to the end of the loop and the variable is still set to ‘NULL’, then we didn’t find anything. We should just throw an exception to get out of the routine.

//Throw exception on error if we cannot find it!
if (configElem == null) {
throw "Configuration Element not found!";
}

Now we can finally search for the attribute by name:

//Get the attribute that is the portCatalog with all the entries
var testAttrib = configElem.getAttributeWithKey('testAttrib');
//Throw exception on error if we cannot find it!
if (testAttrib == null) {
throw "Attribute not found in configuration element!";
}

Again, if we did not find it, then we throw an exception.

Reading Data:

We are going to now save off the current array value for the attribute so that we can add an entry later. We will pull the values into an object:

//We are working with an array for this attribute, so let's get the current values. If it is null, create a blank array
if (testAttrib.value == null) {
var objArray = new Array()
} else {
var objArray = testAttrib.value;
}

Notice how we check to see if the value is null or “not set”. If it is, we need to just create a new Array so we can use the “push” string function below. Otherwise, we will get an exception error.

Finally, we are going to take out input value of “testEntry”, push that onto the objArray Object, and overwrite the attribute value with the new objArray.

//Add value to current array and then push new value to attribute
objArray.push(newEntry);
configElem.setAttributeWithKey('testAttrib',objArray);

Let’s create a workflow with one input variable and link it to the “testEntry” input variable needed to run the action.



Now that we have the workflow created, let’s add an entry by running it:

Looks good to me! Let’s check the configuration element:

Success!! ‘tester123’ was added to the array stored in the attribute. Let’s add another one:

It was successful and here it is in the configuration element:

Wrap-up:

Configuration elements are a nice and easy way to keep track of values within the vRO server. They can be used across multiple actions and workflows to help coordinate data. Full code is below:

//Get all the configuration elements on the server
var pgconfig = Server.getConfigurationElementCategoryWithPath('Blog');
//If we didn't find the config element, then we should die out
if (pgconfig == null) {
throw "Configuration element path not found!!"
}
//Loop through all the found config elements
for (var x=0;x<pgconfig.configurationElements.length;x++) {
//if we find one that matches the name of the testElement
if (pgconfig.configurationElements[x].name == 'testElement') {
//Save it off in a variable
var configElem = pgconfig.configurationElements[x];
System.debug("Found the testElement config element!");
break;
}
}
//Throw exception on error if we cannot find it!
if (configElem == null) {
throw "Configuration Element not found!";
}
//Get the attribute that is the portCatalog with all the entries
var testAttrib = configElem.getAttributeWithKey('testAttrib');
//Throw exception on error if we cannot find it!
if (testAttrib == null) {
throw "Attribute not found in configuration element!";
}
//We are working with an array for this attribute, so let's get the current values. If it is null, create a blank array
if (testAttrib.value == null) {
var objArray = new Array()
} else {
var objArray = testAttrib.value;
}
//Add value to current array and then push new value to attribute
objArray.push(newEntry);
configElem.setAttributeWithKey('testAttrib',objArray);

Hope this helps,

AJ

NSX-T Protection: Who Needs Protection?

While working with the python SDK for NSX-T, I happened to notice a parameter for objects called ‘Protection’.  Here is the definition of the parameter directly from the source code:

Protection status is one of the following: PROTECTED - the client
    who retrieved the entity is not allowed to modify it. NOT_PROTECTED
    - the client who retrieved the entity is allowed to modify it
    REQUIRE_OVERRIDE - the client who retrieved the entity is a super
    user and can modify it, but only when providing the request header
    X-Allow-Overwrite=true. UNKNOWN - the _protection field could not
    be determined for this entity.
    This attribute may be present in responses from the server, but if
    it is present in a request to server it will be ignored.

Notice the above states that this attribute is ignored if it is set in a client request.  Setting this attribute will prevent users (with the exception of enterprise admins) from deleting or editing objects in the NSX Manager.  Having this functionality could be useful, so let’s dive a bit deeper on how to use it!

Normal REST calls
To prove that the call does not work with a regular rest call, let’s try it.  Below is an object we want to create in the NSX Manager:

{
"display_name" : "TestGroup",
"_protection" : "REQUIRE_OVERRIDE"
}

We will attempt to set the _protection attribute by setting the variable in the data and sending the call to the NSX Manager via CURL.

curl -k -u admin -d @testgroup.json -X POST https://XX.XX.XX.XX/api/v1/ns-groups/ --header "Content-Type: application/json"

{
"resource_type" : "NSGroup",
"id" : "8d8fe782-7767-4b39-a26e-5af09e48a281",
"display_name" : "TestGroup",
"members" : [ ],
"member_count" : 0,
"_create_time" : 1537278850472,
"_last_modified_user" : "admin",
"_last_modified_time" : 1537278850475,
"_system_owned" : false,
"_create_user" : "admin",
"_protection" : "NOT_PROTECTED",
"_revision" : 1
}

Notice how the object in the GUI does not have a lock next to it. That is because, as stated above, a client request cannot set the protection attribute. The setting can only be done by the NSX Manager. What we need to perform this change, is a trusted ID in the NSX Manager that is allowed to set the attribute upon creation. We need a principal ID defined in the system

Create a Principal ID
Principal IDs must be created based off of a trusted certificate that is uploaded to the NSX Manager. The NSX Manager does not validate the certificate chain, so a basic self-signed certificate will do just fine here. In this example, I will be creating a cert using openssl. The NSX Manager can be very picky so use the example below as a baseline template for creating your certificate:

openssl req -newkey rsa:2048 -extensions usr_cert -nodes -keyout test.key -x509 -days 365 -out test.crt -subj "/C=US/ST=Michigan/L=Detroit/O=NSX/CN=test" -sha256

The command above generates a certificate (test.crt) and a private key (test.key). We can now upload these to the NSX Manager:

Go to System->Trust and click ‘Import’

Upload the cert and key into the NSX Manager:

You should now see the cert in the NSX Manager:

Now that the cert is uploaded, we can grab its ID so we can create a principal ID off it. The cert will be used to validate the REST calls being sent from that machine.

The example below is the payload needed to create a principal ID using the cert ID of “d5f0549d-004f-45f7-b544-97251972c46c”.

{
"name" : "testuser",
"node_id" : "testuser",
"is_protected" : "true",
"certificate_id" : "d5f0549d-004f-45f7-b544-97251972c46c",
"permission_group" : "read_write_api_users"
}

The Node_ID can be used to differentiate multiple nodes in a cluster that could make changes to the NSX Manager. This is primarily used in a VIO (VMware Integrated Openstack) environment. In this case, we make it the same as the ‘name’ attribute. Also, the ‘is_protected’ attribute MUST be set to “true”.  With this, any object created by this principal ID will be protected from other admin users.

We use the admin account to create the principal ID:
curl -k -u admin -d @principalid.json -X POST https://XX.XX.XX.XX/api/v1/trust-management/principal-identities/ --header "Content-Type: application/json"

{
"resource_type" : "PrincipalIdentity",
"id" : "eabc82a4-c5e7-4e14-84fb-21de38a898d1",
"display_name" : "testuser@testuser",
"tags" : [ ],
"certificate_id" : "d5f0549d-004f-45f7-b544-97251972c46c",
"role" : "enterprise_admin",
"name" : "testuser",
"permission_group" : "undefined",
"is_protected" : true,
"node_id" : "testuser",
"_create_time" : 1537280635914,
"_last_modified_user" : "admin",
"_last_modified_time" : 1537280635914,
"_system_owned" : false,
"_create_user" : "admin",
"_protection" : "NOT_PROTECTED",
"_revision" : 0
}

If it worked, we should have something like the output above. The NSX Manager will now show the new testuser principal ID:

Now we can test!!

Testing REST Calls
I deleted the old “testgroup” and will recreate it with the principal ID now:

{
"display_name" : "TestGroup"
}

curl -k --cert ./certs/test/test.crt --key ./certs/test/test.key -d @testgroup.json -X POST https://XX.XX.XX.XX/api/v1/ns-groups/ --header "Content-Type: application/json"

{
"resource_type" : "NSGroup",
"id" : "0ec60ce2-deea-4e74-9a0c-d867e88c16e7",
"display_name" : "TestGroup",
"members" : [ ],
"member_count" : 0,
"_create_time" : 1537281161137,
"_last_modified_user" : "testuser",
"_last_modified_time" : 1537281161140,
"_system_owned" : false,
"_create_user" : "testuser",
"_protection" : "NOT_PROTECTED",
"_revision" : 1
}

Now you are probably looking at the output above and saying “wait a minute! The _protection attribute still says “NOT_PROTECTED”!

Correct, because from the perspective of the testuser, it CAN edit that value. If we are to look at it in the NSX Manager, we will see that it has a lock symbol by it and shouldn’t be editable.

As you can see, the edit button is grayed out in the GUI and there is a lock symbol by the object. We successfully created a protected object in the NSX Manager! Let’s look at the object from the perspective of another user:

curl -k -u admin -X GET https://XX.XX.XX.XX/api/v1/ns-groups/

{
“resource_type” : “NSGroup”,
“id” : “0ec60ce2-deea-4e74-9a0c-d867e88c16e7”,
“display_name” : “TestGroup”,
“members” : [ ],
“member_count” : 0,
“_create_time” : 1537281161137,
“_last_modified_user” : “testuser”,
“_last_modified_time” : 1537281161140,
“_system_owned” : false,
“_create_user” : “testuser”,
“_protection” : “REQUIRE_OVERRIDE”,
“_revision” : 1
}

The admin account, from its perspective, can see it is a protected object and would require a special override to delete or edit it. If the user were not an enterprise admin, then the _protection attribute would say “PROTECTED”.

To override it and delete the object, we have to set a special header variable called X-Allow-Overwrite to ‘true’:

curl -k -u admin -X DELETE https://XX.XX.XX.XX/api/v1/ns-groups/0ec60ce2-deea-4e74-9a0c-d867e88c16e7 --header "X-Allow-Overwrite: true"

Now if we look in the NSX Manager, it should be gone:

All set! Hope this was helpful.

AJ