I'm using PowerShell 5.1, Visual Studio 2017, C# , and XenServer SDK 7.1.1.
Using Get-Credentials and Export-CliXml in a PowerShell program, I've saved my pool master server login credentials for the root user to an XML credentials file (xml_creds.xml)
Now, I want to create and login to a session using C# (see code below). As you can see, I'm forced to convert my secure string to a plain text string to satisfy the signature for the Xen .NET API's login_with_password
method.
Using the API, how do I login to the session using a secure string?
Code
try
{
securedPassword = new SecureString();
string unsecuredPassword = "";
Runspace rs = RunspaceFactory.CreateRunspace();
rs.Open();
Pipeline pipeline = rs.CreatePipeline(@"Import-CliXml 'C:\foo\xml_creds.xml';");
Collection<PSObject> results = pipeline.Invoke();
if (results.Count == 1)
{
PSObject psOutput = results[0];
securedPassword = ((PSCredential)psOutput.BaseObject).Password;
unsecuredPassword = new System.Net.NetworkCredential(string.Empty, securedPassword).Password;
username = ((PSCredential)psOutput.BaseObject).UserName;
rs.Close();
session = new Session(hostname, port);
session.login_with_password(username, unsecuredPassword, API_Version.API_1_3);
}
else
{
throw new System.Exception("Could not obtain pool master server credentials");
}
}
catch (Exception e1)
{
System.Console.WriteLine(e1.Message);
}
finally
{
if (securedPassword != null)
{
securedPassword.Dispose();
}
if (session != null)
{
session.logout(session);
}
}
I contacted Citrix.
The Xen API does not provide a mechanism for logging into a session using a secure string password.
So, I ended up using a C# program that executes two PowerShell
scripts that do support a secure string password.
See the code below.
Notes:
I have the 7.1.1 XenServerPSModule installed in %USERPROFILE%\Documents\WindowsPowerShell\Modules\XenServerPSModule. This module provides the Connect-XenServer cmdlet
I created the xml credentials file using PowerShell get-credentials followed by using export-clixml
I loaded the requisite
System.Management.Automation
reference by installingMicrosoft.PowerShell.5.ReferenceAssemblies
fromNuGet
form1.cs (form has just a button)
using System;
using System.Windows.Forms;
using XenSnapshotsXenAccess;
namespace Create_XenSnapshotsUi
{
public partial class Form1 : Form
{
XenSessionAccess xenSession = null;
public Form1()
{
InitializeComponent();
}
private void Button1_Click(object sender, EventArgs e)
{
xenSession = new XenSessionAccess("https://xxx.xx.x.x", @"C:\foo\xml_credentials.xml");
xenSession.Logout();
}
}
}
XenSessionAccess class
using System;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using XenAPI;
namespace XenSnapshotsXenAccess
{
public class XenSessionAccess
{
private Session xenSession = null;
public Session XenSession { get => xenSession; set => xenSession = value; }
public void Logout()
{
if (XenSession != null)
{
XenSession.logout(XenSession);
}
}
public XenSessionAccess(string poolMasterServerUrl, string xml_creds_path)
{
Collection<PSObject> results = null;
PSCredential psCredential = null;
//https://learn.microsoft.com/en-us/powershell/developer/hosting/creating-an-initialsessionstate
//Createdefault2* loads only the commands required to host Windows PowerShell (the commands from the Microsoft.PowerShell.Core module.
InitialSessionState initialSessionState = InitialSessionState.CreateDefault2();
using (Runspace runSpace = RunspaceFactory.CreateRunspace(initialSessionState))
{
runSpace.Open();
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = runSpace;
powerShell.AddCommand("Import-CliXml");
powerShell.AddArgument(xml_creds_path);
results = powerShell.Invoke();
if (results.Count == 1)
{
PSObject psOutput = results[0];
//cast the result to a PSCredential object
psCredential = (PSCredential)psOutput.BaseObject;
}
else
{
throw new System.Exception("Could not obtain pool master server credentials");
}
}
runSpace.Close();
}
initialSessionState = InitialSessionState.CreateDefault2();
initialSessionState.ImportPSModule(new string[] { "XenServerPSModule" });
initialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.Unrestricted;
SessionStateVariableEntry psCredential_var = new SessionStateVariableEntry("psCredential", psCredential, "Credentials to log into pool master server");
initialSessionState.Variables.Add(psCredential_var);
SessionStateVariableEntry poolUrl_var = new SessionStateVariableEntry("poolUrl", poolMasterServerUrl, "Url of pool master server");
initialSessionState.Variables.Add(poolUrl_var);
using (Runspace runSpace = RunspaceFactory.CreateRunspace(initialSessionState))
{
runSpace.Open();
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.Runspace = runSpace;
powerShell.AddScript(@"$psCredential | Connect-XenServer -url $poolUrl -SetDefaultSession -PassThru");
results = powerShell.Invoke();
}
if (results.Count == 1)
{
PSObject psOutput = results[0];
//cast the result to a XenAPI.Session object
XenSession = (Session)psOutput.BaseObject;
}
else
{
throw new System.Exception(String.Format("Could not create session for {0}", poolMasterServerUrl));
}
runSpace.Close();
}
}
}
}