2015-12-03 1 views
3

Je connais les travaux PowerShell, mais je veux utiliser start-process et passer un objet sérialisable au nouveau processus powershell. Est-ce qu'il y a un moyen de faire ça?Existe-t-il un moyen de transmettre des objets sérialisables à un script PowerShell avec un processus de démarrage?

Il semble qu'en utilisant le processus de démarrage, vous devez fournir une liste d'arguments de chaîne qui ne me le coupera pas. J'essaie d'obtenir un PSCredential d'un processus à un autre (ou un SecureString, je prends l'un ou l'autre). Peut-être que cela contourne la sécurité.


MISE À JOUR - ajout de la solution je après avoir vu l'aide des autres (en utilisant une solution de @PetSerAl)

j'ai écrit deux scripts de test: un script parent et un script enfant. Le script parent appelle le script enfant.

Parent Scénario:

$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 

Script Enfant:

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 
+2

'$ b = [Convert] :: ToBase64String ([Text.Encoding] :: UTF8.GetBytes ([Management.Automation.PSSerializer] :: Serialize ($ a)))' '$ c = [ Management.Automation.PSSerializer] :: Désérialiser ([Text.Encoding] :: UTF8.GetString ([Convert] :: FromBase64String ($ b))) '' – PetSerAl

+0

Excellente approche, PetSerAl !!! Je vais essayer ça bientôt. –

Répondre

1

Oui. Comme PetSerAl a écrit dans un commentaire, vous pouvez utiliser la classe PSSerializer pour gérer que:

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

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

Je ne sais pas si votre processus comprendra le format de CliXML bien; vous ne spécifiez pas le processus que vous démarrez. Puisque ceci produira du XML qui est multi-ligne, il peut ne pas fonctionner pour passer ceci à un processus en tant que paramètre qui est pourquoi PetSerAl inclut le code à Base64 codent le XML résultant.

Vous pouvez également passer le code XML sur STDIN au lieu du codage Base64, ce qui garantit également que vous n'obtiendrez pas accidentellement la limite de taille de ligne de commande.

+0

Comme je vais le passer en début de processus, les sauts de ligne peuvent être inutiles, mais je peux probablement essayer dans les deux sens. Merci pour l'explication. Je rapporterai une fois que j'essaierai ceci plus tard ce matin. –

+0

@KirkLiemohn Il existe une option de ligne de commande powershell pour accepter l'entrée de commande encodée en Base64. Donc, vous pouvez l'utiliser pour éviter les problèmes d'échappement, y compris avec newline. –

+0

@ user2460798 true, mais il ne peut pas spécifier de fichier à exécuter (si tel était le plan). Il devrait générer du code qui incorpore l'objet XML dans le code qu'il voulait déjà exécuter, puis base64 encoder le tout. De plus, cela doit être unicode (contrairement à UTF8), qui double la taille de la chaîne codée en base64, ce qui augmente la probabilité d'atteindre la limite de la ligne de commande. – briantist

1

Vous pouvez transmettre un scriptblock en tant qu'argument d'un processus PS parent à un processus PS enfant. Le résultat du processus enfant exécutant le script est sérialisé et renvoyé au parent. (Exécutez powershell -? pour obtenir de l'aide sur cette fonctionnalité). Donc, si la direction que vous souhaitez déplacer est de l'enfant au parent, alors ceci est une alternative, ou si vous voulez déplacer des données dans les deux directions, vous pouvez combiner les réponses précédentes avec cette approche. (L'OP ne dit pas dans quelle direction).

EDIT

Je suppose OP veut lancer un autre processus Powershell - parce qu'il a dit "nouveau processus Powershell". Et il veut passer un PSCredential du parent à l'enfant ou vice versa. Je devinerais le premier. Basé sur la solution de PetSerAl, il pourrait sérialiser le cred comme CliXML. Il aurait de nouvelles lignes. En plus de potentiellement causer des problèmes avec le passage des arguments à Powershell, les nouvelles lignes provoqueront l'échec de la désérialisation si elles sont au milieu d'une valeur. Donc, pour éviter ces problèmes, le script entier peut être encodé en base64 et transmis via le paramètre -EncodedCommand. Ça va ressembler à quelque chose comme ceci:

$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 

Si OP a besoin quelque chose du script qui a couru chez l'enfant PS, et je voulais utiliser la fonction scriptblock mentionné précédemment dans cette réponse, je ne pouvais utiliser il cette approche, car cette fonctionnalité est liée au paramètre -Command.Au contraire, les nouvelles lignes devraient être supprimées et les préoccupations d'échappement pourraient entrer en jeu.

+0

Si cela démarre un autre processus dans une autre fenêtre, cela devrait fonctionner correctement pour moi. Je peux essayer aussi. Tout le monde m'a donné de bonnes options. Je rapporterai plus tard aujourd'hui. –

+0

Basé sur un test rapide, cette fonctionnalité ne fonctionnera pas si vous démarrez powershell dans une autre fenêtre. –

+0

OK. Merci pour le test rapide. Je suis allé avec start-processus et l'encodage de base 64 + sérialisation à l'origine recommandé par @ PetSerAl. Très similaire à ce que vous avez, sauf que vous appelez "PowerShell" vs start-process. Je suppose que cela aurait très bien fonctionné. –

1

Voici une variante qui utilise ScriptBlock au lieu de fichiers de script. Voir le poste par Greg Bray au 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