cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
6514
Views
12
Helpful
5
Comments
matday
Community Member
Task NamePassing Variables from PowerShell to UCSD
Description
Prerequisites
  1. Tested on 5.3
CategoryWorkflow
ComponentsvSphere 5.x
User Inputs

Instructions for Regular Workflow Use:

  1. Download the attached .ZIP file below to your computer. *Remember the location of the saved file on your computer.
  2. Unzip the file on your computer. Should end up with a .WFD file.
  3. Log in to UCS Director as a user that has "system-admin" privileges.
  4. Navigate to "Policies-->Orchestration" and click on "Import".
  5. Click "Browse" and navigate to the location on your computer where the .WFD file resides. Choose the .WFD file and click "Open".
  6. Click "Upload" and then "OK" once the file upload is completed. Then click "Next".
  7. Click the "Select" button next to "Import Workflows". Click the "Check All" button to check all checkboxes and then the "Select" button.
  8. Click "Submit".
  9. A new folder should appear in "Policies-->Orchestration" that contains the imported workflow. You will now need to update the included tasks with information about the specific environment.

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.

Setting variables inside PowerShell

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>


Parsing PowerShell variables

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.

Putting it all together

I built a small Custom Workflow Task to show the concept.

Inputs:

Screen Shot 2015-05-12 at 21.41.03.png

Outputs:

Screen Shot 2015-05-12 at 21.42.21.png

Script

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");


Sample Output

To prove the concept, here's a sample workflow:

Workflow Summary

Screen Shot 2015-05-12 at 21.45.26.png

PowerShell Script

Screen Shot 2015-05-12 at 21.47.00.png

Custom Workflow Inputs

Screen Shot 2015-05-12 at 21.47.48.png

Results

Screen Shot 2015-05-12 at 21.48.51.png

Log File

Screen Shot 2015-05-12 at 21.49.26.png


Extending in the Future

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:

Screen Shot 2015-05-13 at 23.11.50.png

And reduce the outputs to a single variable result:

Screen Shot 2015-05-13 at 23.13.01.png

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);


Running the Workflow

In this case the user input mapping is the same (mapped to a PowerShell command output):

Screen Shot 2015-05-13 at 23.14.03.png

However this time we map the variable name to the desired outcome (in this case an IP address):

Screen Shot 2015-05-13 at 23.14.09.png

When run the variable that we desire is collected:

Screen Shot 2015-05-13 at 23.15.02.png

You can see in the logs how it's matched:

Screen Shot 2015-05-13 at 23.15.09.png

Here are the examples from the attached workflow:

Workflow:

Screen Shot 2015-05-13 at 5.31.15 PM.png

Custom Workflow Tasks:

Screen Shot 2015-05-13 at 5.32.25 PM.png

The workflow:

Screen Shot 2015-05-13 at 5.31.31 PM.png

Comments
Patsy P
Level 1
Level 1

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!

cjonszies
Community Member

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.

Orf Gelbrich
Cisco Employee
Cisco Employee

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

Orf Gelbrich
Cisco Employee
Cisco Employee

This works just tested it:

Screen Shot 2017-04-21 at 7.53.40 AM.png

cjonszies
Community Member

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>

Getting Started

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:

Quick Links