Private/Interop/Invoke-ITFInMTA.ps1

function Invoke-ITFInMTA {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$CommandName,

        [Parameter(Mandatory)]
        [hashtable]$Arguments,

        [Parameter(Mandatory)]
        [string]$ModuleManifestPath,

        [Parameter(Mandatory)]
        [string]$ResultPath
    )

    $argsJson = ($Arguments | ConvertTo-Json -Compress -Depth 6)

    $encodedArgs = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($argsJson))
    $encodedResultPath = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($ResultPath))
    $encodedManifestPath = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($ModuleManifestPath))

    $cmdTemplate = @'
$ErrorActionPreference = 'Stop'
$argsJson = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('__ARGS__'))
$resultPath = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('__RESULT__'))
$manifestPath = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('__MANIFEST__'))
$params = ConvertFrom-Json -InputObject $argsJson
Import-Module $manifestPath -Force
$ht = @{}
foreach ($p in $params.psobject.Properties) { $ht[$p.Name] = $p.Value }
$r = & __CMD__ @ht -NoMtaRelaunch
$r | ConvertTo-Json -Depth 6 | Set-Content -Path $resultPath -Encoding UTF8 -Force
'@


    $cmd = $cmdTemplate.Replace('__ARGS__', $encodedArgs).Replace('__RESULT__', $encodedResultPath).Replace('__MANIFEST__', $encodedManifestPath).Replace('__CMD__', $CommandName)

    # Use -EncodedCommand to avoid quoting/escaping issues across shells.
    $encodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd))

    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName = 'powershell.exe'
    $psi.Arguments = "-NoProfile -ExecutionPolicy Bypass -MTA -EncodedCommand $encodedCommand"
    $psi.UseShellExecute = $false
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError = $true

    $p = [System.Diagnostics.Process]::Start($psi)
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()
    $p.WaitForExit()

    return [pscustomobject]@{
        ExitCode = $p.ExitCode
        StdOut   = $stdout
        StdErr   = $stderr
        ResultPath = $ResultPath
    }
}