HelloWorld Example - PowerShell
Introduction
Here is a short example which will guide you through the creation of a XenCenter plugin.
Objective: We would like a new menu item in XenCenter which says "hello from ..." followed by the names of whichever objects are selected in the XenCenter resource list (treeview).
In this example we shall be using the XenServerPSSnapIn PowerShell bindings to communicate with the servers listed in XenCenter.
Requirements
The first step is to install the following requirements:
| Requirement |
Download Location |
|---|---|
| .Net Framework v2.0 |
http://msdn.microsoft.com/en-us/netframework/aa731542.aspx |
| .Net Framework v2.0 SDK |
http://msdn.microsoft.com/en-us/netframework/aa731542.aspx |
| Microsoft PowerShell v1.0 | http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx |
| Citrix XenCenter v5.6 |
http://www.citrix.com/xenserver/download |
| Citrix XenServerPSSnapIn v5.6 |
http://community.citrix.com/cdn/xs/sdks |
| Create-PluginInstaller.ps1 |
Create-PluginInstaller.ps1 |
Also create the XenServerPSSnapIn directory as described on the XenCenter Plugins page.
XCPlugin file
To add a menu item into XenCenter and call our PowerShell script we need to create a plugin configuration file.
We start with the XenCenterPlugin node with the following attributes:
- xmlns - Specifies the schema of the XML file.
- version - The XenCenter Plugins version, we are using version 1.
<XenCenterPlugin xmlns="http://www.citrix.com/XenCenter/Plugins/schema" version="1">
To create a menu item we add a MenuItem node under the XenCenterPlugin node. We give this three attributes:
- name - For identfying the menu item.
- menu - The name of the menu under which the menu item should appear. Let's use the 'View' menu.
- serialized - Decides if copies of the plugin running simutaneously are allowed for the same object. Here we do not limit this so we set to "none".
<MenuItem name="hello-menu-item" menu="view" serialized="none">
Next we add the XenServerPowerShell tag which points to a PowerShell script to run:
- filename - The filepath of our target PowerShell script, relative to the install directory of the XenCenter executable.
- window - Whether to show the console window which runs the script. We don't want this, lets turn it off.
<XenServerPowerShell filename="Plugins\Citrix\HelloWorld\HelloWorld.ps1" window="false" />
Close off all the tags and save as HelloWorld.xcplugin.xml. The name of this file must be the same name as the name of the plugin directory in which it resides.
Resources
The next step is to add some resources which provide strings and image paths for the plugin. These are stored in DLLs so that the correct language (if other cultures are provided) can be loaded at run-time.
First we need to create the resx file. This can be done using Visual Studio. Add strings for the menu-item labels, copyright statements, filepaths to icons etc. The names of the resources strings should be <name>.<property>, where <name> is the name given to the tag and <property> is one of the properties found in the specification. Here is the resources table for this plugin.
| Name |
Value |
|---|---|
| HelloWorld.description | XenServer PowerShell plugin example. |
| HelloWorld.copyright | © Citrix Systems Inc. 2009 |
| HelloWorld.link | http://community.citrix.com/xencenter |
| hello-menu-item.label | Hello World! |
| hello-menu-item.description | Displays 'Hello World' from the selected object. |
| hello-menu-item.icon | Plugins\Citrix\HelloWorld\HelloWorld.png |
The final step is to convert this .resx into a DLL. We do this with two tools:
- ResGen.exe - Creates a .resources file from the resx. This can be found in the .NET framework SDK.
- Al.exe - Embeds the .resources file into a DLL. We specify we want to create a library, the file we wish to embed, that we need the invariant culture and the name of the DLL file. Al.exe is in the .NET framework directory.
C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ResGen.exe HelloWorld.resx C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\al.exe /t:lib /embed:HelloWorld.resources /culture:0x007F /out:HelloWorld.resources.dll
Attachments: HelloWorld.resx, HelloWorld.resources.dll, HelloWorld.png
The PowerShell script
We now need to write the script, which will compile a list of names from the selected objects in the resource list and show a message box with our "Hello from ..." in it.
XenCenter will pass in parameter sets to your plugin to let you know what is selected in the resource list, and to enable you to communicate with your connected servers. PowerShell plugins can access these parameter sets from the $ObjInfoArray variable which XenCenter populates before running your script.
$ObjInfoArray
This handy variable is an array of hashmaps. Each map represents a parameter set with the following keys - for each $map in $ObjInfoArray:
- $map["url"] - The URL of the server which owns the object selected in the tree view
- $map["sessionRef"] - An authenticated session reference for the server (allows the script to log into the server)
- $map["class"] - The type of the object selected: Server, VM etc
- $map["objUuid"] - The UUID for the selected object.
You will get:
- One parameter set per object selected in the resource list.
- One parameter set per object in a folder if a folder is selected.
- One parameter set per connected server if the user has the XenCenter node selected. These are provided to allow you to communicate with any server if launched from the XenCenter node, however the "class" and "objUuid" keys will be marked as blank.
Here is an example of how to use this array:
$SelectedObjectNames=@();
$XenCenterNodeSelected = 0;
#the object info array contains hashmaps, each of which represent a parameter set and describe a target in the XenCenter resource list
foreach($parameterSet in $ObjInfoArray)
{
if ($parameterSet["class"] -eq "blank")
{
#When the XenCenter node is selected a parameter set is created for each of your connected servers with the class and objUuid keys marked as blank
if ($XenCenterNodeSelected)
{
continue
}
$XenCenterNodeSelected = 1;
$SelectedObjectNames += "XenCenter"
}
elseif ($parameterSet["sessionRef"] -eq "null")
{
#When a disconnected server is selected there is no session information, we get null for everything except class
$SelectedObjectNames += "a disconnected server"
}
else
{
Connect-XenServer -url $parameterSet["url"] -opaqueref $parameterSet["sessionRef"]
#Use $class to determine which server objects to get
#-properties allows us to filter the results to just include the selected object
$exp = "Get-XenServer:{0} -properties @{{uuid='{1}'}}" -f $parameterSet["class"], $parameterSet["objUuid"]
$obj = Invoke-Expression $exp
$SelectedObjectNames += $obj.name_label;
}
}
Looking at that final else block you can see that before any server commands can be used we need to connect to the server. We use the sessionRef from the existing connection in XenCenter which means we don't need to provide any new credentials.
Connect-XenServer -url $parameterSet["url"] -opaqueref $parameterSet["sessionRef"]
Then when we use XenServer commands they automatically use the session from the last Connect-XenServer call. We construct an expression using the class and uuid information we were given which will retrieve the object selected in the resource list.
$exp = "Get-XenServer:{0} -properties @{{uuid='{1}'}}" -f $parameterSet["class"], $parameterSet["objUuid"] $obj = Invoke-Expression $exp
Because being pretty is important the following bit of code constructs a nice sentence out of our list of names, but equally you could just string join your name array at this point.
$NameString = "Hello from {0}" -f $SelectedObjectNames[0]; if ($SelectedObjectNames.length -gt 1) { #we are aiming for "name_1, name_2, name_3...name_n-1 and name_n" for ($i=1; $i -lt $SelectedObjectNames.length - 1; $i++) { $NameString += ", {0}" -f $SelectedObjectNames[$i] } $NameString += " and {0}" -f $SelectedObjectNames[$SelectedObjectNames.length - 1] }
Finally we use .NET Windows Forms to create an alert box by loading the correct DLL.
[Reflection.Assembly]::loadwithpartialname('system.windows.forms')
[system.Windows.Forms.MessageBox]::show($NameString, "Hello World")
The full PowerShell script is HelloWorld.ps1
Deployment
The best way to distribute your XenCenter plugin is to package your plugin into a single MSI (Windows Installer) file.
Using a Windows Installer allows you to make sure the plugin is being installed into the correct place (by checking the XenCenter InstallDir registry key) and it gives versioning. A newer version will automatically uninstall the old version and then install the new one.
You can use the Create-PluginInstaller.ps1 PowerShell script to do this. The script requires WiX 2.0 and should be executed from your XenCenter install directory. The following command was used to makean msi for this plugin:
.\Create-PluginInstaller.ps1 -out HelloWorld.msi -title "XenCenter Hello World Plugin" -description "Sample plugin for XenCenter" -manufacturer Citrix -upgrade_code $([System.Guid]::NewGuid().ToString())
Summary
This exampled covered all the basics of producing a MenuItem plugin for XenCenter using PowerShell. It also introduced how to resource and deploy your plugin.
Here are all the files you need to run the plugin again:
Extract them to <XenCenterInstallDirectory>\Plugins\Citrix\HelloWorld\ and launch XenCenter to see the new menu item.
Screenshots

