web-dev-qa-db-ja.com

Powershellを使用してリモートマシンでGUIプログラムを起動する

同じネットワーク上に2つのWindows 7マシンがセットアップされています。
私は、winrmと通信できるように必要なすべてのものを有効にしました。

次のコマンドを実行すると:

Invoke-Command -ComputerName REMOTE-PC -ScriptBlock { Start-Process calc.exe }

正常に動作しますが、プログラムはリモートマシンでは表示されません。私の知る限り、これは予想される動作です。

プロセスが正しく開始され、セッションが終了するとすぐに閉じられると思います。ホストマシンに表示されるようにプログラムを実行するにはどうすればよいですか?

7
Trevor Hickey

設計上、実際には他の人のセッションでプロセスを起動できるとは限りません。

(明確にするために、コンピューターのデスクトップで対話的にログオンしていて、同じ資格情報を使用して同じマシンに同時に別のネットワークログオンしている場合でも、それらは2つの異なるログオンとしてカウントされますセッション)

これは単にWindows自体のセキュリティモデルに反するものであり、それを覆そうとする試みは避けられます。そのため、これを行うための簡単でサポート可能な方法を見つけることはできません。技術的には可能ですが、ローカルシステムとして実行し、ログオンしている別のユーザーのセキュリティトークンをコピーし、その代替トークンでプロセスを起動する必要があります。これにはWindows APIが必要になります。これは、Powershellがあまり得意でないことのほとんどです。詳細については、Windows APIのWTSQueryUserTokenおよびCreateProcessAsUserを参照してください。

Cheeriosで完全におしっこをしないようにするもう1つのアイデアは、プロセスを起動するスケジュールされたタスクをリモートで作成することでこれを実現できる可能性があります。詳細は http://blogs.technet.com/b/heyscriptingguy/archive/2005/09/06/how-can-i-remotely-start-an-interactive-process.aspx を参照してくださいその上で。

編集:ああ、私は忘れました... PsExecと-iパラメータはそれを行うことができます。ログオンセッションIDを指定する必要があります。そしてそれを行う権限を持っています。 PsExecがローカルシステムとして実行される一時的なサービスをインストールするという事実を利用して、私が言及したのと同じWindows APIを使用する可能性が最も高いです。

10
Ryan Ries

別の回答で述べたように、PsExecを使用してこれを機能させることができました。

  1. ダウンロード PSTools
  2. _C:\Windows\PSTools_に解凍します
  3. PATHに_C:\Windows\PSTools_を追加します
  4. RDPセッションのプロセスIDを取得します(tasklistは機能しますが、豪華なワンライナー:$session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1]
  5. プロセスの開始:_PsExec.exe -s -i 123 calc.exe_( "123"はRDPセッションID)

これは私がAnsible 2.3.0でそれをやっている方法です:

_- name: get RDP session number
  win_Shell: "{{ item }}"
  with_items:
    - $session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1]
  register: rdp_session

- name: start calc.exe
  win_Shell: PsExec.exe -s -i {{ rdp_session.results[0].stdout_lines[0] }} calc.exe
_
6

リモートマシンで「呼び出し可能なフック」を作成できます。これは、「ユーザーがログオンしたときにのみ実行する」に設定されたスケジュールタスクであり、実行時にログインするユーザーのアカウントで実行するように割り当てられています。タスクのアクションは、実行する実行可能ファイルです。

スケジュールされたタスクは、powershellまたはschtasksを介してリモートで作成でき、その後、schtasksまたはpowershellのStart-ScheduledTaskを使用して、タスク自体の「名前」によって単に呼び出されます。

  1. リモートマシンで、現在のセッションを実行しているユーザーが実行するベアボーンスケジュールタスクを作成します。
  2. 「ユーザーがログオンしているときのみ」実行するようにタスクを設定する
  3. スケジュールされたタスクの[アクション]タブの.exeまたはアイテムのファイルシステムアイテムに[管理者として実行]が設定されている場合(右クリック、プロパティ、互換性>管理者として実行)、スケジュールされたタスクを昇格して実行する必要があります。権限も同様に失敗するか、表示されません。
  4. スケジュールされたタスクの作成者は管理者権限を持っている必要があるため、リモートで呼び出されたときに、この管理者アカウントを使用してスケジュールされたタスクを呼び出すことができます。
  5. マシンがそのユーザーとしてログオンしていることを確認してください。
  6. マシンがロックされている場合にこのタスクが適切に実行されるかどうかは、アプリケーションによって異なります。私の経験ではそうです。
  7. ここから、schtasks.exeを使用して、スケジュールされたタスクの名前をホスト名とともに呼び出し、スケジュールされたタスクの作成に使用される昇格されたアカウント(ログオンするユーザーではない)の資格情報を渡すことができます。
  8. リモートマシンがStart-ScheduledTaskをサポートするPowerShellバージョンを実行している場合は、Powershellで呼び出すこともできます。

タスクを呼び出すには、指定した名前でタスクを参照します。

schtasks /run /TN "mytaskname" /s "Host" /u "user" /p "password"

スケジュールされたタスクをリモートで作成するには、Powershellのschtasks.exeまたはNew-ScheduledTaskPrincipalを使用します。タスクの実行可能ファイルまたは「アクション」アイテムにファイルシステムで「管理者として実行」のフラグが付けられている場合、そのプロパティを適切に設定するには、タスクの作成中にタスクにNew-ScheduledTaskPrincipal資格情報を添付する必要があります。

現在ログインしているユーザーを移動ターゲットにすることができますが、スケジュールされたタスクの作成自体の前に、Powershellを介してGet-LoggedOnUserでクエリできます。

そのような詳細なコードは今後48時間で公開されます。基本的な構造をすべての人が利用できるようにしたいと考えました。

4

RDPがビクターの答えでプロセスIDを取得するのを避けたい場合は、リモートマシンでquery sessionコマンドを呼び出すことで実行できます。 Powershellスクリプトのサンプル

$user = "some_user"
$password = "password of the user"
$ComputerName = "name of the remote machine"

$processId = Invoke-Command $session -ScriptBlock  {
param($user)
    $sessions = query session $user;
    return $sessions[1].split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[2];

} -ArgumentList ($user)

PsExec \\$ComputerName -u $user -p $password -h -i $processId [some executable name]  [arguments to be passed if any]
1
Ankit Aggarwal