on 05-12-2015 01:34 PM - edited on 03-25-2019 01:28 PM by ciscomoderator
Task Name | Passing Variables from PowerShell to UCSD |
Description | |
Prerequisites |
|
Category | Workflow |
Components | vSphere 5.x |
User Inputs |
Instructions for Regular Workflow Use:
UCS Director supports running PowerShell scripts straight from a workflow. Although this adds powerful new possibilities, it can be tricky to pass objects back from PowerShell to UCS Director.
For example, let's take this trivial PowerShell script:
write "192.168.100.1";
return "Hello World!";
Running that from UCS Director will give me the following XML output:
<?xml version='1.0'?>
<Objects>
<Object Type='System.String'>192.168.100.1</Object>
<Object Type='System.String'>Hello World!</Object>
</Objects>
As you can see, anything that is written to the terminal or returned will be passed back to UCS Director. Although some basic Regular Expression might help out here, I wanted to do something a bit smarter.
The first step was to create a method for containing my variables. For example, if I want to pass an IP Address, a machine name and some metadata back, how can I guarantee the order of the XML? What if it changes in the future?
The way I've dealt with this problem is to store everything in an associative array. This way I can pass key/value pairs back to UCS Director in a standard, easy to use way. For example:
$ip_address = "192.168.100.1";
$machine_name = "matday-test-01";
# Create an array to store all the UCSD return values in:
$ucsd = @{}
$ucsd.ip_address = $ip_address;
$ucsd.machine_name = $machine_name;
$ucsd.metadata = "Authored by Matt Day";
# Return the array back to UCSD:
return $ucsd;
The neat thing here is that I'm able to pass anything I like back to UCS Director in a safe fashion. Taking a look at the XML this will return, you get this:
<?xml version='1.0'?>
<Objects>
<Object Type='System.Collections.Hashtable'>
<Property Name='Key' Type='System.String'>metadata</Property>
<Property Name='Value' Type='System.String'>Authored by Matt Day</Property>
<Property Name='Key' Type='System.String'>ip_address</Property>
<Property Name='Value' Type='System.String'>192.168.100.1</Property>
<Property Name='Key' Type='System.String'>machine_name</Property>
<Property Name='Value' Type='System.String'>matday-test-01</Property>
</Object>
</Objects>
Once this PowerShell script is complete, we need to parse the XML data. To do this, we can create a custom workflow task to capture this output.
For this example, I'm going to create a custom task that captures IP Address, Machine Name and Metadata, although yours can include anything you like.
The first step is to import the UCS Director and Java libraries.
// Import UCS Director scripting library and Java standard library
importPackage(com.cloupia.lib.util);
importPackage(java.util);
Then the above XML needs to be included as a variable.
var xml = "<?xml version='1.0'?><Objects><Object Type='System.Collections.Hashtable'><Property Name='Key' Type='System.String'>metadata</Property><Property Name='Value' Type='System.String'>Authored by Matt Day</Property><Property Name='Key' Type='System.String'>ip_address</Property><Property Name='Value' Type='System.String'>192.168.100.1</Property><Property Name='Key' Type='System.String'>machine_name</Property><Property Name='Value' Type='System.String'>matday-test-01</Property></Object></Objects>";
You might want to map this to an input for your custom task.
I now need to use the built-in XMLUtil class to parse the above XML tree. It works hierarchically, so each layer of XML needs to be done sequentially.
The first step is to grab the data inside the <Objects>...</Objects> layer:
var objects_xml = XMLUtil.getValue("Objects", xml);
This returns a list of all the matching sections. In this case, it's only going to return 1 item containing the <Object>...</Object> tags. Again, we need to parse this:
// Get the first match (0) from the <Objects>...</Objects> section:
object_list = XMLUtil.getTag("Object",objects_xml.get(0))
Now we need to build a list of all the <Property>...</Property> tags from the single <Object>...</Object> tree:
// Get the first match (0) from the <Object>...</Object> section:
property_list = XMLUtil.getTag("Property",object_list.get(0))
As you saw above, the key/value pairs are on alternating lines. The easiest way to obtain this information is to iterate through the list, treating even numbers as keys and odd numbers as values.
// Store results in a HashMap (a Java associative array)
var variable_map = new HashMap();
// Store previous keys in buffer:
var key_buffer = "";
// Loop through all values taking even as keys and odd as values:
for (i = 0; i < property_list.size(); i++) {
// Remove XML tags with a bit of Regular Expression
property_list.set(i, property_list.get(i).replaceAll("<.*?>",""));
// Keys (even numbers)
if ((i % 2) == 0) {
key_buffer = property_list.get(i);
}
// Values (odd numbers)
else {
variable_map.put(key_buffer, property_list.get(i));
}
}
Now using the example above where the IP Address, Machine Name and Metadata are all being passed back, we can pull this out via a UCS Director script:
output.ip_address = variable_map.get("ip_address");
output.machine_name = variable_map.get("machine_name");
output.metadata = variable_map.get("metadata");
This could be extended to anything you like and further checked, mapped etc in to UCS Director's various types.
I built a small Custom Workflow Task to show the concept.
importPackage(com.cloupia.lib.util);
importPackage(java.util);
var xml = input.xml
// Try and parse the <Objects>...</Objects> section
var objects_xml = XMLUtil.getValue("Objects", xml);
// Parse the objects list now (should also be a single section):
object_list = XMLUtil.getTag("Object",objects_xml.get(0))
// Parse the object_list to get properties:
property_list = XMLUtil.getTag("Property",object_list.get(0))
// Store results in a HashMap (a Java associative array)
var variable_map = new HashMap();
// Store previous keys in buffer:
var key_buffer = "";
// Loop through all values taking even as keys and odd as values:
for (i = 0; i < property_list.size(); i++) {
// Remove XML tags
property_list.set(i, property_list.get(i).replaceAll("<.*?>",""));
// Keys
if ((i % 2) == 0) {
key_buffer = property_list.get(i);
}
// Values
else {
variable_map.put(key_buffer, property_list.get(i));
}
}
// Match desired output to HashMap fields:
output.ip_address = variable_map.get("ip_address");
output.machine_name = variable_map.get("machine_name");
output.metadata = variable_map.get("metadata");
To prove the concept, here's a sample workflow:
This could be extended to support any output from a PowerShell script. By adding a new input to the above workflow, "variable", you could specify exactly what you wanted output and run it multiple times against the same PowerShell output, allowing a general purpose variable collector.
For example, we add a new input variable_name:
And reduce the outputs to a single variable result:
The following script could then be used to match any output:
importPackage(com.cloupia.lib.util);
importPackage(java.util);
var xml = input.xml
// Try and parse the <Objects>...</Objects> section
var objects_xml = XMLUtil.getValue("Objects", xml);
// Parse the objects list now (should also be a single section):
object_list = XMLUtil.getTag("Object",objects_xml.get(0));
// Parse the object_list to get properties:
property_list = XMLUtil.getTag("Property",object_list.get(0));
// PowerShell returns arrays weirdly to UCSD, alternating rows of keys/values
// Like this:
// <Property Name="Key" Type="System.String">ip</Property>
// <Property Name="Value" Type="System.String">192.168.100.1</Property>
// <Property Name="Key" Type="System.String">server_name</Property>
// <Property Name="Value" Type="System.String">New Server</Property>
//
// Store output in a HashMap:
var variable_map = new HashMap();
// Store previous keys in buffer:
var key_buffer = "";
// Loop through all values taking even as keys and odd as values:
for (i = 0; i < property_list.size(); i++) {
// Remove XML tags (can't seem to coax the XML library to do this for me!)
property_list.set(i, property_list.get(i).replaceAll("<.*?>",""));
// Keys
if ((i % 2) == 0) {
key_buffer = property_list.get(i);
}
// Values
else {
variable_map.put(key_buffer, property_list.get(i));
}
}
// Match desired output to HashMap fields:
output.variable = variable_map.get(input.variable_name);
In this case the user input mapping is the same (mapped to a PowerShell command output):
However this time we map the variable name to the desired outcome (in this case an IP address):
When run the variable that we desire is collected:
You can see in the logs how it's matched:
Here are the examples from the attached workflow:
Workflow:
Custom Workflow Tasks:
The workflow:
This is absolute gold. Been hunting and playing for quite a while trying to figure out how to get this working properly with the little information that is out there. Thanks for explaining!
I really need to work with that to enhance my Workflows but I have a big problem.
When I try to check an output variable with IfElse or a Conditional Task it does not work.
For Example:
when I use your Example Workflow:
- Add an Output Variable "IPAddress" to the Workflow (Type: Generic Text)
- Map the User Output of Task "Get IP Address" to the Variable "IPAddress"
- Add an IfElse Task with the following condition: IPAddress == "192.168.100.1"
The Workflow Fails:
Apr 21, 2017 14:09:45 CEST Task: PowerShell Variable Passing Example (If Else) failed with error - Not able to parse to a integer value, please check the condition - IPAddress=='192.168.100.1', selectedContext=<None>
Apr 21, 2017 14:09:45 CEST Task #5 (PowerShell Variable Passing Example (If Else)) failed after 0 seconds
Can anyone help me out with that?
It would be great to get this running.
this is the new write up for the if statement
Conditional Operators Supported - integers
==, !=, <, <=, >, >=
Conditional Operators Supported - lexical
equals, notEquals, contains, startsWith, endsWith
Conditional Operators Supported - Boolean
|| - Logical Boolean OR operator, && - Logical Boolean AND operator
This works just tested it:
Hi Orf,
thanks for the information.
I had tested „equals“ but it always resulted in „False“.
I have now found the problem ☺
If I pass the output to an output variable and check the variable „IPAddress“ then IfElse always results in „False“.
I have to check the task-value itself:
Get IP Address.variable equals "192.168.100.1"
This works!
Best regards
Christian
i.A. Christian Jonszies
Professional Consultant
christian.jonszies@stemmer.de
<mailto:christian.jonszies@stemmer.de>Bitte beachten Sie die neue E-Mail-Adresse!
Tel.: +49 (2734) 2759-0
Fax: +49 (2734) 2759-44
BT Stemmer GmbH
Standort Freudenberg
Hommeswiese 136, 57258 Freudenberg, Germany
Sitz der Gesellschaft: Olching, AG München HR B 132831
Geschäftsführer: Stefan Hischer (Vorsitzender), Henning Heimann, Oliver Herrmann, Dirk Behrens
Von: Orf Gelbrich
Gesendet: Freitag, 21. April 2017 14:55
An: Jonszies, Christian <Christian.jonszies@stemmer.de>
Betreff: Re: - Passing variables from PowerShell to UCS Director
Cisco Communities <https://communities.cisco.com/>
Passing variables from PowerShell to UCS Director
new comment by Orf Gelbrich<https://communities.cisco.com/people/ogelbric>
View all comments on this document<https://communities.cisco.com/docs/DOC-58250#comment-29847>
Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: