Experienced with PowerShell, but new to C#.
I am creating a Binary PowerShell Cmdlet and have run into an issue when trying to serialize PSCredential to XML with the XmlSerializer.
When I serialize the the custom object I have created the PSCredential Properties are empty.
I have the following Custom Object :
namespace MyNameSpace
{
[XmlRoot(), XmlType("MyNamespace.CustomObject")]
public class CustomObject
{
#region Properties
[XmlElement]
public string Name { get; set; }
[XmlElement]
public string Domain { get; set; }
[XmlElement]
public string Database { get; set; }
[XmlArray(ElementName = "Plazas", IsNullable = true)]
public string[] Plazas { get; set; }
[XmlArray(ElementName = "Filter", IsNullable = true)]
public string[] Filter { get; set; }
[XmlElement]
public PSCredential DatabaseCredential { get; set; }
[XmlElement]
public PSCredential LaneCredential { get; set; }
#endregion
#region Constructors
// Blank, for Cmdlet and XML Support
public CustomObject() { }
public CustomObject(string name, string domain, string Database, PSCredential databaseCredential, PSCredential laneCredentials)
{
// ...
}
#endregion
}
}
The Command I have Written to export the object to XML :
[Cmdlet(VerbsData.Export, "CustomObject")]
public class ExportCustomObjectCommand : Cmdlet
{
[Parameter(Mandatory = true, ValueFromPipeline = true)]
public CustomObject CustomObject { get; set; }
[Parameter(Mandatory = true)]
[Alias("Path")]
public string FilePath { get; set; }
protected override void ProcessRecord()
{
XmlSerializer serializer = new XmlSerializer(typeof(CustomObject));
using (FileStream fs = File.Create(FilePath))
{
serializer.Serialize(fs, CustomObject);
}
}
}
In Powershell :
$laneCreds = [pscredential]::new("testuser1", (ConvertTo-SecureString -AsPlainText -Force -String "1234567890"))
$DBCreds = [pscredential]::new("testuser2", (ConvertTo-SecureString -AsPlainText -Force -String "0987654321"))
# Create a new Object
$obj = New-CustomObject -Name "TEST" -Database "1.2.3.4" -Domain "TEST-DOMAIN" -DatabaseCredential $DBCreds -LaneCredential $laneCreds
# verify PSCredentials are there with the original object.
$obj.DatabaseCredential.UserName
$obj.DatabaseCredential.GetNetworkCredential().Password
# Output :
testuser2
0987654321
# Exports everything except PSCredentials
Export-CustomObject -CustomObject $obj -Path "C:\temp\test.xml"
This is the command that creates a new CustomObject :
[Cmdlet(VerbsCommon.New, "CustomObject")
public class NewCustomObjectCommand: Cmdlet
{
[Parameter(Mandatory = true)]
public string Name { get; set; }
[Parameter(Mandatory = true)]
public string Domain { get; set; }
[Parameter(Mandatory = true)]
public string Database { get; set; }
[Parameter(Mandatory = true)]
public PSCredential DatabaseCredential { get; set; }
[Parameter(Mandatory = true)]
public PSCredential LaneCredential { get; set; }
[Parameter()]
public string[] Plazas { get; set; }
[Parameter()]
public string[] Filter { get; set; }
protected override void ProcessRecord()
{
CustomObject customObject = new CustomObject(Name, Domain, Database, DatabaseCredential, LaneCredential);
if (Filter != null)
{
customObject.Filter = Filter;
}
if (Plazas != null)
{
customObject.Plazas = Plazas;
}
WriteObject(customObject);
}
}
Test.xml :
<?xml version="1.0"?>
<MyNamespace.CustomObject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Name>TEST</Name>
<Domain>TEST-DOMAIN</Domain>
<Database>1.2.3.4</Database>
<Plazas>
<string>1</string>
<string>2</string>
<string>3</string>
</Plazas>
<Filter>
<string>4.3.2.1</string>
<string>5.6.7.8</string>
</Filter>
<DatabaseCredential />
<LaneCredential />
</MyNamespace.CustomObject>
I can use the PSCredentials when the object is in memory, however when writing to file the PSCredentials are null.
I was expecting the PSCredentials to be present in the appropriate fields.
I suspect it has something to do with how secure strings need to be handled.
Is this something that can be done in this manner?
I found a solution to this issue, you need to use the PSSerializer
from the System.management.Automation
assembly rather than XmlSerializer
and write the resulting string to file.
The following code produces the desired output:
namespace MyNameSpace
{
[Cmdlet(VerbsData.Export, "CustomObject")]
public class ExportCustomObjectCommand : Cmdlet
{
[Parameter(Mandatory = true, ValueFromPipeline = true)]
public CustomObject CustomObject { get; set; }
[Parameter(Mandatory = true)]
[Alias("Path")]
public string FilePath { get; set; }
protected override void ProcessRecord()
{
string serialized = PSSerializer.Serialize(CustomObject);
using (StreamWriter sw = new StreamWriter(FilePath))
{
sw.WriteLine(serialized);
}
}
}
}