4

我知道 PowerShell 作业,但我想使用 start-process 并将可序列化的对象传递给新的 powershell 进程。有没有办法做到这一点?

似乎使用 start-process 你必须提供一个字符串参数列表,它不会为我剪掉它。我正在尝试将 PSCredential 从一个进程获取到另一个进程(或 SecureString,我将采用其中一个)。也许这会绕过安全性。


更新 - 在看到其他人的帮助后添加我使用的解决方案(使用 @PetSerAl 的解决方案)

我写了两个测试脚本:一个父脚本和一个子脚本。父脚本调用子脚本。

父脚本:

$securePassword = ConvertTo-SecureString "testpassword" -AsPlainText -Force
$cred = New-Object PSCredential("testuser", $securePassword)
$credSerial = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes([Management.Automation.PSSerializer]::Serialize($cred)))

$psFile = "C:\repos\Test\PowerShell Scripts\KirkTestChild.ps1"
$p1 = "-someparam ""this is a test!!!"""
$p2 = "-cred ""$credSerial"""

$proc = Start-Process PowerShell.exe -PassThru:$true -Argument "-File ""$($psFile)""", $p1, $p2
Write-Host "ID" $proc.Id
Write-Host "Has Exited" $proc.HasExited

Start-Sleep -Seconds 15
Write-Host "Has Exited" $proc.HasExited

子脚本:

Param(
    $someParam,
    $cred
)

Write-Host "someParam: $($someParam)"
Write-Host "cred (raw): $($cred)"
$realCred=[Management.Automation.PSSerializer]::Deserialize([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($cred)))
Write-Host "cred user: $($realCred.UserName)"
Write-Host "start"
Start-Sleep 5
Write-Host "ending"
Start-Sleep 5
4

4 回答 4

4

是的。正如PetSerAl在评论中所写,您可以使用PSSerializer该类来处理:

$ser = [System.Management.Automation.PSSerializer]::Serialize($credential)

$cred = [System.Management.Automation.PSSerializer]::Deserialize($ser)

我不知道您的流程是否会理解 CliXML 格式;您没有指定要开始的过程。

由于这将生成多行的 XML,因此将其作为参数传递给进程可能不起作用,这就是 PetSerAl 包含对生成的 XML 进行 Base64 编码的代码的原因。

您可能还可以通过 STDIN 而不是 Base64 编码传递 XML,这也确保您不会意外地达到命令行大小限制。

于 2015-12-03T22:25:21.100 回答
1

您可以将脚本块作为参数从父 PS 进程传递给子 PS 进程。子进程执行脚本的结果被序列化并返回给父进程。(运行powershell -?以获得有关此功能的帮助)。因此,如果您想要移动凭证的方向是从子级到父级,那么这是另一种选择,或者如果您想要在两个方向上移动数据,那么您可以将之前的答案与这种方法结合起来。(OP没有说哪个方向)。

编辑

我假设 OP 想要启动另一个 Powershell 进程 - 因为他说“新的 Powershell 进程”。他想将 PSCredential 从父母传递给孩子,反之亦然。我猜是前者。基于 PetSerAl 的解决方案,他可以将 cred 序列化为 CliXML。它将有新的线路。除了可能会导致将参数传递给 Powershell 时出现问题,如果换行符位于值的中间,它们还会导致反序列化失败。因此,为了避免这些问题,整个脚本可以进行 base64 编码并通过-EncodedCommand参数传递。它看起来像这样:

$xmlStr = [management.automation.psserializer]::Serialize($cred)
$scriptStr = "$liveCred = " +
  "[management.automation.psserializer]::Deserialize('$xmlstr');" +
  "# PS statements to do something with $liveCred"
$inB64 = [Convert]::ToBase64String( [Text.Encoding]::UTF8.GetBytes($scriptStr))
powershell -EncodedCommand $inB64

如果 OP 需要从子 PS 中运行的脚本返回某些内容,并且想要使用此答案前面提到的 scriptblock 功能,我不认为他可以使用这种方法,因为该功能与-Command参数有关。相反,需要删除换行符,并且可能会出现逃避问题。

于 2015-12-03T23:12:50.257 回答
1

这是一个使用 ScriptBlock 而不是脚本文件的变体。请参阅Greg Bray在Powershell Start-Process 上的帖子以启动 Powershell 会话并传递局部变量

$securePassword = ConvertTo-SecureString 'testpassword' -AsPlainText -Force
$cred = New-Object PSCredential( 'testuser', $securePassword )

$scriptBlockOuter = {
    $sb = {
        Param(
           [Parameter( Mandatory, Position = 0 )]
           [String] $someParam,
           [Parameter( Mandatory, Position = 1 )]
           [String] $credSerial
        )
        $cred = [System.Management.Automation.PSSerializer]::Deserialize( [System.Text.Encoding]::UTF8.GetString( [System.Convert]::FromBase64String( $credSerial )))
        Write-Host "someParam: $someParam"
        Write-Host "cred user: $($cred.UserName)"
        Write-Host 'start'
        Start-Sleep 5
        Write-Host 'ending'
        Start-Sleep 5
    }
}

$p1 = 'this is a test!!!'
$credSerial = [System.Convert]::ToBase64String( [System.Text.Encoding]::UTF8.GetBytes( [System.Management.Automation.PSSerializer]::Serialize( $cred )))

$proc = Start-Process PowerShell -PassThru -ArgumentList '-Command', $scriptBlockOuter, '& $sb', '-someParam', "'$p1'", '-credSerial', "'$credSerial'"
Write-Host 'ID' $proc.Id
Write-Host 'Has Exited' $proc.HasExited

Start-Sleep -Seconds 15
Write-Host 'Has Exited' $proc.HasExited
于 2016-02-20T05:34:47.393 回答
0

使用此ConvertTo-Expression cmdlet:

父脚本:

$Expression = $Cred | ConvertTo-Expression
$credSerial = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Expression))

子脚本:

$Expression = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($credSerial))
$Cred = Invoke-Expression $Expression   # or safer: &([ScriptBlock]::Create($Expression))
于 2019-10-19T15:09:12.973 回答