In my previous post on the Nitro APIs for NetScaler I shared some PowerShell examples for interacting with a NetScaler using the Nitro C# API SDK in PowerShell. I wanted to share some similar tips and samples for scripting with the Command Center APIs, which has quite a few more gotchas than the NetScaler APIs (in my opinion :).
Loading the Command Center version of the Nitro C# framework in PowerShell is about the same as the NetScaler. Just like on the NetScaler, the needed assemblies can be found at the ‘Downloads’ section on the Command Center web console:
Download and extract the tarball to your scripting environment’s working directory. Next, add the Command Center Nitro .NET framework into your runspace using the Add-Type cmdlet:
Add-Type -Path .\newtonsoft.json.dll Add-Type -Path .\cmdctr_nitro.dll
The last requirement, which is not needed with the NetScaler APIs, is to copy ccapi.xml to your $HOME directory:
Copy-Item -Path .\ccapi.xml -Destination $HOME
If you don’t have this file in your $HOME directory you’ll get an exception when attempting to connect to the server. To do so, use com.citrix.cmdctr.nitro.service.nitro_service.login(UserName, Password):
$Credentials = Get-Credential $nitrosession = new-object com.citrix.cmdctr.nitro.service.nitro_service($ccserver,8443,"https") $nitrosession.login($Credentials.GetNetworkCredential().UserName, $Credentials.GetNetworkCredential().Password)
Once logged in the class heirarchy is very similar to the NetScaler APIs. Let’s look at a couple of examples of what you can do from here with some code snips along the way.
The primary reason that I wanted to write a script against Command Center was to run batches of tasks against a list of NetScalers and variables. In the script I took two such csv lists as parameters loop on each to execute the tasks sequentially against each NetScaler in the list.In this example I’ll show you how to run a custom task and passing values to populate the task’s ‘UserInput’ variables.
First, create a custom task in Command Center, in this example we’ll add a user that’s passed as a variable $user$:
In your PowerShell runspace load the following cmdctr.nitro.resource.configuration objects; ns_task_execution_data to get the task’s UserInput values, an ns_task of the task that will be executed, and a scheduler_data to specify how it should be execute:
$taskname = "Test" $user = "User1" $nsip = "10.0.0.1" $task = New-Object com.citrix.cmdctr.nitro.resource.configuration.ns_task_execution_data $taskdetails = [com.citrix.cmdctr.nitro.resource.configuration.ns_task]::get($nitrosession, $taskname) $taskschedule = New-Object com.citrix.cmdctr.nitro.resource.configuration.scheduler_data $taskschedule.recurr_type = "no_reccur"
From here we’ll need to put the user variable into the $task.user_input_props property. To do this you’ll need to build an explicit Dictionary[System.String,System.String] object:
$userinputprops = New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]"
Next, assign the user name variable taskdetails.task_variable_list[0].name dictionary entry. Note that the format is very specific here, most notably explicit single quotes around $UserInput$ to make sure the $’s stay in the string:
$userinputprops['$UserInput$' + $taskdetails.task_variable_list[0].name] = $user)
Since there’s only one variable in this task, we can set the $task.user_input_props property using ns_taskexecution_data.set_user_input_props(), but would otherwise do a for loop on the $taskdetails.task_variable[] array to assign multiple variables:
$task.set_user_input_props($userinputprops)
Now fill out a few other task properties, including the user who ran the job, an annotation notating the computer it was executed from, the target NetScaler IP to run the task against
$task.task_name = $taskdetails.name $task.executed_by = ($Credentials.UserName).Split('\')[1] $task.scheduler_data = $taskschedule $task.annotation = "Nitro automated task executed from $($env:COMPUTERNAME)" $task.device_list = $nsip
Then execute the task using the execute() method of the ns_task_execution_data class, passing the $task object as the second parameter:
$task = [com.citrix.cmdctr.nitro.resource.configuration.ns_task_execution_data]::execute($nitrosession,$cctask)
This will start the task in Command Center, which can then be monitored by calling get() in ns_task_status:
$taskstatus = [com.citrix.cmdctr.nitro.resource.configuration.ns_task_status]::get($nitrosession, ($cctask.execution_ids)[0])
You can then do a while/start-sleep loop on $taskstatus.status to find out what happened:
Do { $taskstatus = [com.citrix.cmdctr.nitro.resource.configuration.ns_task_status]::get($nitrosession, ($cctask.execution_ids)[0]) Start-Sleep -Seconds 1 } While ($taskstatus.status -match "Progress" -or $taskstatus.status -match "Queued")
Once the task is complete, check the status of what happened:
if ($taskstatus.status -eq "Success") { Write-Host "$($cctask.task_name) completed on $nsip" }
As always it’s a lot easier to deal with a success than a failure, here’s wait it takes to see what command failed, and what the error was, if the task faced a conflicting config:
if ($taskstatus.status -eq "Failed") { $ccfilter = New-Object com.citrix.cmdctr.nitro.util.filtervalue $ccfilter.properties.Add("id",$taskstatus.taskexecution_id + "") $errorlog = [com.citrix.cmdctr.nitro.resource.configuration.command_log]::get_filtered($nit rosession,$ccfilter) $failedcmd = ($errorlog.output.Split("`n")[2] -replace "`n|`r").Split(':')[1] $failedmsg = ($errorlog.output.Split("`n")[3] -replace "`n|`r").Split(':')[1] Write-Host "$($cctask.task_name) failed at '$failedcmd', the error was '$failedmsg'" }
I hope this example was useful and that you enjoyed a second Nitro boost for your Citrix scripting repertoire!
trying to use your powershell script to login to comman center, but i’m getting : “new-object : Exception calling “.ctor” with “3” argument(s): “Object reference not set to an instance of an object.””
This sounds like what happens if ccapi.xml is missing from $HOME; can you run a ‘dir $HOME ccapi.xml’ to confirm?
Started to work, when moved ccapi.xml to C:\windows\system32 folder
Good afternoon, I was attempting to use your example to run a command center task that kicks off a “show vrid” for a list of netscalers. When I try to execute method on the task, it tells me that the SSH credentials are missing, but I cannot find where I would set those in your example or by browsing in visual studio… would you happen to know how I set those? they are required fields in command center, but on the task I don’t see a way to set them.
The SSH credentials are defined in the Device Profile (under Citrix Network), which is then bound to the NetScalers in the Device Inventory.
Did you already assign a device profile to the NetScalers that you’re running the custom task against?
Our admins do have a profile setup and when I look, I’m assuming that is the user credentials area of that profile… they have the username populated but not the password. I was hoping there was a way to pass in my network creds kind of like when I create the $nitrosession for the task, is that not possible? I do not believe they will change the profiles due to security reasons… Any other options? If not, I will try to bark up that tree… 😉
Ah, in that case you’d likely need to create and bind a new device profile before executing the task; as far as I know the Device Profile is the only way to provide credentials for custom tasks.
thank you for the quick response.
One last question… is there a way to bind a profile to the netscaler via the NITRO API, or do I have to do it inside command center and then just call the task? I don’t see anything off the top of my head, but I’m fairly new to the NITRO piece.
Adding a device profile is pretty easy; something like this at minimum:
$newprofile = New-Object com.citrix.cmdctr.nitro.resource.discovery.device_profile
$newprofiledetails = New-Object com.citrix.cmdctr.nitro.resource.discovery.device_profile_details
$newprofiledetails.ssh_user_name = "nsroot"
$newprofiledetails.ssh_password = "password"
$newprofile.name = "Profile Name"
$newprofile.device_family = "NS"
$newprofile.prof_details = $newprofiledetails
$addprofile = [com.citrix.cmdctr.nitro.resource.discovery.device_profile]::add($nitrosession, $newprofile)
As far as assigning the profile to a device, I’m not too sure at first glance, as I’ve yet to find a way to get devices from the inventory using the API, let alone assign a device profile.
That said, if anyone reading knows, please share; otherwise I’ll do the same if/when I figure it out.