Powershell でイベント時にアクティブ化する Windows タスクを作成する方法

Powershell でイベント時にアクティブ化する Windows タスクを作成する方法

グループ ポリシーのログオフ スクリプト (私たちの環境には AD がありません) とほぼ同じように動作する Windows タスクを作成する必要があります。タスクはログインしたユーザーとして実行する必要があります。そうすることで、スクリプトは、それを実行しているユーザーの名前を、ユーザーのログオフ/ログインの時間と日付を追跡する .csv に出力できます。PowerShell がこれを行うための絶対的な最善の方法であるかどうかはわかりませんが、管理者としてタスクを作成し、その時点でログインしているユーザー (誰であっても) としてスクリプトを実行するようにユーザーを設定する必要があります。これは数百台のマシンに展開されます。

ユーザーのログオフを追跡する別の方法があれば、PowerShell を使用するかどうかに関係なく、その方法に非常に興味があります。以前、レジストリを編集して GP でログオフ スクリプトを設定しようとしてスクリプトを実行しようとしましたが、当初予想していたよりも複雑に思えました。Zenworks も別のオプションでしたが、ユーザーがログアウトした後にのみスクリプトが実行され、スクリプトが .csv に誤った結果を出力します。

参考までに、以下のリンクには同様の問題を抱えている人がいますが、私がターゲットにする必要のある詳細の一部が欠けています。

https://stackoverflow.com/questions/33390035/windows-createing-a-task-for-an-event-from-the-command-line

答え1

編集: 質問が更新されたため、回答を大幅に修正しました。
これは、汎用のログオフスクリプトとしては依然として悪いアイデアです。この質問理由の説明については。

注目すべき重要な引数は です/MO。これは修飾子です。Microsoftのドキュメントによると、タスク/SC引数がの場合ONEVENT/MOXPath イベント クエリ文字列が許可されます。

このTechnetブログ - Windows イベント ビューアーでの高度な XML フィルタリング- フォーマットについて説明し、役立つ例を示します。

データで実際に行っていることを踏まえると、ユーザーがログオフするコンテキスト内で実行する必要はありません。これは、ログオフ中に新しいプロセスを起動しようとする場合に発生する潜在的な競合状態を回避するため、良いことです。また、XPath クエリも簡素化されます。

*[System[EventID=4647]]

本当に必要なのは、このイベントから起動されたタスクが、それをトリガーしたイベントの詳細を受け取るようにすることです。スケジュールされたタスクは xml ファイルとして保存され、手動で XML を編集して、UI を通じて公開されていないタスク スケジューラ XML スキーマの要素を追加できます。まず、SCHTASKS または UI を通じて、指定できるすべての値を使用してタスクを作成することをお勧めします。次に、輸出UI または次の方法で実行します。

schtasks /Query [/S <system> [/U <username> [/P [<password>]]]] `
/XML /TN <taskname> >event.xml

Technetの記事Windows イベントから PowerShell スクリプトをトリガーするは、これをどのように使用するかについての優れた説明と例を提供しています。しかし、彼らが追加した重要な属性は値クエリ:

<ValueQueries>
    <Value name="eventChannel">Event/System/Channel</Value>
    <Value name="eventRecordID">Event/System/EventRecordID</Value>
    <Value name="eventSeverity">Event/System/Level</Value>
</ValueQueries>

デモで示したように、EventRecordID を使用してイベントをクエリするのではなく、必要な限られたデータを直接取得できるはずです。

<Value name="logoffUsername">Event/EventData/Data[@Name='TargetUserName']</Value>

スクリプト内から呼び出される時間関数よりも確実に正確であるため、TimeCreated を取り込むこともできます。

<Value name="eventTime">Event/System/TimeCreated/@SystemTime</Value>

これにより、タスクによって実行される Powershell スクリプト内で参照$(logoffUsername)(オプション)できるようになります。$(eventTime)

他の値を取得したい場合は、イベントXMLスキーマが定義されていますMSDNで. また、イベントパラメータの受け渡しに関する Michael Albert のブログ投稿構文例については、こちらをご覧ください。

タスク定義の変更が完了したら、輸入すべてのコンピューターで同じ XML:

schtasks /Create [/S <system> [/U <username> [/P [<password>]]]]

/XML /TN

注: 元の質問では、そのユーザーのコンテキストでそのユーザーのイベントを作成し、そのユーザーがログオフしたときにのみそれを実行するよう求めていました。これは、前述の潜在的な競合状態のため、良い考えではありません。参考までに、これが提案された解決策です。

$cred = Get-Credential
$password = $cred.GetNetworkCredential().Password
$command = <your command>
$myQuery = "*[System[EventID=4647] and EventData[Data [@Name='TargetUserName'] = '" + $env:username + "']]"

SCHTASKS /Create /TN "Logoff Monitor" /TR $command /SC ONEVENT `
/RL Highest /RU $cred.Username /RP $password `
/EC ScriptEvents /MO $myQuery

答え2

これをタスクスケジューラで動作させようとした後、最終的に、当初の考えは当初期待していたほどシンプルかつ確実には実装されないという結論に達しました。タスクスケジューラから離れましたが、PowerShell を使用するという考えはそのままにして、関連すると思われる詳細を追跡するために、zenworks がユーザー ログオフ時に実行できる PowerShell スクリプトを作成しました。そのスクリプトはここにあります (互換性の問題により、バッチ ファイルで実行される PowerShell スクリプトです)。

@@:: This prolog allows a PowerShell script to be embedded in a .CMD file.
@@:: Any non-PowerShell content must be preceeded by "@@"
@@setlocal
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([char]10,$((Get-Content '%~f0') -notmatch '^^@@'))) & goto :EOF

if( -Not (Test-Path -Path C:\Windows\Logs\$env:computername.csv ) )
{
New-Item -Path C:\Windows\Logs\ -name "$env:computername.csv" -itemtype "file"
}

$events = Get-WinEvent -FilterHashtable @{LogName='Security'; ID=4647} -MaxEvents 1
foreach ($event in $events)
{
    #Writes events to XML file
    $eventXML = [xml]$event.ToXml()

    #Converts Datetime of event into Two entries
    $DateTime = '{0:MM/dd/yyyy hh:mm:ss}' -f ($event.timecreated)
    $date = ($DateTime -split ' ')[0]
    $time = ($DateTime -split ' ')[1]

    #Formats Outputs of data to prep for export
    $NewLine = "{0},{1},{2},{3},{4}" -f 
    "Logoff",
    $env:computername,
    $eventXML.Event.EventData.Data[1].'#text',
    $Date,
    $Time

    #Exports Data to .csv
    $NewLine | add-content -path C:\Windows\Logs\$env:computername.csv
}

関連情報