问题
I know about PowerShell jobs, but I want to use start-process and pass a serializable object to the new powershell process. Is there any way to do this?
It seems that using start-process you have to provide a string argument list which won't cut it for me. I'm trying to get a PSCredential from one process to another (or a SecureString, I'll take either one). Maybe this circumvents security.
UPDATE - adding the solution I used after seeing help from others (using solution from @PetSerAl)
I wrote two test scripts: a parent script and a child script. The parent script calls the child script.
Parent Script:
$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
Child Script:
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
回答1:
Yes. As PetSerAl wrote in a comment, you can use the PSSerializer
class to handle that:
$ser = [System.Management.Automation.PSSerializer]::Serialize($credential)
$cred = [System.Management.Automation.PSSerializer]::Deserialize($ser)
I don't know if your process will understand the CliXML format though; you don't specify what process you're starting.
Since this will produce XML which is multi-line, it may not work to pass this to a process as a parameter which is why PetSerAl is including the code to Base64 encode the resulting XML.
You might also be able to pass the XML over STDIN instead of Base64 encoding, which also ensures that you won't accidentally hit the command line size limit.
回答2:
You can pass a scriptblock as an argument from a parent PS process to a child PS process. The result of the child process executing the script is serialized and returned to the parent. (Run powershell -?
to get help on this feature). So if the direction you want to move the credential is from child to parent then this is an alternative, or if you want to move data in both directions, then you could combine the previous answers with this approach. (The OP doesn't say which direction).
EDIT
I'm assuming OP wants to start another Powershell process - because he said "new Powershell process". And he wants to pass a PSCredential from either the parent to the child or vice versa. I'd guess the former. Based on PetSerAl's solution he could serialize the cred as CliXML. It would have new lines. Besides potentially causing problems with passing the arguments to Powershell, the newlines will cause the Deserialize to fail if they are in the middle of a value. So to avoid those problems the whole script can be base64 encoded and passed via the -EncodedCommand
parameter. It'll look something like this:
$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
If OP needs something back from the script that ran in the child PS, and wanted to use the scriptblock feature mentioned earlier in this answer, I don't he could use this approach, because that feature is related to the -Command
parameter. Rather newlines would need to be removed and escaping concerns might come into play.
回答3:
Here's a variation that uses a ScriptBlock instead of script files. See the post by Greg Bray at Powershell Start-Process to start Powershell session and pass local variables.
$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
回答4:
Using this ConvertTo-Expression cmdlet:
Parent script:
$Expression = $Cred | ConvertTo-Expression
$credSerial = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Expression))
Child script:
$Expression = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($credSerial))
$Cred = Invoke-Expression $Expression # or safer: &([ScriptBlock]::Create($Expression))
来源:https://stackoverflow.com/questions/34076478/is-there-a-way-to-pass-serializable-objects-to-a-powershell-script-with-start-pr