powershellserializationdeserializationpowershell-corepowershell-7.3

How to serialize/deserialize data back and forth between Windows PowerShell and PowerShell core?


I've been trying to find a solution to work with a specific cmdlet from ConfigCi built-in module that is incompatible with PowerShell core. The cmdlet is New-CIPolicyRule

I want to use it in my module that targets PowerShell 7.3+.

Here is the command that fails in PowerShell core but works in Windows PowerShell 5.1

$ScanLocation = "C:\Program Files\WindowsApps\*"
$RulesWildCards = New-CIPolicyRule -FilePathRule $ScanLocation
New-CIPolicy -MultiplePolicyFormat -FilePath .\policy.xml -Rules $RulesWildCards

This is the error message:

New-CIPolicy: Cannot bind parameter 'Rules'. Cannot convert value "Microsoft.SecureBoot.UserConfig.Rule" to type "Microsoft.SecureBoot.UserConfig.Rule". Error: "Cannot convert the "Microsoft.SecureBoot.UserConfig.Rule" value of type "Deserialized.Microsoft.SecureBoot.UserConfig.Rule" to type "Microsoft.SecureBoot.UserConfig.Rule"."

The problem is exactly this command:

New-CIPolicy -MultiplePolicyFormat -FilePath .\policy.xml -Rules $RulesWildCards

So I thought why not just let Windows PowerShell handle it, like this:

$ScanLocation = "C:\Program Files\WindowsApps\*"
$RulesWildCards = New-CIPolicyRule -FilePathRule "$ScanLocation"
powershell.exe "New-CIPolicy -MultiplePolicyFormat -FilePath .\ss.xml -Rules `"$RulesWildCards`""

But nope, it's not that easy, the error message is this:

New-CIPolicy : Cannot bind parameter 'Rules'. Cannot convert the "Microsoft.SecureBoot.UserConfig.Rule" value of type "System.String" to type "Microsoft.SecureBoot.UserConfig.Rule".

I tried a bunch of combinations of the commands above but no luck

Then I tried importing and exporting the serialized data since IsSerial is True for the output of that command which is stored in $RulesWildCards variable

enter image description here

And these are the interfaces it implements

enter image description here

$ScanLocation = "C:\Program Files\WindowsApps\*"
$objectpolicy = New-CIPolicyRule -FilePathRule $ScanLocation
Export-Clixml -InputObject $objectpolicy -Path .\policyobj.xml
powershell.exe "New-CIPolicy -MultiplePolicyFormat -FilePath .\policy.xml -Rules $(Import-Clixml -Path .\policyobj.xml)"

Even that didn't work.

I tried a few different things too but none of them worked.

I need a proper way to handle this problem so that I can apply the same solution every time I need to use this cmdlet in PowerShell core.


Solution

  • Try the following - note the use of a script block ({ ... }), -args to pass (invariably positional) arguments, and the reference to the first (and only) positional argument as $args[0] in the script block:

    powershell.exe { 
      $RulesWildCards = New-CIPolicyRule -FilePathRule $args[0]
      New-CIPolicy -MultiplePolicyFormat -FilePath .\ss.xml -Rules $RulesWildCards
    } -args 'C:\Program Files\WindowsApps\*'
    

    Passing a script block ({ ... }) to the PowerShell CLI (which is only supported from inside a PowerShell session) - of either edition - triggers special behavior that tries to preserve type fidelity as much as possible, within the constraints of PowerShell's XML-based cross-process serialization - see this answer for background information.