Introduction
On a recent project I was calling a PowerShell script and wanted to return Xml from the script so that I could iterate on an object using Linq to Xml. I investigated options available for exporting an object in Powershell to Xml and looked at some built-in cmdlets like Export-CliXml: http://technet.microsoft.com/en-us/library/dd347657.aspx, but I could not get over how complicated my Linq to Xml query would have to be for relatively simple objects. The Export-CliXml format is extremely verbose and is not really easy to work with once you get the Xml out.
Technique
The following snippet shows how to output the Xml and uses custom serialization to Xml for the SamlEndpoint type which shows up as the type name until you drill into the type to get the property information.
function DumpObjectToXml($obj) { $a = $obj $openingTag = "<" + $a.GetType().Name + ">" $ret = $ret + $openingTag Get-Member -InputObject $a -MemberType Properties | ForEach-Object { $CurrentName = $_.Name $a.GetType().GetProperties() | ? { $_.Name -eq $CurrentName } | ForEach-Object { $specialSerialization = $false # Handle specialized serialization for object properties of parent object if ($_.Name -eq "SamlEndpoints") { if ($obj.SamlEndpoints -ne $null) { $d = $obj.SamlEndpoints | ? { $_.BindingUri -like "*HTTP-POST" } $val = $d.Location.AbsoluteUri $specialSerialization= $true } else { $val = "" } } if ($_.CanRead -eq $true -and $specialSerialization -eq $false) { $val = $_.GetValue($a, $null) } } $out = "<" + $_.Name + ">" + $val + "</" + $_.Name + ">" $ret = $ret + $out } $closingTag = "</" + $a.GetType().Name + ">" $ret = $ret + $closingTag return $ret }
This can be helpful for outputting a list of objects returned by another cmdlet as Xml. Here is a simple example that builds a usable Xml element for the relying parties returned from the built-in ADFS cmdlet:
$rpOut = "<RelyingPartyTrusts>" Get-ADFSRelyingPartyTrust | ForEach-Object { $rpOut = $rpOut + (DumpObjectToXml -obj $_) } $rpOut = $rpOut + "</RelyingPartyTrusts>"
The resulting output looks like this:
<RelyingPartyTrusts> <RelyingPartyTrust> <AutoUpdateEnabled>False</AutoUpdateEnabled> <ClaimsAccepted>Microsoft.IdentityServer.PowerShell.Resources.ClaimDescription Microsoft.IdentityServer.PowerShell.Resources.ClaimDescription</ClaimsAccepted> <ConflictWithPublishedPolicy>False</ConflictWithPublishedPolicy> <DelegationAuthorizationRul es></DelegationAuthorizationRules> <Enabled>False</Enabled><EncryptClaims>False</EncryptClaims> <Identifier>https://myAdfsServer/ClaimsAwareWebAppWithManagedSTS/</Identifier> ... </RelyingPartyTrust> </RelyingPartyTrusts>
Conclusion
This technique of using objects returned from PowerShell can be very valuable when you want to report on objects that may only be exposed through a PowerShell interface and no supported or available .NET API. There are some limitations to this approach though. Not all objects returned from some cmdlets have properties that can be read in an isolated, atomic way. When trying this technique with Get-Process errors occur indicating the services must be stopped before reading the properties. I think this is due to the way the cmdlet is coded. I tested this script with many of the ADFS cmdlets and it is working well.