Donnerstag, 17. Januar 2013

Using Powershell to call a webservice with a complex data type

Hey,
in the last time I often had to implement webservice calls into my powershell scripts. I havent done this very often in the past, so I needed to look for the correct doing on google.

What I found was lots of tutorials how to call a webservice, using the New-WebServiceProxy commandlet. To be honest, this is not satisfying.
What I was looking for is how to pass arguments of a costum types. And this is what I will show you in this blog post. If you see the solution, it is very simple.

Ok lets start with a additional parameter, the credentials.

First we need to build a credentials object:

$username = "admin"
$password = "adminpassword"
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))

Next step is to build a variable, which holds the webservice call:

$webservice = New-WebServiceProxy -Uri "http://vco-appliance:8280/vmware-vmo-webcontrol/webservice?WSDL" -Credential $cred
 
The webservice I use in this example is the webservice of the vCenter Orchestrator, a very nice tool to automate your vCenter workflows.

You can browse through your webservice very easy. you just have to pipe your $webservice object to the get-member commandlet -> $webservice | gm

What we need to know at this point is, what kind of namespace is the webservice using. This is important, because we need this to specify the type of our arguments later.
So, how to get the namespace:

$t = $webservice.getType().namespace
#output
$t
 
what you get is something like that:
Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceP
roxy1vmo_webcontrol_webservice_WSDL
 
Now our basement is build, lets create the arguments we want to pass to the webservice later:
$attributes = New-Object ($t + ".WorkflowTokenAttribute")
$attributes.name = "input"
$attributes.value = " "
$attributes.type = "String"

Maybe you ask now, how does he know that he has to use "WorkflowTokenAttribute" as type. I forget to say WorkflowTokenAttribute is the type of our object.

You can verify your object type by typing:

$attributes | gm  (gm is the short form of get-member)
or
$webservice.getType()

This is easy, the right way would be to have look into the WSDL file. The easier way is to use the error message of powershell. So run your script, passing all arguments you want to pass just as a string.
You will get an error message like that:

Cannot convert argument "3", with value: "123456", for "executeWorkflow" to type "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1vmo_
webcontrol_webservice_WSDL.WorkflowTokenAttribute[]": "Cannot convert the "123456" value of type "System.String" to type Microsoft.PowerShell.Commands.NewWebserviceProxy
.AutogeneratedTypes.WebServiceProxy1vmo_webcontrol_webservice_WSDL.WorkflowTokenAttri
bute[]"."
As you can see powershell tells us, what kind of type it expects.

Now you need to know what parameters should be included in our new object, maybe you know it, have a look to the wsdl, or the way I prefer: Import the webservice into a tool which shows you the content of the wsdl. I use wavemaker to do this.

Wavemaker is cool tool to build your own website using webservices. It is very easy. Everybody will be surprised when you create a selfservice portal within 30min ;). If you are intrested in the piece of software, just download it, it's free.

 Back to our topic:

The last step is to call your webservice with all objects we build until now:
$resp = $webservice.executeWorkflow($parameter1,"vCOuser","userPW",$attributes)
$resp means respons, I use the variable to check if my webservice call was successfull

in some webservices you will get a returncode, status, name, id and many others.

For now we are done. A complete script could look like this:

$username = "admin"
$password = "adminPW"
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList @($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
$webservice = New-WebServiceProxy -uri "http://vCO-appliance:8280/vmware-vmo-webcontrol/webservice?WSDL"  -Credential $cred
$t = $webservice.getType().namespace
$attributes = New-Object ($t + ".WorkflowTokenAttribute")
$attributes.name = "input"
$attributes.value = " "
$attributes.type = "String"
$resp = $webservice.getWorkflowsWithName("MyWorkflow*","vCOUser","userPW")
foreach( $element in $resp)
    {
        $element.id + "`t" + $element.name
   
$resp = $webservice.executeWorkflow($element,"vCOuser","userPW",$attributes)
$Workflowid = $resp.id
$status = $webservice.getWorkflowTokenStatus($Workflowid,"vCOuser","userPW")
$status
while ( $status -eq "running")
    {
        $status = $webservice.getWorkflowTokenStatus($Workflowid ,"vCOuser","userPW")
        $status
        "running"
    }
   
}

This script gets all workflows, which are starting in its name with "MyWorkflow" and starts it in a loop.

You also can build objects of a complex type:
you need this for example to call a HP service manager webservice.
$t = $webservice.GetType().Namespace
$myComplextype = new-object ($t + ".Request")
$instance = new-object ($t + ".InstanceType")
$model = new-object ($t + ".ModelType")
$keys = new-object ($t + ".KeysType")
$changeID = new-object ($t + ".StringType")

$changeID.Value = "1234"
$keys.ChangeID = $changeID
$model.instance = $instance
$model.keys = $keys

$myComplextype.model = $m

Kommentare:

  1. "What we need to know at this point is, what kind of namespace is the webservice using. This is important, because we need this to specify the type of our arguments later."

    If you use the -Namespace parameter, you can specify a namespace. That way you avoid having to use the autogenerated ones. I was about to embark on the same path as you when I discovered this.

    AntwortenLöschen
  2. Manuel, thank you so much for your explanation of how to access the webservice type. Much appreciated.

    AntwortenLöschen
  3. Thanks for your valuable posting.I have collect more than information from your website. It is really wonderful blog. please added more than tips. i'm working in Erp in india.Here providing very low price and Quality ERP,Cloud ERP, CMS , responsive webdesign and ERP. you have any more than information kindly make me call this number 044-42127512 or send your mail info@excelanto.com.

    AntwortenLöschen
  4. Hello - and thanks for the nice explantion how to access hp/sm incidents.
    I would like to know how you ie in creating an incident fill in the "Description and Journal Updates" under the Incident.

    I have tried like this (no success) - >I get a conversion error ...

    $desc = new-object ($t + ".IncidentInstanceTypeDescription")
    $jour = new-object ($t + ".IncidentInstanceTypeJournalUpdates")

    $description = "Linje1", "Linje2","linje3","Linje slut"
    $journal = "JLinje1", "JLinje2","Jlinje3","JLinje slut"

    $jour = $journal
    $desc = $description

    $instance.Description = $desc
    $instance.JournalUpdates = $jour

    The other fields in the incident are created fine ...

    Do you have hint how to make that array ?

    regards
    Mette from DK

    AntwortenLöschen
  5. Hello - and thanks for the nice explantion how to access hp/sm incidents.
    I would like to know how you ie in creating an incident fill in the "Description and Journal Updates" under the Incident.

    I have tried like this (no success) - >I get a conversion error ...

    $desc = new-object ($t + ".IncidentInstanceTypeDescription")
    $jour = new-object ($t + ".IncidentInstanceTypeJournalUpdates")

    $description = "Linje1", "Linje2","linje3","Linje slut"
    $journal = "JLinje1", "JLinje2","Jlinje3","JLinje slut"

    $jour = $journal
    $desc = $description

    $instance.Description = $desc
    $instance.JournalUpdates = $jour

    The other fields in the incident are created fine ...

    Do you have hint how to make that array ?

    regards
    Mette from DK

    AntwortenLöschen
  6. Try
    $JournalUpdate = New-Object ($type + ".StringType")
    $JournalUpdate.Value = "Journal Update"
    $juArray = @($JournalUpdate)
    $jour.JournalUpdates = $juArray
    $instance.JournalUpdates = $jour

    AntwortenLöschen