Passing Hashtables to Start-SMARunbook as a Parameter

In this post, we are going to explore using hashtables as parameters in an SMA workflow. I’ll let the code do most of the talking this time.

Consider the following SMA runbook:

workflow DEV_MDTestArea
{   
    Param(
        [hashtable]$hashTest
    )
    $hashtest.keys
}


Lets try run this runbook:

$strSMAManagementServer = "https://yourmgmtserver"
Start-SmaRunbook -name "DEV_MDTestArea" -Parameters @{"hashtest" = $hashTest} -WebServiceEndpoint $strSMAManagementServer

Return:

The values provided for the root activity’s arguments did not satisfy the root activity’s requirements:
‘DynamicActivity’: Expected an input parameter value of type ‘System.Collections.Hashtable’ for parameter named ‘hashTest’.
Parameter name: rootArgumentValues

Well, that didn’t really do what we want….does it….
The trick is that SMA doesn’t support passing hashtables as parameters. According to MSDN, we need to define the hashtable as an [object] type.

Let’s try that:

workflow DEV_MDTestArea
{   
    Param(
        [object]$hashTest
    )
    [hashtable]$hashTest = [hashtable]$hashtest
    $hashtest.keys
}

According to this blog post:
http://blogs.technet.com/b/orchestrator/archive/2014/01/10/sma-capabilities-in-depth-runbook-input-output-and-nested-runbooks.aspx
We should be able to pass a hashtable to this runbook. There isn’t really mention of the Start-SMARunbook cmdlet though….let’s try it out:

I’ve used this as my input:

workflow Dev_MDTestArea3
{
    $hash = @{"Test1" = 1}
    DEV_MDTestArea `
        -hashTest $hash

}

That worked!….the output from the runbook is “Test1”

Lets try that from our Start-SMARunbook command:

$strSMAManagementServer = "https://yourmgmtserver"
$hashTest = @{"test1" = "1"}
Start-SmaRunbook -name "DEV_MDTestArea" -Parameters @{"hashtest" = $hashTest} -WebServiceEndpoint $strSMAManagementServer

Well that appears to have worked….but wait a minute…the output is blank.

Let’s modify our main runbook a little:

workflow DEV_MDTestArea
{   
    Param(
        [object]$hashTest
    )
    $hashTest | get-member
}

Running the same Start-SMARunbook command as previously, we now get the following output:

TypeName: Deserialized.System.Management.Automation.PSCustomObject
Name MemberType Definition PSComputerName
—- ———- ———- ————–
Equals Method bool Equals(System.Object obj) localhost
ToString Method string ToString() localhost
GetType Method type GetType() localhost
obj) localhost
GetHashCode Method int GetHashCode() localhost
mputerName=localhost localhost
PSShowComputerName NoteProperty System.Boolean PSShowComputerName=True localhost
st
ToString Method string ToString() localhost
PSComputerName NoteProperty System.String PSComputerName=localhost localhost
localhost
PSComputerName NoteProperty System.String PSComputerName=localhost localhost
PSShowComputerName NoteProperty System.Boolean PSShowComputerName=True localhost
PSSourceJobInstanceId NoteProperty System.Guid PSSourceJobInstanceId=f5ba8bbc-eb2d-4289-a128-4e31c95db2cd localhost
test1 NoteProperty System.String test1=1 localhost

hmmm….That doesn’t format so well…..but we can pick out a few nuggets….
It appears that the hashtable we supplied in our Start-SMARunbook command got converted into a PSCustomObject. We can also see the key and value pair on the last line.

So how do we convert a PSCustomObject to a hashtable? This can be found numerous places on the interweb. You may notice however that there are a few small things to keep in mind within SMA. The property we want is a NoteProperty as expected. However there are also some properties that start with PS that are also note properties. We will need to filter these out before reconstructing our hashtable:

workflow DEV_MDTestArea2
{
    param(
        [parameter(Mandatory=$false)]
        [Object]$objTest
    )
    $hashTest = @{}
    $objTest | Get-member -MemberType NoteProperty | Where-Object -FilterScript{ !($_.Name.StartsWith("PS")) } | ForEach{$hashTest.($_.Name) = $objTest.($_.Name)}
    $hashTest.keys
}

And let’s execute it:

$strSMAManagementServer = "https://yourmgmtserver"
$hashTest = @{"test1" = "1"}
Start-SmaRunbook -name "DEV_MDTESTAREA2" -Parameters @{"objTest" = $hashTest} -WebServiceEndpoint $strSMAManagementServer 

And the output:
“test1”

Now let’s try from within SMA:

workflow Dev_MDTestArea3
{
    $hash = @{"Test1" = 1}
    DEV_MDTestArea2 `
        -objTest $hash
}

And no output.

So it appears that if you have a runbook that you need to pass a hashtable as a parameter you need to be very careful. It appears that if the runbook is invoked from within SMA the hashtable is left as a hashtable…..if you are invoking it from the Start-SMARunbook it is a PSCustomObject.

Let’s verify the first part of that:

workflow DEV_MDTestArea
{   
    Param(
        [object]$hashTest
    )
    $hashTest | get-member
}
workflow Dev_MDTestArea3
{
    $hash = @{"Test1" = 1}
    DEV_MDTestArea `
        -hashTest $hash
}

And the output

TypeName: Deserialized.System.Collections.Hashtable

Name MemberType Definition
—- ———- ———-
Add Method void Add(System.Object key, System.Object value), void IDictionary.Add(System.Object key, System.Obje…

Clear Method void Clear(), void IDictionary.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
Contains Method bool Contains(System.Object key), bool IDictionary.Contains(System.Object key)
ContainsKey Method bool ContainsKey(System.Object key)
ContainsValue Method bool ContainsValue(System.Object value)
CopyTo Method void CopyTo(array array, int arrayIndex), void ICollection.CopyTo(array array, int index)
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IDictionaryEnumerator GetEnumerator(), System.Collections.IDictionaryEnum…
GetHashCode Method int GetHashCode()
GetObjectData Method void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Seria…
GetType Method type GetType()
OnDeserialization Method void OnDeserialization(System.Object sender), void IDeserializationCallback.OnDeserializ…
Remove Method void Remove(System.Object key), void IDictionary.Remove(System.Object key)
ToString Method string ToString()
PSComputerName NoteProperty System.String PSComputerName=localhost
PSShowComputerName NoteProperty System.Boolean PSShowComputerName=True
PSSourceJobInstanceId NoteProperty System.Guid PSSourceJobInstanceId=b779252a-adfa-4fed-80eb-390067a54391
Item ParameterizedProperty System.Object Item(System.Object key) {get;set;}
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
Keys Property System.Collections.ICollection Keys {get;}
SyncRoot Property System.Object SyncRoot {get;}
Values Property System.Collections.ICollection Values {get;}

So, let’s fix our runbook so it works with both the Start-SMARunbook PScmdlet and within SMA itself:

workflow DEV_MDTestArea2
{
    param(
        [parameter(Mandatory=$false)]
        [Object]$objTest
    )
    $hashTest = @{}
    $strObjTestTypeName = ($objTest | get-member).TypeName | Select-Object -first 1
    If($strObjTestTypeName -eq "System.Collections.Hashtable"){
        $hashTest = $objTest
    }
    If($strObjTestTypeName -eq "System.Management.Automation.PSCustomObject"){
        $objTest | Get-member -MemberType NoteProperty | Where-Object -FilterScript{ !($_.Name.StartsWith("PS")) } | ForEach{$hashTest.($_.Name) = $objTest.($_.Name)}
    }
    $hashTest.keys
}

Note: Due to the similarities between Azure Pack which SMA uses and the hosted Azure Automation, this solution may be portable to Azure automation as well….although I have no way of testing this…

Advertisements

2 thoughts on “Passing Hashtables to Start-SMARunbook as a Parameter

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s