web-dev-qa-db-ja.com

パラメーターと資格情報を使用してPowerShellから.ps1スクリプトを開始し、変数を使用して出力を取得する

こんにちはスタックコミュニティ:)

単純な目標があります。別のPowershellスクリプトからPowerShellスクリプトを開始したいのですが、3つの条件があります。

  1. 資格情報を渡す必要があります(実行は特定のユーザーがいるデータベースに接続します)
  2. それはいくつかのパラメータを取る必要があります
  3. 出力を変数に渡したい

同様の質問 リンク があります。しかし、答えは、2つのPSスクリプト間で通信する方法としてファイルを使用することです。アクセスの競合を回避したいだけです。 @更新:メインスクリプトは他のいくつかのスクリプトを開始します。そのため、複数のユーザーから同時に実行される場合、ファイルを使用したソリューションは扱いにくい場合があります。

Script1.ps1は、出力として文字列が必要なスクリプトです。 (明確にするために、それは架空のスクリプトであり、実際のスクリプトは150行なので、例を作りたかっただけです)

param(  
[String]$DeviceName
)
#Some code that needs special credentials
$a = "Device is: " + $DeviceName
$a

ExecuteScripts.ps1は、上記の3つの条件で呼び出します。

複数の解決策を試しました。これの例:

$arguments = "C:\..\script1.ps1" + " -ClientName" + $DeviceName
$output = Start-Process powershell -ArgumentList $arguments -Credential $credentials
$output 

そこから何の出力も得られず、スクリプトを単に呼び出すことはできません

&C:\..\script1.ps1 -ClientName PCPC

-Credentialパラメータを渡すことができないからです。

前もって感謝します!

10
Dmytro

注意:

  • 次の解決策any外部プログラムで動作し、captures常にtextとして出力します。

  • Toinvoke別のPowerShellインスタンスおよびその出力リッチオブジェクトとして(制限付き)、下部のセクションのバリアントソリューションを参照するか、検討してください Mathias R. Jessenの役立つ回答PowerShell SDK を使用します。

System.Diagnostics.ProcessSystem.Diagnostics.ProcessStartInfo .NETタイプの直接使用に基づく概念実証は次のとおりですメモリ内のプロセス出力をキャプチャする(質問に記載されているように、Start-Processは出力のキャプチャのみをサポートしているため、オプションではありませんfilesthis answer )のように:

注意:

  • 別のユーザーとして実行しているため、これはWindowsのみ(.NET Core 3.1以降)でサポートされていますが、両方のPowerShellエディションでサポートされています。

  • 別のユーザーとして実行する必要があり、出力をキャプチャする必要があるため、.WindowStyleを使用してコマンドを実行することはできませんhidden.WindowStyleを使用するため、.UseShellExecute$trueになり、これらの要件と互換性がありません);ただし、すべての出力がcapturedであるため、.CreateNoNewWindow$trueに設定すると、実質的に非表示の実行になります。

# Get the target user's name and password.
$cred = Get-Credential

# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
  # For demo purposes, use a simple `cmd.exe` command that echoes the username. 
  # See the bottom section for a call to `powershell.exe`.
  FileName = 'cmd.exe'
  Arguments = '/c echo %USERNAME%'
  # Set this to a directory that the target user
  # is permitted to access.
  WorkingDirectory = 'C:\'                                                                   #'
  # Ask that output be captured in the
  # .StandardOutput / .StandardError properties of
  # the Process object created later.
  UseShellExecute = $false # must be $false
  RedirectStandardOutput = $true
  RedirectStandardError = $true
  # Uncomment this line if you want the process to run effectively hidden.
  #   CreateNoNewWindow = $true
  # Specify the user identity.
  # Note: If you specify a UPN in .UserName
  # ([email protected]), set .Domain to $null
  Domain = $env:USERDOMAIN
  UserName = $cred.UserName
  Password = $cred.Password
}

# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)

# Read the captured standard output.
# By reading to the *end*, this implicitly waits for (near) termination
# of the process.
# Do NOT use $ps.WaitForExit() first, as that can result in a deadlock.
$stdout = $ps.StandardOutput.ReadToEnd()

# Uncomment the following lines to report the process' exit code.
#   $ps.WaitForExit()
#   "Process exit code: $($ps.ExitCode)"

"Running ``cmd /c echo %USERNAME%`` as user $($cred.UserName) yielded:"
$stdout

上記の結果は次のようになり、指定されたユーザーIDでプロセスが正常に実行されたことを示しています。

Running `cmd /c echo %USERNAME%` as user jdoe yielded:
jdoe

別のPowerShellinstanceを呼び出しているので、PowerShell CLI の出力をCLIXML形式で表す機能を利用します。これにより、出力をリッチオブジェクトに逆シリアル化できますが、型が制限されます この関連回答 で説明されているように、fidelity

# Get the target user's name and password.
$cred = Get-Credential

# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
  # Invoke the PowerShell CLI with a simple sample command
  # that calls `Get-Date` to output the current date as a [datetime] instance.
  FileName = 'powershell.exe'
  # `-of xml` asks that the output be returned as CLIXML,
  # a serialization format that allows deserialization into
  # rich objects.
  Arguments = '-of xml -noprofile -c Get-Date'
  # Set this to a directory that the target user
  # is permitted to access.
  WorkingDirectory = 'C:\'                                                                   #'
  # Ask that output be captured in the
  # .StandardOutput / .StandardError properties of
  # the Process object created later.
  UseShellExecute = $false # must be $false
  RedirectStandardOutput = $true
  RedirectStandardError = $true
  # Uncomment this line if you want the process to run effectively hidden.
  #   CreateNoNewWindow = $true
  # Specify the user identity.
  # Note: If you specify a UPN in .UserName
  # ([email protected]), set .Domain to $null
  Domain = $env:USERDOMAIN
  UserName = $cred.UserName
  Password = $cred.Password
}

# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)

# Read the captured standard output, in CLIXML format,
# stripping the `#` comment line at the top (`#< CLIXML`)
# which the deserializer doesn't know how to handle.
$stdoutCliXml = $ps.StandardOutput.ReadToEnd() -replace '^#.*\r?\n'

# Uncomment the following lines to report the process' exit code.
#   $ps.WaitForExit()
#   "Process exit code: $($ps.ExitCode)"

# Use PowerShell's deserialization API to 
# "rehydrate" the objects.
$stdoutObjects = [Management.Automation.PSSerializer]::Deserialize($stdoutCliXml)

"Running ``Get-Date`` as user $($cred.UserName) yielded:"
$stdoutObjects
"`nas data type:"
$stdoutObjects.GetType().FullName

上記は次のようなものを出力し、[datetime]によって出力されたSystem.DateTimeインスタンス( Get-Date )がそのようにデシリアライズされたことを示しています。

Running `Get-Date` as user jdoe yielded:

Friday, March 27, 2020 6:26:49 PM

as data type:
System.DateTime
2
mklement0

パラメータをps1スクリプトに渡すには、次のことを実行できます。

最初のスクリプトはOrigin.ps1にすることができます。

& C:\scripts\dest.ps1 Pa$$w0rd parameter_a parameter_n

宛先スクリプトdest.ps1には、変数を取得する次のコードを含めることができます

$var0 = $args[0]
$var1 = $args[1]
$var2 = $args[2]
Write-Host "my args",$var0,",",$var1,",",$var2

そして結果は

my args Pa$$w0rd, parameter_a, parameter_n
0
Andy McRae

rcv.ps1

param(
    $username,
    $password
)

"The user is:  $username"
"My super secret password is:  $password"

別のスクリプトからの実行:

.\rcv.ps1 'user' 'supersecretpassword'

出力:

The user is:  user
My super secret password is:  supersecretpassword
0
thepip3r