В этой статье мы рассмотрим особенности использования командлета Invoke-Command для удаленного выполнения команд и скриптов. Возможно запускать команды удаленно на одном компьютере, или параллельно на множестве компьютерах в вашей сети. Командлет Invoke-Command использует возможности удаленного управления, заложенные в PowerShell Remoting. PowerShell Remoting позволяет удаленно подключаться к PowerShell сессиям на компьютерах через службу WinRM (Windows Remote Management) через протокол Web Services for Management (WS-Management). Этот сервис дает возможность принимать команды Powershell и устанавливать сеансы.
В Powershell есть командлет, похожий на grep в Linux, Select-String. С помощью него мы можем искать как файлы, так и вхождения строк и, по желанию, используя регулярные выражения.
Каждый раз, когда вы устанавливаете или обновляете драйвер устройства, Windows продолжает хранить старую версию драйвера на диске в системном хранилище драйверов (Driver Store). Благодаря этому, если система работает некорректно с новым драйвером или возникает какой-нибудь конфликт, пользователь в любой момент может откатить текущую версию драйвер и заменить его более старой версий. На диске хранилище драйверов расположено в папке C:\windows\system32\DriverStore\FileRepository. Однако, многие пользователи Windows замечают, что со временем размер каталога %windir%\system32\DriverStore\FileRepository начинает занимать все больше и больше места на системном диске. В моем случае размер этого каталога на диске составляет почти 11,5 Гб. Это довольно много, особенно для моего не слишком большого SSD диска.
Чтобы очистить хранилище драйверов от старых и неиспользуемых драйверов, я нашел небольшой PowerShell скрипт, который позволяет удалить все версии одного и того же драйвера в хранилище (по сути это частичные дубли), сохранив в системе только самую свежую версию.
Внимание! Ни в коем случае не нужно вручную удалять папки или файлы из каталога FileRepository!
Важные советы.
Для запуска данного скрипта я использовал версию PowerShell, устанавливаемая вместе с Windows Management Framework 5.1 (нужен как минимум WMF4)
Перед запуском скрипта убедитесь, у вас под рукой имеются все драйвера для важных компонентов системы (видеокарта, чипсет, звук и т.д.). В случае чего вы сможете с помощью них быстро переустановить драйвер и задействовать проблемное оборудование.
Также желательно предварительно создать точку восстановления системы. Для этого воспользуйтесь командой:
. Проверить успешность создания точки восстановления можно с помощью команды
1
Get-ComputerRestorePoint
Список всех сторонних (не Microsoft) драйверов, которые установлены в системе можно получить с помощью утилиты DISM:
1
dism/online/get-drivers
Вывод данной команды нужно будет распарсить с помощью PowerShell, найти дубликаты одного и того же драйвера и отсортировать их по дате. Из итогового списка нужно исключить самую последнюю (актуальную) версию каждого драйвера.
Затем можно удалить драйверы, оставшиеся в списке с помощью утилиты pnputil. В некоторых случаях, если драйвер не удается удалить, нужно воспользоваться принудительным удалением с помощью ключа -f.
Сам скрипт выглядит следующим образом:
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
$dismOutput=dism/online/get-drivers
$Lines=$dismOutput|select-Skip 10
$Tbls="theName"
$Drivers=@()
foreach($Linein$Lines){
$tmp=$Line
$txt=$($tmp.Split(':'))[1]
switch($Tbls){
'theName'{
$Name=$txt
$Tbls='theFileName'
break
}
'theFileName'{
$FileName=$txt.Trim()
$Tbls='theEntr'
break
}
'theEntr'{
$Entr=$txt.Trim()
$Tbls='theClassName'
break
}
'theClassName'{
$ClassName=$txt.Trim()
$Tbls='theVendor'
break
}
'theVendor'{
$Vendor=$txt.Trim()
$Tbls='theDate'
break
}
'theDate'{
# для более удобной сортировки сменим формат данных
Совет. Если вы хотите только вывести список неиспользуемых драйверов на экран, закомментируйте строку с командой Invoke-Expression (поставьте перед ней символ #).
Скопируйте указанный код и сохраните его в текстовый файл cleanupdriver.ps1. Откройте командную строку PowerShell с правами администратора и разрешите выполнение неподписанных скриптов:
Скрипт удалит неиспользуемые драйвера. После его завершения, перезагрузите компьютер и убедитесь, что все работает нормально (если нет – переустановите драйвера устройств, которые работают некорректно).
В моем случае (с момента установки системы прошло чуть больше 2 лет) размер каталога FileRepository уменьшился с 11,5 до 1.5 Гб. Почти в 8 раз, неплохо!
Включение PowerShell Remoting при помощи групповой политики позволит получить удаленно доступ используя PowerShell ко всем клиентам и управлять системой как если бы вы находились за консолью локально.
Очень часто нам приходится совершать некую операцию над группой объектов — файлов, процессов, текстовых строк, и т.д. Делать это в PowerShell можно разными способами. Например For:
1
2
3
4
5
6
7
8
9
10
11
12
# Получаем объекты представляющие файлы в переменную $files
$Files=dirC:\root\test
# С помощью for:
# 1) объявляем переменную $i, и присваиваем ей значение 0
# 2) указываем условие повторения цикла($i меньше количества файлов)
# 3) указываем что после каждого выполнения цикла надо увеличить $i на единицу
for($i=0;$i-lt$files.Count;$i++)
{
# Пока условие удовлетворяется, вызываем утилиту md5.exe, передавая ей в
# качестве аргумента элемент массива файлов под номером $i (отсчет идет с 0)
Технология Windows PowerShell позволяет не только выполнять команды администрирования, но и полноценно программировать алгоритмы на языке PowerShell. Для этого в языке PowerShell существуют атрибуты, присущие всем современным языкам программирования такие как: переменные, условия, циклы, массивы, обработка ошибок и все это можно использовать для написания сложных сценариев администрирования.
Язык PowerShell – это объектно-ориентированный скриптовой язык программирования, который является частью технологии Windows PowerShell. Синтаксис всех программных конструкций языка PowerShell схож с синтаксисом СИ подобных языков.
В данной статье мы рассмотрим такую технологию от компании Microsoft как Windows PowerShell, мы поговорим о том, что такое PowerShell, что такое командлеты и конвейер, как писать сценарии и модули, а также затронем другие не менее важные и полезные возможности Windows PowerShell.
Что способствовало появлению Windows PowerShell?
До появления PowerShell существовали (и существуют) следующие инструменты для автоматизации и администрирования сервисов: командная строка Windows и Windows Script Host. Но у этих технологий есть недостатки.
Командлетов в Windows PowerShell много поэтому сегодня я предлагаю рассмотреть небольшой список наиболее полезных и часто используемых командлетов с кратким описанием того, что они умеют. Данный справочник поможет Вам быстрей найти интересующую Вас команду Windows PowerShell и ориентирован он на начинающих системных администраторов.
Когда наше файловое хранилище разменяло третий терабайт, все чаще наш отдел стал получать просьбы выяснить, кто удалил важный документ или целую папку с документами. Нередко это происходит по чьему-то злому умыслу. Бэкапы — это хорошо, но страна должна знать своих героев. А молоко вдвойне вкусней, когда мы можем написать его на PowerShell.
Пока разбирался, решил записать для коллег по цеху, а потом подумал, что может пригодиться кому-то еще. Материал получился смешанный. Кто-то найдет для себя готовое решение, кому-то пригодятся несколько неочевидные методы работы с PowerShell или планировщиком задач, а кто-то проверит на быстродействие свои скрипты.
Во-первых, время генерации отчета за четыре часа на 2-терабайтном хранилище, с которым одновременно работает около 200 человек, составило около пяти минут. И это притом, что лишнего у нас в логи не пишется. Это меньше, чем у Deks, но больше, чем хотелось бы, потому что…
Во-вторых, все то же самое нужно было реализовать еще на двадцати серверах, гораздо менее производительных, чем основной.
В-третьих, вызывал вопросы график запуска генерации отчетов.
И в-четвертых, хотелось исключить себя из процесса доставки собранной информации конечным потребителям (читай: автоматизировать, чтобы мне с этим вопросом больше не звонили).
Краткий дискурс: При включенном аудите файловой системы в момент удаления файла в журнале безопасности создаются два события, с кодами 4663 и, следом, 4660. Первое записывает попытку запроса доступа на удаление, данные о пользователе и пути к удаляемому файлу, а второе — фиксирует свершившийся факт удаления. У событий есть уникальный идентификатор EventRecordID, который отличается на единицу у этих двух событий.
Ниже приведен исходный скрипт, собирающий информацию об удаленных файлах и пользователях, их удаливших.
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$time=(get-date)-(new-timespan-min 240)
$Events=Get-WinEvent-FilterHashtable@{LogName="Security";ID=4660;StartTime=$time}|SelectTimeCreated,@{n="Запись";e={([xml]$_.ToXml()).Event.System.EventRecordID}}|sort Запись
Получилось уменьшить этажность пайпа и убрать перечисления Foreach, а заодно сделать код более читаемым, но большого эффекта это не дало, разница в пределах погрешности:
Пришлось немного подумать головой. Какие операции занимают больше всего времени? Можно было бы натыкать еще десяток Measure-Command, но в общем-то в данном случае и так очевидно, что больше всего времени тратится на запросы в журнал (это не самая быстрая процедура даже в MMC) и на повторяющиеся конвертации в XML (к тому же, в случае с EventRecordID это и вовсе необязательно). Попробуем сделать и то и другое по одному разу, а заодно исключить промежуточные переменные:
А вот это уже результат. Ускорение практически в два раза!
Автоматизируем
Порадовались, и хватит. Три минуты — это лучше, чем пять, но как лучше всего запускать скрипт? Раз в час? Так могут ускользнуть записи, которые появляются одновременно с запуском скрипта. Делать запрос не за час, а за 65 минут? Тогда записи могут повторяться. Да и искать потом запись о нужном файле среди тысячи логов — мутор. Писать раз в сутки? Ротация логов забудет половину. Нужно что-то более надежное. В комментариях к статье Deks кто-то говорил о приложении на дотнете, работающем в режиме службы, но это, знаете, из разряда «There are 14 competing standards»…
В планировщике заданий Windows можно создать триггер на событие в системном журнале. Вот так:
Отлично! Скрипт будет запускаться ровно в момент удаления файла, и наш журнал будет создаватья в реальном времени! Но наша радость будет неполной, если мы не сможем определить, какое событие нам нужно записать в момент запуска. Нам нужна хитрость. Их есть у нас! Недолгий гуглинг показал, что по триггеру «Событие» планировщик может передавать исполняемому файлу информацию о событии. Но делается это, мягко говоря, неочевидно. Последовательность действий такая:
Создать задачу с триггером типа «Event»;
Экспортировать задачу в формат XML (через консоль MMC);
Добавить в ветку «EventTrigger» новую ветвь «ValueQueries» с элементами, описывающими переменные:
где «eventRecordID» — название переменной, которую можно будет передать скрипту, а «Event/System/EventRecordID» — элемент схемы журнала Windows, с которой можно ознакомиться по ссылке внизу статьи. В данном случае это элемент с уникальным номером события.
Импортировать задание обратно в планировщик.
Но мы же не хотим натыкивать все это мышкой на 20 серверах, верно? Нужно автоматизировать. К сожалению, PowerShell не всесилен, и командлет New-ScheduledTaskTrigger пока что не умеет создавать триггеры типа Event. Поэтому применим чит-код и создадим задачу через COM-объект (пока что достаточно часто приходится прибегать к COM, хотя штатные командлеты умеют все больше и больше c каждой новой версией PS):
1
2
3
4
$scheduler=New-Object-ComObject"Schedule.Service"
$scheduler.Connect("localhost")
$rootFolder=$scheduler.GetFolder("\")
$taskDefinition=$scheduler.NewTask(0)
Нужно обязательно разрешить одновременный запуск нескольких экземпляров, а также, как мне кажется, стоит запретить ручной запуск и задать лимит времени выполнения:
Создадим триггер типа 0 (Event). Далее задаем XML-запрос для получения нужных нам событий. Код XML-запроса можно получить в консоли MMC «Журнал событий», выбрав необходимые параметры и переключившись на вкладку «XML»:
1
2
3
4
5
6
7
8
$Trigger=$taskDefinition.Triggers.Create(0)
$Trigger.Subscription='<QueryList>
<Query Id="0" Path="Security">
<Select Path="Security">
*[System[Provider[@Name="Microsoft-Windows-Security-Auditing"] and EventID=4660]]
</Select>
</Query>
</QueryList>'
Главная хитрость: указываем переменную, которую нужно передать скрипту.
Вернемся к скрипту для записи логов. Теперь нам не надо получать все события, а нужно доставать одно-единственное, да еще переданное в качестве аргумента. Для этого мы допишем заголовки, превращающие скрипт в командлет с параметрами. До кучи — сделаем возможным изменять путь к логу «на лету», авось, пригодится:
Дальше возникает нюанс: до сего момента мы получали события командлетом Get-WinEvent и фильтровали параметром -FilterHashtable. Он понимает ограниченный набор атрибутов, в который не входит EventRecordID. Поэтому фильтровать мы будем через параметр -FilterXml, мы же теперь и это умеем!
1
2
3
4
5
6
7
$XmlQuery="<QueryList>
<Query Id='0' Path='Security'>
<Select Path='Security'>*[System[(EventID=4663) and (EventRecordID=$($RecordID - 1))]]</Select>
Теперь нам больше не нужно перечисление Foreach-Object, поскольку обрабатывается всего одно событие. Не два, потому что событие с кодом 4660 используется только для того, чтобы инициировать скрипт, полезной информации оно в себе не несет.
Помните, в начале я хотел, чтобы пользователи могли без моего участия узнатьзлодея? Так вот, в случае, если файл удален в папке документов какого-либо отдела — пишем лог также в корень папки отдела.
Осталось поместить скрипт в удобное для вас место и запустить с ключом -Install.
Теперь сотрудники любого отдела могут в реальном времени видеть, кто, что и когда удалил из их каталогов. Отмечу, что я не стал рассматривать здесь права доступа к файлам логов (чтобы злодей не мог их удалить) и ротацию. Структура и права доступа к каталогам на нашем файлере тянут на отдельную статью, а ротация в какой-то степени усложнит поиск нужной строки.