Как создать задачу Windows, которая активируется при событии в Powershell

Как создать задачу Windows, которая активируется при событии в Powershell

Эй, мне нужно создать задачу Windows, которая действует так же, как сценарии выхода из системы в групповой политике (в нашей среде нет AD). Задача должна запускаться от имени вошедшего в систему пользователя, чтобы сценарий мог выводить имя пользователя, запустившего ее, в файл .csv, который отслеживает время и дату выхода/входа пользователя. Я не уверен, что PowerShell — это лучший способ сделать это, но мне нужно иметь возможность создавать задачу как администратор, но при этом настроить пользователя на запуск сценария от имени текущего вошедшего в систему пользователя, кем бы он ни был в данный момент. Это будет развернуто на нескольких сотнях машин.

Если есть другой способ отслеживать выход пользователя из системы, я был бы очень заинтересован в этом методе, независимо от того, включает ли он PowerShell или нет. Ранее я пытался заставить скрипты запускаться, редактируя реестр, чтобы попытаться установить скрипты выхода из системы в GP, но это оказалось сложнее, чем я изначально ожидал. Zenworks был другим вариантом, но он запускал скрипты только ПОСЛЕ того, как пользователь выходил из системы, что приводило к тому, что скрипт выводил неверные результаты в .csv.

Для справки по ссылке ниже вы найдете человека с похожей проблемой, но без некоторых деталей, на которые мне нужно обратить внимание.

https://stackoverflow.com/questions/33390035/windows-создание-задачи-для-события-из-командной-строки

решение1

Редактировать: Значительно измененный ответ в связи с обновленным вопросом.
Это все еще плохая идея для универсального сценария выхода из системы — см.этот вопросдля объяснения причин.

Ключевой аргумент, на который вам нужно обратить внимание, это /MO. Это модификатор. Согласно документу MicrosoftSCHTASKS, когда /SCаргумент равен ONEVENT, то /MOдопускается строка запроса события XPath.

Этот блог Technet - Расширенная фильтрация XML в средстве просмотра событий Windows- описывает формат и приводит полезные примеры.

Исходя из того, что вы фактически делаете с данными, вам на самом деле не нужно выполнять в контексте выхода пользователя из системы. Это хорошо, поскольку позволяет избежать потенциального состояния гонки, присущего попытке запустить новый процесс в середине выхода из системы. Это также упрощает ваш запрос XPath:

*[System[EventID=4647]]

Все, что вам действительно нужно сделать, это убедиться, что задача, запущенная из этого события, получает сведения о событии, которое ее вызвало. Запланированные задачи хранятся в виде XML-файлов, и вы можете вручную редактировать XML, чтобы добавить дополнительные элементы XML-схемы планировщика задач, которые не отображаются через пользовательский интерфейс. Я предлагаю вам сначала создать задачу с помощью SCHTASKS или через пользовательский интерфейс со всеми значениями, которые вы можете указать. Затемэкспортчерез пользовательский интерфейс или с помощью:

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

Статья TechnetЗапуск сценария PowerShell из события Windowsпредлагает хорошее объяснение и пример того, как это использовать. Однако, ключевой атрибут, который они добавили, былValueQueries:

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

Это позволит вам ссылаться $(logoffUsername)и (при необходимости) $(eventTime)в скрипте Powershell, выполняемом задачей.

В случае, если вы хотите получить другие значения, определена XML-схема событий.на MSDN. Я также ссылалсяЗапись в блоге Майкла Альберта о передаче параметров событийдля примеров синтаксиса.

После того, как вы закончили изменять определение задачи, вы можетеИмпортироватьодин и тот же 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

Попытавшись сделать это с помощью taskscheduler, я наконец пришел к выводу, что моя первоначальная идея не будет реализована так просто и надежно, как я изначально надеялся. Отойдя от tasksched, но все еще сохраняя идею использования powershell, я создал скрипт powershell, который zenworks мог бы запустить при выходе пользователя из системы, чтобы отслеживать детали, которые я считал важными. Этот скрипт можно найти здесь (это скрипт 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
}

Связанный контент