Глава 15. Управление с помощью Windows Management Instrumentation

В данной главе вы рассмотрите следующие рецепты:

  • Изучение WMI в Windows

  • Изучение пространств имён WMI

  • Изучение классов WMI

  • Получение локальных и удалённых объектов WMI

  • Применение методов WMI

  • Управление событиями WMI

  • Реализацию постоянной обработки событий WMI

Введение

Windows Management Instrumentation (WMI, Инструментарий управления Windows) это компонент Windows, который вы можете применять в помощь управления системами Windows. WMI это частная реализация Microsoft имеющегося стандарта Web-Based Enterprise Management (WBEM, Основанного на веб управления корпорацией). WBEM это открытый стандарт, обнародованный Distributed Management Task Force (DMTF), которая имеет целью унификацию необходимого управления для распределённых вычислительных сред с применением стандартных технологий Интернет.

Помимо WMI для Windows, существуют и иные реализации WBEM, включая OpenWBEM. Дополнительно про DMTF и WBEM вы можете прочесть в https://www.dmtf.org/about/faq/wbem_faq, ознакомиться с OpenWBEM в http://openwbem.sourceforge.net/.

Впервые Microsoft представил WMI в качестве дополнительного компонента для Windows NT 4. Затем они интегрировали WMI в качестве существенного компонента своего клиента Windows, начиная с Windows XP и далее и в Windows Server начиная с версии Windows Server 2000. Впоследствии несколько специализированных команд внутри группы Windows активно применяли WMI. Например, как хранилище, так и сетевые стеки внутри Windows применяют WMI. Многие командлеты, например, Get-NetAdapter, непосредственно основаны на WMI.

Имевшиеся стандарты WBEM изначально определяли что все компоненты WMI взаимодействуют при помощи HTTP. Для улучшения производительности вместо этого реализовал взаимодействие компонентов WMI с использованием Common Object Model (COM), которая на тот момент времени была популярной технологией. WMI сам по себе остаётся реализованным на COM. Для доступа к функциональным возможностям WMI PowerShell пользуется .NET.

Windows PowerShell 1.0 поставлялся с неким множеством командлетов, которые могли применять доступ к WMI. Это были базовые командлеты, которые работали во всех последующих версиях Windows PowerShell. Однако для этих командлетов не существует никакой непосредственной поддержки в PowerShell 7. Вы можете активировать более старые сценарии на основе командлетов WMI при помощи удалённого исполнения PowerShell, если вы действительно в них нуждаетесь.

Начиная с V3 PowerShell Microsoft проделал значительную работу с WMI, приведшую в результате к некому новому модулю, CimCmdlets. Имелось несколько веских причин как для нового модуля, так и для связанных с ним обновлений некоторых внутренних компонентов WMI в помощь разработчикам. В этой главе для доступа к к функциональным возможностям WMI вы пользуетесь модулем CimCmdlets. Почему соответствующая команда приняла решения собирать новые командлеты вы можете прочесть в посте блога в https://devblogs.microsoft.com/powershell/introduction-to-cim-cmdlets/. Если у вас имеются сценарии, которые пользуются более старыми командлетами WMI, рассмотрите возможность обновления их на применение вместо этого более позднего модуля CimCmdlets. Эти новые командлеты быстрее, что всегда даёт приятные преимущества.

Архитектура WMI

WMI это сложный предмет. Прежде чем мы рассмотрим CimCmdlets и что мы с ним можем делать, полезно разобраться с собственно архитектурой WMI внутри Windows. Сама архитектура WMI одна и та же в Windows 10 и Windows Server 2022. Наша следующая схема отображает концептуальную архитектуру WMI:

 

Рисунок 15-01


Архитектура WMI

Будучи профессионалом в ИТ для доступа к WMI вы пользуетесь модулем командлетов CimCmdlets. Эти командлеты применяют .NET через транспортный протокол для взаимодействия с ядром WMI и диспетчером объекта CIM в неком локальном или удалённом хосте. Имеющиеся транспортные протоколы включают в своём составе DCOM и WSMan. Центральные компоненты WMI, в частности Диспетчер объекта CIM (CIMOM - CIM Object Manager), это компоненты COM, которые вы можете обнаружить в каждом хосте Windows.

Сам CIMOM хранит информацию в репозитории WMI, порой именуемом как Common Information Model (CIM) или База данных CIM. Эта база, по- существу, некое подмножество базы данных ANSI- SQL. Соответствующие командлеты CIM позволяют вам выполнять доступ к имеющимся внутри этой базы данных сведениям. Такая база данных CIM организует имеющиеся сведения в пространствах имён классов .NET, также носящих название пространств имён для организации классов. Тем не менее, классы .NET содержат и собственно название пространства имён в качестве части названия самого класса. Например, вы можете создать при помощи класса .NET вы можете создать некое новое сообщение электронной почты, System.Net.Mail.Mailmessage, где собственно пространством имён выступает System.Net.Mail. Тем самым, само пространство имён содержится в значении полного названия класса. При помощи WMI названия пространств имён и названия классов разделяются и поддерживаются в имеющихся командлетах CIM при помощи различных параметров.

Классы WMI содержат экземпляры данных, которые хранят соответствующие сведения об управлении. Например, пространство имён Root\CimV2 содержит класс Win32_Share. Всякий экземпляр внутри этого класса представляет один из совместных ресурсов SMB внутри вашего хоста. При помощи PowerShell вы можете обычным образом применять имеющиеся командлеты SMB для управления совместными ресурсами SMB. Существуют полезные сведения, содержащиеся в прочих классах WMI для которых не имеется никакой поддержки командлетами.

Когда вы выполняете выборку экземпляров, скажем, соответствующего класса Win32_Shar, .NET получает необходимые сведения об экземпляре и возвращает их в некой оболочке .NET объекта. Строго говоря, какой- то экземпляр объекта WMI это некий экземпляр специализированного класса .NET с возвращаемыми в WMI данными. По этой причине вы воспринимаете объекты WMI применяющими те же самые методы, которые вы используете с прочими объектами .NET.

Многие классы WMI обладают методами, которые вы можете активировать, и которые способны выполнять некоторые операции либо в каком- то заданном экземпляре WMI, или статически основанном на этом классе. Наш класс Win32_Share, например, обладает статическим методом Create(), который вы можете применять для создания какого- то нового совместного ресурса. Каждый экземпляр этого класса обладает динамическим методом Delete(), который удаляет соответствующий совместный ресурс SMB.

Некой важной архитектурной функциональной возможностью WMI выступает поставщик WMI. Поставщик WMI, это некое добавление к WMI, которое реализует классы WMI внутри некого заданного хоста. Такой поставщик WMI, например, реализует сотни классов WMI, включая Win32_Share и Win32_Bios. Некий поставщик к тому же реализует методы класса и события класса. К примеру, соответствующий поставщик Win32 отвечает за выполнение как указанного метода Delete() для удаления совместного ресурса SMB и обозначенного метода Create() для создания некого нового совместного ресурса SMB.

В промышленном решении, скорее всего, для управления совместными ресурсами SMB вы будете использовать командлеты SMB и маловероятно что вы воспользуетесь напрямую WMI. Поскольку общие ресурсы должны быть хорошо знакомы, они представляют собой отличный пример, который поможет вам лучше разобраться с WMI и рецепты этой главы пользуются этим классом.

WMI и поставщики WMI предлагают богатую систему событий. Классы WMI и поставщиков WMI могут реализовывать события, на которые вы способны подписываться. Когда происходит соответствующее событие, имеющаяся система событий уведомляет вас, и вы можете предпринимать некое действие для обработки такого произошедшего события. Например, вы можете выполнить регистрацию для какого- то события WMI, которое происходит когда изменяет имеющееся членство в некой группе AD. Когда такое случается, обработка событий WMI позволяет вам предпринимать какие- то действия, например, отправить электронное письмо администратору безопасности, информируя его об изменении такого членства в группе. WMI также реализует постоянную обработку событий. Эта функциональная возможность позволяет вам настраивать WMI для отлавливания и обработки событий без исполнения какого бы то ни было активного сеанса PowerShell. Постоянные события способны также переживать перезапуск своего хоста, что чрезвычайно действенно в средах с отключением электропитания.

[Совет]Совет

Существует множество подробностей относительно WMI, которые можно уместить в этой главе. Для получения дополнительных сведений относительно WMI и того как вы можете взаимодействовать с ним, проконсультируйтесь с книгой Ричарда Сиддэвея PowerShell и WMI (© Manning, Aug 2012). Книга Ричарда углубляется в отличные подробности относительно WMI, однако все примеры кода пользуются более старыми командлетами WMI. Вы должны быть способны перевести эти примеры на применение командлетов CIM. Ключевую ценность этой книги составляет собственно обсуждение функциональные возможности WMI и того как они работают. Эти базовые функциональные возможности WMI не претерпели значительных изменений с даты публикации этой книги.

Исследование WMI в Windows

Windows устанавливает WMI в процессе установки самой ОС. Такой процесс установки помещает большинство компонентов WMI, включая репозиторий, инструменты и необходимых поставщиков, в папку C:\Windows\System32\WBEM.

Внутри запущенного хоста Windows WMI исполняется в качестве службы, службы winmgmt (winmgmt.exe). Windows запускает эту службу внутри процесса совместных служб (svchost.exe). В своих более ранних версиях WMI в Windows, WMI загружал всех необходимых поставщиков WMI в такую службу winmgmt. Подобная функциональность единого поставщика может вызывать отказ службы WMI целиком. Позднее, в Windows XP и последующих версиях, Microsoft улучшил WMI загрузкой поставщиков в отдельном процессе, WmiPrvSE.exe.

В данном рецепте вы изучите имеющееся содержимое в папке WBEM, саму службу WMI и компоненты времени исполнения WMI.

Подготовка

Этот рецепт пользуется SRV1, Присоединённым к домену хостом. Вы уже установили в этом хосте PowerShell 7 и VS Code.

Как это сделать...

  1. Просматриваем содержимое папки WBEM

    
    $WBEMFOLDER = "$Env:windir\system32\wbem"
    Get-ChildItem -Path $WBEMFOLDER |
      Select-Object -First 20
    		
  2. Просматриваем папку репозитория WMI

    
    Get-ChildItem -Path $WBEMFOLDER\Repository
    		
  3. Просматриваем подробности своей службы WMI

    
    Get-Service -Name Winmgmt  | 
      Format-List -Property *
    		
  4. Получаем подробности процесса

    
    $S = tasklist.exe /svc /fi "SERVICES eq winmgmt" |
           Select-Object -Last 1
    $P = [int] ($S.Substring(30,4))
    Get-Process -Id $P
    		
  5. Изучаем загружаемые нашей службой WMI DLL

    
    Get-Process -Id $P | 
      Select-Object -ExpandProperty modules | 
        Where-Object ModuleName -match 'wmi' |
          Format-Table -Property FileName, Description, FileVersion
    		
  6. Выявляем поставщиков WMI

    
    Get-ChildItem -Path $WBEMFOLDER\*.dll | 
      Select-Object -ExpandProperty Versioninfo | 
        Where-Object FileDescription -match 'prov' |
          Format-Table -Property Internalname, 
                                 FileDescription, 
                                 ProductVersion
    		
  7. Изучаем процесс WmiPrvSE

    
    Get-Process -Name WmiPrvSE
    		
  8. Обнаруживаем журнал событий WMI

    
    $Log = Get-WinEvent -ListLog *wmi*
    $Log
    		
  9. Просматриваем типы имеющихся событий в выявленном журнале WMI

    
    $Events = Get-WinEvent -LogName $Log.LogName
    $Events | Group-Object -Property LevelDisplayName
    		
  10. Изучаем записи своего журнала событий WMI

    
    $Events |
      Select-Object -First 5 |
        Format-Table -Wrap
    		
  11. Просматриваем исполняемые программы в папке WBEM

    
    $Files = Get-ChildItem -Path $WBEMFOLDER\*.exe
    "{0,15}  {1,-40}" -f 'File Name','Description'
    Foreach ($File in $Files){
     $Name = $File.Name
     $Desc = ($File | 
              Select-Object -ExpandProperty VersionInfo).FileDescription
    "{0,15}  {1,-40}" -f $Name,$Desc
    }
    		
  12. Изучаем свой модуль CimCmdlets

    
    Get-Module -Name CimCmdlets |
      Select-Object -ExcludeProperty Exported*
        Format-List -Property *
    		
  13. Находим в этом модуле CimCmdlets командлеты

    
    Get-Command -Module CimCmdlets
    		
  14. Изучаем возвращаемый из Get-CimInstance тип .NET

    
    Get-CimInstance -ClassName Win32_Share | Get-Member
    		

Как это работает...

Сама служба WMI и относящиеся к ней файлы пребывает в папке установки Windows, папке System32\WBEM. На Шаге 1 вы просматриваете часть имеющегося содержимого этой папки со следующим выводом:

 

Рисунок 15-02


Изучаем папку WBEM

WMI хранит свой репозиторий CIM в отдельной папке. На Шаге 2 вы изучаете те файлы, которые составляют саму базу данных с таким выводом:

 

Рисунок 15-03


Изучаем те файлы, которые составляют репозиторий CIM

На Шаге 3 вы пользуетесь Get-Service для изучения своей службы WMI с подобным приводимому ниже выводом:

 

Рисунок 15-04


Просматриваем свою службу WMI

На Шаге 4 вы изучаете тот процесс Windows, который запускает службу WMI с подобным отображаемому далее выводу:

 

Рисунок 15-05


Просматриваем свою службу WMI

На Шаге 5 вы рассматриваете DLL, загружаемые самим процессом службы WMI со следующим выводом:

 

Рисунок 15-06


Просматриваем DLL, загружаемые процессом службы WMI

Каждый поставщик WMI это DLL, которым может воспользоваться WMI. На Шаге 6 вы рассматриваете поставщиков WMI в SRV1 с подобным приводимому ниже выводом:

 

Рисунок 15-07


Просматриваем DLL поставщика WMI

На Шаге 7 вы изучаете процесс WmiPrvSE с отображённым далее выводом:

 

Рисунок 15-08


Просматриваем процесс WmiPrvSE

Как и прочие службы Windows, WMI регистрирует события в некотором журнале событий, который способен помогать в устранении неисправностей. На Шаге 8 вы рассматриваете все относящиеся к WMI события с таким выводом:

 

Рисунок 15-09


Просматриваем относящиеся к WMI журналы событий

На Шаге 9 вы получаете имеющиеся события из этого журнала для просмотра различных уровней регистраций со следующим выводом:

 

Рисунок 15-10


Выявление типов событий WMI

На Шаге 10 вы просматриваете самые первые пять записей журнала событий WMI в SRV1. Вот как выглядит получаемый вывод:

 

Рисунок 15-11


Выявление типов событий WMI

На Шаге 11 вы просматриваете все исполняемые программы в своей папке WBEM с подобным приводимому ниже выводом:

 

Рисунок 15-12


Просмотр исполняемых программ в папке WBEM

В PowerShell 7 (и опционально в Windows PowerShell) вы получаете доступ к функциональным возможностям WMI при помощи командлетов из модуля CimCmdlets. Вы устанавливаете этот модуль как часть установки PowerShell 7. Программа установки Windows устанавливает некую версию этого модуля при установке вами самой ОС хоста. На Шаге 12 вы изучаете имеющиеся свойства этого модуля с таким выводом:

 

Рисунок 15-13


Просмотр подробностей модуля CimCmdlets

На Шаге 13 вы пользуетесь Get-Command для выявления имеющихся в модуле CimCmdlets командлетов, что выглядит следующим образом:

 

Рисунок 15-14


Просматриваем имеющиеся в модуле CimCmdlets командлеты

На Шаге 14 вы изучаете имеющиеся свойства некого объекта, возвращаемого из WMI после применения команды Get-CimInstance. Вывод с этого шага выглядит так:

 

Рисунок 15-15


Изучаем вывод, получаем из Get-CimInstance

Есть кое- что ещё...

На Шаге 1 вы просматриваете самые первые 20 файлов/ папок в своей папке WBEM. Имеется гораздо больше файлов, чем вы видите на этом рисунке. Здесь включены те файлы DLL, которые доступны в вашей системе доступными для поставщиков WMI.

На Шаге 7 вы просматриваете свой процесс WmiPrvSE. Этот процесс содержит поставщиков WMI. В зависимости от тех действий, которые в настоящий момент сделал WMI, вы можете видеть ноль, один или более вхождений этого процесса в своих хостах.

На Шаге 9 и на Шаге 10 вы выявляете и изучаете свой журнал событий WMI системы. В SRV1 вы можете наблюдать и записи журнала событий Information, и Error. Как вы можете наблюдать на Шаге 10, записи Information в основном указывают что WMI загрузил и активировал определённого поставщика. В большинстве случаев наблюдаемые вами сообщения событий Errorявляются проходящими или слабыми.

На Шаге 14 вы рассматриваете данные, возвращаемые Get-CimInstance. Как вы можете видеть из получаемого вывода, этот командлет возвращает те данные, которые получаются из класса WMI. Эти данные обёрнуты в неком объекте .NET, который имеет класс Microsoft.Management.Infrastructure.CimInstance с указывающим на значение пути к соответствующему классу WMI суффиксом, в данном случае классу Win32_Share из пространства имён ROOT/CIMV2. Этот возвращаемый объект и его содержимое отличается от того, что возвращается Get-WMIObject. Это может вызывать некую проблему если вы обновляете сценарии Windows PowerShell, которые изначально пользовались более старыми командлетами WMI.

Исследование пространств имён WMI

Командлеты CIM PowerShell позволяют вам производить выборку, обновление и удаление сведений из имеющейся базы данных и подписываться и обрабатывать события WMI. Имеющаяся база данных CIM организует свои сведения в наборы классов внутри некого иерархического множества пространств имён. Пространство имён, по существу, это некий контейнер, содержащий классы WMI и пространства имён WMI.

Названием корневого пространства имён WMI выступает ROOT, хотя, как вы можете заметить, WMI не слишком согласован в отношении применения заглавных букв. Пространство имён может содержать классы, а также дополнительные дочерние пространства имён. К примеру, само корневого пространства имён обладает дочерним пространством имён, CIMV2, которое ссылается на ROOT\CIMV2. Это пространство имён также обладает дочерними пространствами имён.

Всякое пространство имён в установленной базе данных CIM, включая ROOT, обладает специальным классом системы с названием __NAMESPACE. Этот класс содержит соответствующие названия дочерних пространств внутри текущих пространств имён. Таким образом, в нашем пространстве имён ROOT, имеющийся класс __NAMESPACE содержит некий экземпляр для его дочернего пространства имён CIMV2. Поскольку этот класс имеется внутри каждого пространства имён, именно это упрощает обнаружение всех имеющихся пространств имён в вашей системе.

Внутри каждой конкретной системы имеется множество пространств имён и классов в рамках WMI. Эти конкретные пространства имён и классы зависят от тех приложений и свойств Windows которые вы запускаете в неком хосте. Кроме того, не все пространства имён или классы полезны. Собственно пространство имён ROOT\CimV2 реализуемый соответствующим поставщиком WMI Win32, содержит классы WMI, которые полезны профессионалам ИТ. Другие классы или пространства имён могут также содержать полезные классы, однако большинство прочих имеющихся обычно полезны лишь для разработчиков, реализующих компоненты WMI или поставщиков WMI.

Другим менее широко применяемым пространством имён выступает ROOT\directory\LDAP, который содержит относящиеся к Active Directory классы. В то время как вы выполняете большинство функций управления AD при помощи командлетов AD, имеются функциональные возможности этого пространства имён, в особенности обработки событий, которые не доступны при помощи имеющихся командлетов AD и ккоторые вы можете найти полезными.

Подготовка

Этот рецепт пользуется SRV1, присоединённым к домену хостом. В этом хосте вы имеете установленными PowerShell 7 и VS Code.

Как это сделать...

  1. Просматриваем классы WMI в своём корневом пространстве имён

    
    Get-CimClass -Namespace 'ROOT' | 
      Select-Object -First 10
    		
  2. Просматриваем в ROOT класс __NAMESPACE

    
    Get-CimInstance -Namespace 'ROOT' -ClassName __NAMESPACE |
      Sort-Object -Property Name
    		
  3. Получаем и пересчитываем классы в ROOT\CIMV2

    
    $Classes = Get-CimClass -Namespace 'ROOT\CIMV2'  
    "There are $($Classes.Count) classes in ROOT\CIMV2"
    		
  4. Выявляем все имеющиеся пространства имён в SRV1

    
    Function Get-WMINamespaceEnum {
      [CmdletBinding()]
      Param($NS) 
      Write-Output $NS
      Get-CimInstance "__Namespace" -Namespace $NS @EAHT | 
      ForEach-Object { Get-WMINamespaceEnum "$ns\$($_.name)"   }
    }  # End of function
    $Namespaces = Get-WMINamespaceEnum 'ROOT' | 
      Sort-Object
    "There are $($Namespaces.Count) WMI namespaces on SRV1"
    		
  5. Просматриваем первые 25 пространств имён SRV1

    
    $Namespaces |
      Select-Object -First 25
    		
  6. Создаём блок сценария для подсчёта пространств имён и классов

    
    $SB = {
     Function Get-WMINamespaceEnum {
       [CmdletBinding()]
       Param(
         $NS
        ) 
       Write-Output $NS
       $EAHT = @{ErrorAction = 'SilentlyContinue'}
       Get-CimInstance "__Namespace" -Namespace $NS @EAHT | 
         ForEach-Object { Get-WMINamespaceEnum "$NS\$($_.Name)"   }
       }  # End of function
       $Namespaces = Get-WMINamespaceEnum 'ROOT' | Sort-Object
       $WMIClasses = @()
       Foreach ($Namespace in $Namespaces) {
       $WMIClasses += Get-CimClass -Namespace $Namespace
      }
     "There are $($Namespaces.Count) WMI namespaces on $(hostname)"
     "There are $($WMIClasses.Count) classes on $(hostname)"
    }
    		
  7. Выполняем этот юлок сценария локально в SRV1

    
    Invoke-Command -ComputerName SRV1 -ScriptBlock $SB
    		
  8. Выполняем тот же блок сценария в SRV2

    
    > Invoke-Command -ComputerName SRV2 -ScriptBlock $SB
    		
  9. Выполняем это блок сценария в DC1

    
    Invoke-Command -ComputerName DC1 -ScriptBlock $SB
    		

Как это работает...

На Шаге 1 SRV1

 

Рисунок 15-16


Изучаем классы WMI в самом пространстве имён root

На Шаге 2 SRV1

 

Рисунок 15-17


Изучаем класс __NAMESPACE в пространстве имён root

На Шаге 3 SRV1

 

Рисунок 15-18


Подсчёт классов в пространстве имён ROOT\CIMV2

На Шаге 4 вы определяете и затем применяете функцию для выявления всех имеющихся пространств имён в WMI в этом хосте, причём отсортированных в алфавитном порядке. Вот как выглядит вывод на этом шагу:

 

Рисунок 15-19


Выявление всех пространств имён WMI в SRV1

На Шаге 5 вы просматриваете первые 25 названий пространств имён с подобным приводимому ниже выводом:

 

Рисунок 15-20


Перечисление первых 25 пространств имён WMI в SRV1

На Шаге 6 вы создаёте блок сценария, который подсчитывает пространства имён и классы WMI. Этот шаг не вырабатывает никакого консольного вывода. На Шаге 7 вы выполняем эту функцию для SRV1 с подобным следующему выводом:

 

Рисунок 15-21


Подсчёт пространств имён и классов WMI в SRV1

На Шаге 8 вы выполняете этот блок сценария в SRV2 с таким выводом:

 

Рисунок 15-22


Подсчёт пространств имён и классов WMI в SRV2

На заключительном шаге, Шаге 9 вы исполняете этот блок кода в контроллере домена, DC1. Вывод с этого шага выглядит следующим образом:

 

Рисунок 15-23


Подсчёт пространств имён и классов WMI в DC1

Есть кое- что ещё...

На Шаге 2 вы определяете имеющиеся дочерние пространства имён в корневых пространствах имён. Каждый экземпляр содержит некую строку со значением названия дочернего пространства имён. Самой первой записью является AccessLogging. Таким образом, значением названия пространств имён этого дочернего пространства имён выступает ROOT\AccessLogging.

На Шаге 3 вы перечисляете имеющиеся в ROOT\CIMV2 классы. Как упоминалось ранее, не все эти классы полезны для профессионалов ИТ, хотя многие являются таковыми. Вы можете применять механизм поиска для нахождения классов могут быть полезными.

На Шаге 4 вы определяете рекурсивную функцию. Когда вы вызываете эту функцию, определяя пространство имён ROOT, эта функция выполняет выборку названий дочерних пространств имён из соответствующего класса __NAMESPACE в корневом пространстве имён. Затем для каждого дочернего пространства имён в корне эта функция вызывает сама себя с названием дочернего пространства имён. В конечном счёте эта функция возвращает значения названий каждого пространства имён в WMI. Затем вы сортируете их в алфавитном порядке по названию пространств имён. Обратите внимание, что вы можете отсортировать вывод GET-WMINamespaceEnum без определения свойства - вы сортируете имеющееся содержимое тех строк, которые возвращаются их этой функции.

На Шаге 5 вы просматриваете некоторые имеющиеся пространства имён в WMI из SRV1. Двумя важными пространствами имён выступают ROOT\CIMV2 ROOT\directory\LDAP. Первый содержит классы предоставляются поставщиком WMI Win32, содержащим подробности относительно программного обеспечения и оборудования в вашей системе, включая BIOS, ОС, файлы и многое иное.

На Шаге 6 вы определяете рекурсивную функцию которая перенумеровывает все имеющиеся пространства имён WMI. Эта функция начинает с имеющегося корневого пространства имён WMI (или любого пространства имён, с которым вы вызываете эту функцию) и получаете все имеющиеся дочерние пространства имён. Для каждого из них эта функция возвращает значения дочерних пространств имён и так далее. Для каждого из пространств имён эта функция пересчитывает значение числа классов в каждом пространстве имён.

Шаг 7, Шаг 8 и Шаг 9 запускают эту функцию (определённую на Шаге 6) удалённо. Эти шаги перечисляют и отображают счётчик значений пространств имён и классов во всех трёх системах. По этой причине вы должны ожидать, что значение числа классов и пространств имён разнятся.

Исследование классов WMI

WMI класс определяет управляемый WMI объект. Все классы WMI обитают в рамках пространства имён и содержит участников, которые включают в свой состав свойства, методы и события. Неким образцом класса выступает Win32_Share, который вы обнаруживаете в пространстве имён ROOT\CIMV2. Этот класс определяет некий совместный ресурс SMB в хосте Windows. В рамках WMI соответствующий поставщик Win32 реализует этот класс (совместно со множеством прочих классов ОС и относящихся к хосту).

Как уже упоминалось, вы обычно пользуетесь соответствующими командлетами SMB для управления совместными ресурсами SMB (как это обсуждалось в Главе 10, Управление совместными данными, включая рецепт Создание совместных ресурсов SMB и их безопасность). Точно также большую часть действий по управлению AD вы выполняете при помощи командлетов AD вместо того чтобы выполнять доступ к этим сведениям через WMI. Тем не менее, вы можете выполнять с WMI такие вещи, как обработку событий, что может оказаться очень полезным для всех профессионалов ИТ.

Класс WMI содержит одно или более свойств, которые являются атрибутами некого вхождения класса WMI. Классы могут также включать в свой состав методы, которые действуют в неком вхождении WMI. Например, класс Win32_Share содержит свойство Name, которое содержит значение названия совместного ресурса для этого разделяемого ресурса. Каждое свойство WMI обладает неким типом данных, например, integer или string. Наш класс Win32_Share также обладает методом Create() для создания нового совместного ресурса SMB и метода Delete() для удаления конкретного совместного ресурса. Некий метод WMI может быть динамическим (основанном на экземпляре) или статическим (относящемуся к классу). Метод Delete() класса Win32_Share это динамический метод, который вы применяете для удаления конкретного совместного ресурса SMB. Метод Create() это статический метод, который может выполнять этот класс для создания нового совместного ресурса SMB.

В этом рецепте вы применяете командлеты CIM для выявления сведений относительно классов и того что он может содержать. Вначале вы изучаете класс внутри соответствующего пространства имён ROOT\CIMV2. Вы также изучаете класс в пространстве имён не по умолчанию и выявляете те объекты, которые содержатся в классе.

Подготовка

Данный рецепт применяет SRV1, присоединённый к домену хост. В этом хосте у вас имеются установленными PowerShell 7 и VS Code.

Как это сделать...

  1. Просматриваем свой класс Win32_Share

    
    Get-CimClass -ClassName Win32_Share
    		
  2. Просматриваем свойства класса Win32_Share

    
    Get-CimClass -ClassName Win32_Share |
      Select-Object -ExpandProperty CimClassProperties |
        Sort-Object -Property Name |
          Format-Table -Property Name, CimType
    		
  3. Получаем методы класса Win32_Share

    
    Get-CimClass -ClassName Win32_Share |
      Select-Object -ExpandProperty CimClassMethods
    		
  4. Получаем классы некого не предопределённого по умолчанию пространства имён

    
    Get-CimClass -Namespace root\directory\LDAP |
      Where-Object CimClassName -match '^ds_group'
    		
  5. Просматриваем имеющиеся экземпляры класса ds_group

    
    Get-CimInstance -Namespace root\directory\LDAP -Classname 'DS_Group' |
      Format-Table -Property DS_name, DS_Member
    		

Как это работает...

На Шаге 1 вы просматриваете конкретный класс, класс Win32_Share, с подобным приводимому ниже выводом:

 

Рисунок 15-24


Просматриваем свой класс WMI Win32_Share

На Шаге 2 вы просматриваете имеющиеся свойства класса Win32_Share. Вывод с этого шага выглядит так:

 

Рисунок 15-25


Просматриваем свойства класса Win32_Share

На Шаге 3 вы применяете командлет Get-CimClass для просмотра тех методов, которые вам доступны в классе Win32_Share с подобным следующему выводом:

 

Рисунок 15-26


Просматриваем методы в классе Win32_Share

На Шаге 4 вы получаете относящиеся к группе классы в пространстве имён ROOT\directory\LDAP.Этот шаг возвращает только те классы, которые обладают названием ds_group. Как вы можете видеть, это соответствует нескольким имеющимся классом в этом пространстве имён следующим образом:

 

Рисунок 15-27


Находим относящиеся к группе AD классы в пространстве имён LDAP

На Шаге 5 вы получаете экземпляры соответствующего класса WMI ds_group. Отображаемый здесь вывод содержит и название соответствующей группы AD и имеющихся в настоящий момент участников каждой из групп AD. Вывод с этого шага выглядит подобно такому:

 

Рисунок 15-28


Поиск групп и участников AD

Есть кое- что ещё...

На Шаге 3 вы просматриваете имеющиеся в классе Win32_Share методы при помощи Get-CimClass. Так как вы не определили некое пространство имён, этот командлет WMI предполагает что вы заинтересованы в пространстве имён ROOT\CIV2. Обратите внимание, что на этом шаге метод Create() обладает двумя важными спецификаторами: конструктором и состоянием. Спецификатор конструктора сообщает, что вы пользуетесь именно статическим методом Create() для построения нового экземпляра данного класса (и некого нового совместного ресурса). Точно так же вы пользуетесь методом Delete() для удаления некого экземпляра данного класса.

На Шаге 5 вы просматриваете первые пять экземпляров в классе ds_group. Этот класс содержит некий экземпляр для каждой группы в домене Reskit.Org. Он содержит дополнительные сведения для каждой группы, возвращаемые при применении командлета Get-ADGroup.

Получение локальных и удалённых объектов WMI

В рецепте Исследование классов WMI вы выявляете что WMI предоставляет большое число (более 100) пространства имён каждого хоста совместно с тысячами классов WMI. Для возврата соответствующих экземпляров класса WMI либо в локальном, либо в удалённом хосте вы пользуетесь командлетом Get-CimInstance, как вы можете увидеть в этом рецепте. Этот командлет возвращает соответствующие экземпляры WMI для некого определённого класса WMI, обёрнутого в некий объект .NET.

При помощи WMI у вас имеются три способа, которыми вы способны применять Get-CimInstance:

  • Самый первый способ состоит в применении самого командлета для возврата всех вхождений классов и возвращает все свойства класса.

  • Второй способ состоит в применении параметра -Filter для определения некого фильтра WMI. Такой фильтр указывает соответствующей команде Get-CimInstance для возвращения некоторых, а не всех экземпляров желаемого класса.

  • Третий метод применяет некий запрос WMI при помощи WMI Query Language (WQL). Запрос WQL, по существу, некий оператор SQL, который указывает WMI для возвращения некоторых (не всех) свойств некоторых (не всех) вхождений конкретного предписанного класса WMI.

Когда вы применяете WMI внутри соответствующей сетевой среды, предписывая некий фильтр или полный запрос WMI может снизить общее число передаваемых по проводу данных и улучшает производительность.

Как и в предыдущем рецепте, для выборки экземпляров класса WMI вы применяете командлет Get-CimInstance при помощи каждого из трёх подходов.

Подготовка

В этом рецепте вы пользуетесь SRV1, присоединённом к домену хостом. В этом хосте у вас установлены PowerShell 7 и VS Code.

Как это сделать...

  1. Применяем Get-CimInstance в установленном по умолчанию пространстве имён

    
    Get-CimInstance -ClassName Win32_Share
    		
  2. Получение объектов WMI из не установленного по умолчанию пространства имён

    
    $GCIMHT1 = @{
        Namespace = 'ROOT\directory\LDAP'
        ClassName = 'ds_group'
    }
    Get-CimInstance @GCIMHT1 |
      Sort-Object -Property Name |
        Select-Object -First 10 |
          Format-Table -Property DS_name, DS_distinguishedName
    		
  3. Применяем некий фильтр WMI

    
    $Filter = "ds_Name LIKE '%operator%' "
    Get-CimInstance @GCIMHT1  -Filter $Filter |
      Format-Table -Property DS_Name
    		
  4. Применяем запрос WMI

    
    $Q = @"
      SELECT * from ds_group
        WHERE ds_Name like '%operator%'
    "@
    Get-CimInstance -Query $q -Namespace 'root\directory\LDAP' |
      Format-Table DS_Name
    		
  5. Получаем объект WMI из некой удалённой системы (DC1)

    
    Get-CimInstance -CimSession DC1 -ClassName Win32_ComputerSystem | 
      Format-Table -AutoSize
    		

Как это работает...

На Шаге 1 для возврата всех имеющихся экземпляров Win32_Share из SRV1 вы пользуетесь Get-CimInstance с таким выводом:

 

Рисунок 15-29


Выборка экземпляров класса Win32_Share в SRV1

На Шаге 2 для выборки экземпляров класса не установленного по умолчанию пространства имён, которое вы именуете в явном виде, вы применяете Get-CimInstance и получаете такой вывод:

 

Рисунок 15-30


Выборка объектов WMI в не определённом по умолчанию пространстве имён

На Шаге 3 вы пользуетесь фильтром WMI, определяемом параметром -Filter для того же командлета Get-CimInstance. Вот как выглядит вывод:

 

Рисунок 15-31


Выборка объектов WMI с применением фильтра WMI

На Шаге 4 вы применяете полный запрос WMI, который содержит значения пространства имён/ класса, для которых вы желаете выполнить выборку и подробности каких свойств и из каких экземпляров надлежит возвращать, подобно следующему:

 

Рисунок 15-32


Выборка объектов WMI с применением запроса WMI

На Шаге 5 вы осуществляете выборку объекта WMI из удалённого хоста, DC1. Тот класс, для которого выполнена выборка на данном шаге, Win32_ComputerSystem, содержит подробности этого хоста, такие как имя хоста, название домена, общий объём физической памяти, как вы это можете наблюдать в идущем следом выводе:

 

Рисунок 15-33


Выборка сведений WMI из DC1

Есть кое- что ещё...

На Шаге 4 вы создаёте запрос WMI. Этот запрос возвращает все свойства в любом экземпляре соответствующего класса, название которого содержит символы "operator", применяя синтаксис символов подстановки WMI. Этот запрос возвращает все свойства в соответствующих группах, которые содержат операторов печати и операторов серверов, как вы можете видеть из получаемого на этом шаге вывода.

На Шаге 5 вы возвращаете подробности из своего хоста DC1. Для возврата единственного вхождения класса Win32_ComputerSystem вы применяете Get-CimInstance. Этот вывод показывает, что хост DC1 обладает 4ГБ оперативной памяти. Если вы применяете для этого хоста виртуализацию, вы можете наблюдать различные значения, в зависимости от того как вы настроили эту ВМ.

Применение методов WMI

Во многих объектно- ориентированных языках программирования метод это некое действие, которому может подвергаться какой- то объект. WMI также производить методы класса. Например, класс Win32_Share обладает методом Delete() для удаления заданного совместного ресурса SMB. Этот класс также обладает соответствующим статическим методом Create(), который создаёт новый разделяемый ресурс SMB.

Во многих случаях методы WMI дублируют то, что вы можете совершать при помощи прочих командлетов PowerShell. Например, вы можете воспользоваться командлетом New-SMBShare cmdlet для создания нового совместного ресурса вместо применения статического метода Create() из его класса Win32_Share.

Как уже упоминалось ранее, методы WMI содержат методы экземпляров и статические методы. Динамические методы, или методы экземпляра работают в неком конкретном экземпляре - например, удаляют конкретный совместный ресурс SMB. Классы также производят статические методы и им нет нужды ссылаться на какие- то имеющиеся экземпляры класса. Скажем, вы можете применять статический метод Create() для создания нового совместного ресурса SMB(и нового вхождения в его класс Win32_Share).

Подготовка

В этом рецепте вы применяете SRV1, присоединённый к домену хостом. В этом хосте у вас установлены PowerShell 7 и VS Code.

Как это сделать...

  1. Обозреваем в SRV1 методы класса Win32_Share

    
    Get-CimClass -ClassName Win32_Share | 
      Select-Object -ExpandProperty CimClassMethods
    		
  2. Рассматриваем свойства класса Win32_Share

    
    Get-CimClass -ClassName Win32_Share | 
      Select-Object -ExpandProperty CimClassProperties |
        Format-Table -Property Name, CimType
    		
  3. Применяя статический метод Create() создаём новый совместный ресурс SMB

    
    $NSHT = @{
      Name        = 'TestShare1'
      Path        = 'C:\Foo'
      Description = 'Test Share'
      Type        = [uint32] 0 # disk
    }    
    Invoke-CimMethod -ClassName Win32_Share -MethodName Create -Arguments $NSHT
    		
  4. Просматриваем новый совместный ресурс SMB

    
    Get-SMBShare -Name 'TestShare1'
    		
  5. Просматриваем новый совместный ресурс SMB при помощи Get-CimInstance

    
    Get-CimInstance -Class Win32_Share -Filter "Name = 'TestShare1'"
    		
  6. Удаляем этот совместный ресурс

    
    Get-CimInstance -Class Win32_Share -Filter "Name = 'TestShare1'" |
      Invoke-CimMethod -MethodName Delete
    		

Как это работает...

На Шаге 1 для выборки и отображения всех методов, предоставляемых классом Win32_Share вы пользуетесь Get-CimClass. Это производит такой вывод:

 

Рисунок 15-34


Просматриваем все содержащиеся в классе WMI Win32_Share методы

На Шаге 2 вы применяете Get-CimClass для получения свойств каждого экземпляра из класса Win32_Share, воспроизводя следующий вывод:

 

Рисунок 15-35


Просматриваем свойства экземпляра класса WMI Win32_Share

На Шаге 3 для активации меода класса Win32_Share, Create(), и создания в SRV1 нового совместного ресурса SMB вы применяете Invoke-CimMethod и он выдаёт:

 

Рисунок 15-36


Создание нового совместного ресурса SMB при помощи WMI

На Шаге 4 для получения сведений об имеющемся совместном ресурсе SMB для созданного нами на предыдущем шаге разделяемого ресурса вы польльзуетесь Get-SMBShare на выходе производящем следующее:

 

Рисунок 15-37


Просмотр вновь созданного совместного ресурса SMB при помощи Get-SMBShare

На Шаге 5 для просмотра подробностей этого совместного ресурса через WMI вы воспользовались Get-CimInstance. Этот шаг производит такой вывод:

 

Рисунок 15-38


Просмотр вновь созданного совместного ресурса SMB при помощи Get-CIMInstance

На заключительном шаге данного рецепта, Шаге 6 , вы применяете Invoke-CimMethod для удаления определённого совместного ресурса (созданного вами на Шаге3).

Есть кое- что ещё...

На Шаге3, применяя командлет Invoke-CimMethod, вы создаёте новый совместный ресурс. Этот командлет получает хэш- таблицу, содержащую необходимые свойства и значения свойств для такого нового совместного ресурса. Данный командлет возвращает некий объект, содержащий свойство ReturnCode. Код возврата 0 сообщает об успехе - в нашем случае, WMI создал этот новый совместный ресурс. Для прочих значений кодов возврата вам следует проконсультироваться с соответствующей документацией. Для класса Win32_Share вы можете найти дополнительные сведения в документации из Интернета по адресу https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-share. Эта страница показывает значения кодов возврата, которые вырабатывает метод Create() и на что указывают эти коды возврата. Например, код возврата 8 указывал бы что вы попытались создать совместный ресурс с уже существующим именем. Когда вы планируете применять WMI в промышленных сценариях, рассмотрите возможность проверки для ненулевых кодов возврата и аккуратной обработки общих ошибок.

На Шаге 6 вы применяете метод WMI, Delete(), для удаления ранее созданного совместного ресурса SMB. Вы удаляете этот разделяемый ресурс для начала воспользовавшись Get-CimInstance с неким фильтром WMI с целью выборки подлежащих удалению совместных ресурсов. Затем вы через конвейер передаёте эти объекты совместных ресурсов в командлет Invoke-CimMethod и активируете соответствующий метод Delete() для этого экземпляра, передаваемого через указанный конвейер. Такой предпринимаемый на Шаге 6 подход это общеупотребительный способ удаления экземпляров класса WMI такого класса и, коли уж на то пошло, любого класса WMI.

Управление событиями WMI

Ключевой функциональной возможностью WMI выступает обработка его событий. Существуют тысячи событий, которые могут произойти внутри системы Windows, способный оказаться представляющими внимание. Например, вы можете пожелать знать когда некто добавляет нового участника в группу AD с высокими полномочиями, такую как Enterprise Admins. Вы можете сообщить WMI уведомлять вас при возникновении такого события и затем предпринимать какое- то соответствующее действие. Скажем, вы можете просто распечатать некий обновлённый список участников группы когда происходят изменения участников группы. Вы также можете проверять некий перечень пользователей, которые должны быть участниками такой группы и предпринимать какое- то действие если такой добавляемый пользователь не обладает авторизацией.

События обрабатываются как самим WMI так и поставщиками WMI. WMI сам по себе может сигнализировать об каком- то событии, когда в классе CIM будет обнаружено изменение - то есть, в случае любого нового, обновлённого или удалённого экземпляра класса. Вы также имеете возможность выявлять изменения класса целиком или пространств имён. WMI именует эти события присущими (intrinsic) событиями. Одно из распространённых присущих событий может происходить когда вы (или Windows) запускает какой- то новый процесс и, выполнив это, WMI добавляет новый экземпляр в свой класс Win32_Process (содержащийся в пространстве имён ROOT/CIMV2).

События также реализуют и поставщики WMI. Они носят название привносимых (extrinsic) событий WMI. Соответствующий поставщик WMI AD, к примеру, реализует некое событие, которое загорается всякий раз, когда изменяется некое членство в группе AD. Поставщик Реестра Windows также производит некое привносимое событие, которое раскрывает изменение его реестра, например, какой- то новый ключ реестра или обновлённое значение реестра.

Для того чтобы воспользоваться управлением событием WMI сначала вы создаёте какую- то подписку на событие. Такая подписка на событие сообщает WMI какое именно событие вы собираетесь отслеживать. Кроме того, вы можете определять некий обработчик, который сообщает WMI что вы желаете выполнять в случае возникновения такого события. Скажем, при запуске нового процесса вы можете пожелать отображать подробности такого события. Когда изменяется некое участие в группе AD, вы можете пожелать проверить и убедиться что если некие участники группы не авторизованы, следует сообщить об этом факте или возможно даже удалить недопустимого участника группы.

[Замечание]Замечание

Для получения дополнительных сведений о том как вы можете принимать события WMI обратитесь к https://docs.microsoft.com//windows/win32/wmisdk/receiving-a-wmi-event.

Для получения информации относительно типов получаемых событий смотрите https://docs.microsoft.com/windows/win32/wmisdk/determining-the-type-of-event-to-receive.

Имеются два типа обработки событий, которые вы можете применять. В этом рецепте вы создаёте и обрабатываете временные события WMI, которые работают в рамках сеанса PowerShell. Когда вы закрываете сеанс, WMI прекращает отслеживать все те события, которые вы зарегистрировали для такого сеанса. В следующем рецепте Реализация постоянной обработки событий WMI вы взглянете на создание не зависящих от своего текущего сеанса PowerShell подписок на события.

Когда вы зарегистрировали некое временное событие, вы можете снабдить WMI неким блоком сценария, который вы бы желали для выполнения со стороны WMI при возникновении данного события. WMI выполняет этот блок сценария в фоновом режиме, внутри некого задания PowerShell.

Когда вы регистрируете какое- то событие WMI, PowerShell создаёт это задание, в котором он исполняет соответствующее действие сценария. Как и для всех заданий PowerShell, вы пользуетесь Receive-Job для просмотра любого производимого его сценарием вывода. Когда ваш блок сценария содержит операторы Write-Host, PowerShell отправляет весь вывод непосредственно в вашу консоль (а не в своё задание фонового режима).Вы также можете выполнить регистрацию для некого события WMI без предписания какого бы то ни было блока действий. В таком случае WMI ставит такие события в очередь и для выборки подробностей подобного события вы можете применять Get-WinEvent.

Если WMI определяет некое событие, он вырабатывает запись события, которая содержит все подробности о таком событии. Такие записи событий могут оказаться полезными для вас в снабжении дополнительными подробностями относительно данного события, однако они не выступают полным моментальным снимком этого события. Вы можете зарегистрировать событие WMI об изменении членства в какой-то группе AD и получать такие подробности как сведения о новом участнике. Однако эта запись события не содержит подробностей относительно того пользователя, который изменил данное участие в группе, или значение IP адреса того хоста, который применялся для введения в действие соответствующего не авторизованного изменения.

Подготовка

Данный рецепт применяет SRV1, присоединённый к домену хост. В этом хосте у вас имеются установленными PowerShell 7 и VS Code.

Как это сделать...

  1. Регистрируем некое присущее событие

    
    $Query1 = "SELECT * FROM __InstanceCreationEvent WITHIN 2 
              WHERE TargetInstance ISA 'Win32_Process'"
    $CEHT = @{
      Query            = $Query1
      SourceIdentifier = 'NewProcessEvent'
    }          
    Register-CimIndicationEvent @CEHT
    		
  2. Для включения этого события запускаем Notepad

    
    notepad.exe
    		
  3. Получаем событие своего нового процесса

    
    $Event = Get-Event -SourceIdentifier 'NewProcessEvent' | 
               Select-Object -Last 1
    		
  4. Отображаем подробности этого события

    
    $Event.SourceEventArgs.NewEvent.TargetInstance
    		
  5. Удаляем регистрацию этого события

    
    Unregister-Event -SourceIdentifier 'NewProcessEvent'
    		
  6. Регистрируем некий запрос события на основе поставщика своего реестра

    
    New-Item -Path 'HKLM:\SOFTWARE\Packt' | Out-Null
    $Query2 = "SELECT * FROM RegistryValueChangeEvent 
                WHERE Hive='HKEY_LOCAL_MACHINE' 
                  AND KeyPath='SOFTWARE\\Packt' AND ValueName='MOLTUAE'"
    $Action2 = { 
      Write-Host -Object "Registry Value Change Event Occurred"
      $Global:RegEvent = $Event 
    }
    Register-CimIndicationEvent -Query $Query2 -Action $Action2 -Source RegChange
    		
  7. Создаём новый ключ реестра и устанавливаем значение элемента

    
    $Q2HT = [ordered] @{
      Type  = 'DWord'
      Name  = 'MOLTUAE' 
      Path  = 'HKLM:\Software\Packt' 
      Value = 42 
    }
    Set-ItemProperty @Q2HT
    Get-ItemProperty -Path HKLM:\SOFTWARE\Packt
    		
  8. Удаляем регистрацию этого события

    
    Unregister-Event -SourceIdentifier 'RegChange'
    		
  9. Изучаем подробности данного события

    
    $RegEvent.SourceEventArgs.NewEvent
    		
  10. Создаём запрос WQL события

    
    $Group = 'Enterprise Admins'
    $Query1 = @"
      Select * From __InstanceModificationEvent Within 5  
       Where TargetInstance ISA 'ds_group' AND 
             TargetInstance.ds_name = '$Group'
    "@
    		
  11. Создаём временную регистрацию события WMI

    
    $Event = @{
      Namespace =  'ROOT\directory\LDAP'
      SourceID  = 'DSGroupChange'
      Query     = $Query1
      Action    = {
        $Global:ADEvent = $Event
        Write-Host 'We have a group change'
      }
    }
    Register-CimIndicationEvent @Event
    		
  12. В группу Enterprise Admins добавляем какого- то пользователя.

    
    Add-ADGroupMember -Identity 'Enterprise Admins' -Members Malcolm
    		
  13. Просматриваем своего вновь добавленного пользователя

    
    $ADEvent.SourceEventArgs.NewEvent.TargetInstance | 
      Format-Table -Property DS_sAMAccountName,DS_Member
    		
  14. Удаляем регистрацию своего события

    
    Unregister-Event -SourceIdentifier 'DSGroupChange'
    		

Как это работает...

На Шаге 1 вы регистрируете некое присущее событие, которое происходит всякий раз, когда Windows запускает какой- то процесс. Данная регистрация не содержит блока действий. На Шаге 2 вы запускаете Notepad.exe для включения данного события. На Шаге 3 для выборки подробностей этого события вы пользуетесь Get-Event. Эти три шага не производят консольный вывод.

На Шаге 4 вы просматриваете подробности события запуска своего процесса с подобным следующему выводом:

 

Рисунок 15-39


Отображение подробностей события

На Шаге 5 вы удаляете регистрацию для своего события запуска процесса. Этот шаг ничего не выводит. На Шаге 6 вы регистрируете новую подписку на событие при помощи запроса WMI, который имеет целью вашего поставщика WMI с подобным такому выводом:

 

Рисунок 15-40


Регистрация для события основанного на поставщике реестра

По завершению регистрации этого события, на Шаге 7 , вы создаёте новый ключ реестра и устанавливаете значение ключа реестра для проверки подписки на это созданное событие. Вот как выглядит вывод для этого шага:

 

Рисунок 15-41


Активация события WMI реестра

На Шаге 8 вы удаляете регистрация события реестра чтобы избежать дополнительной обработки события и вывода события. На Шаге 9 вы изучаете получаемый вывод, выработанный WMI на основании изменений реестра, которые вы осуществили на Шаге 7. Подробности этого события выглядят следующим образом:

 

Рисунок 15-42


Изучаем событие WMI изменения реестра

На Шаге 10 вы создаёте запрос WQL, который перехватывает изменения для вашей группы AD Enterprise Admins, что не вырабатывает никакого вывода. На Шаге 11 вы пользуетесь этим запросом для создания временного события WMI, которое зажигается когда изменяется участие в соответствующей группе. Вот как выглядит этот вывод:

 

Рисунок 15-43


Создаём регистрацию временного события WMI

Для проверки события изменения своего каталога, на Шаге 12 , вы добавляете в свою группу AD Enterprise Admins нового пользователя, вырабатывая такой вывод:

 

Рисунок 15-44


Включаем событие изменения участия в группе AD

На Шаге 13 вы изучаете подробности своего события, выработанного на нашем предыдущем шаге в выводом, подобным приводимому ниже:

 

Рисунок 15-45


Изучаем подробности события изменения членства в группе AD

На заключительном шаге данного рецепта, на Шаге 14, вы удаляете регистрацию для изменения участия в соответствующей групе AD. Этот шаг не производит никакого вывода.

Есть кое- что ещё...

На Шаге 7 вы проверяете обработку события своего реестра, которое содержит блок сценария действий, который вы бы желали исполнить PowerShell при возникновении данного события. Так как тот блок сценария, который вы определили на Шаге 6, содержит оператор Write-Host, вы наблюдаете получаемый вывод в своей консоли PowerShell.

На Шаге 9 вы изучаете вырабатываемые WMI подробности при возникновении события WMI изменения реестра. Как и для прочих событий, подробности данного события опускают потенциально критически важные сведения. Например, это событие не сообщает вам какой именно пользователь выполнил это изменение или произвёл новое значение соответствующего значения реестра. Вы можете изучить журнал событий Безопасности Windows для обнаружения регистрации в данной системе соответствующего пользователя (и тем самым, того пользователя который внёс данное изменение). К тому же вы можете воспользоваться Get-ItemProperty для определения самого нового значения для свойства этого ключа реестра.

На Шаге 14 вы в явном виде удаляете свою регистрацию для события изменения AD. В качестве альтернативы, вам придётся закрыть ваш текущий сеанс PowerShell, удаляя подписки его событий.

Реализация постоянной обработки событий WMI

В нашем рецепте Управление событиями WMI вы применяли возможности PowerShell обработки событий WMI. Эти обработчики событий были активными только на протяжении активности соответствующего сеанса PowerShell и пока пользователь зарегистрирован в это хосте. В том рецепте вы создали некую подписку на событие и обрабатывали соответствующие события пока ваша система вырабатывала их. Такая временная обработка событий является великолепным инструментом устранения неисправностей пока вы зарегистрированы и выполняете PowerShell.

WMI к тому же предоставляет постоянную обработку событий. Вы настраиваете WMI на подписку и обработку событий по мере их появления не применяя активного и открытого сеанса. При постоянной обработке событий вы настраиваете WMI на подписку для конкретного события, например, добавление нового участника в группу с наивысшими полномочиями, такую как Enterprise Admins. Вы также настраиваете WMI на выполнение предварительно определяемого действия при возникновении такого события, например создание отчёта или отправку сообщения электронной почтой для отчёта о таком событии если/ когда оно происходит.

WMI в Windows определяет несколько различных типов постоянных потребителей сообщений, которых вы можете применять для настройки некого постоянного события:

  • Active Script Consumer: Вы пользуетесь им для запуска конкретного сценария VBS.

  • Log File Consumer: Этот обработчик записывает подробности событий в файлы журнала событий.

  • NT Event Log Consumer: Этот потребитель записывает подробности событий в Журнал событий Windows.

  • SMTP Event Consumer: Вы можете применять этого потребителя для отправки SMTP сообщения электронной почты при возникновении некого события.

  • Command Line Consumer: при помощи данного поставщика вы можете запускать программу, такую как PowerShell, и передавать название файла сценария. При возникновении соответствующего события, данный сценарий имеет доступ к подробностям этого события и способен достаточно выполнять из того, что вы способны делать в PowerShell.

Microsoft разработал поставщика Active Script во времена сценариев Visual Basic и VBS. К сожалению, этот поставщик не поддерживает сценарии PowerShell. Постоянный обработчик событий WMI командной строки, с другой стороны, даёт вам возможность исполнять любые программы, которые вы пожелаете когда соответствующий обработчик события определяет возникновение события. В данном рецепте вы запрашиваете WMI запустить pwsh.exe и исполнить определённый файл сценария при загорании соответствующего события.

Управление постоянным обработчиком событий аналогично тем временным событиям WMI, которые изучались в нашем рецепте Управление событиями WMI. Вы сообщаете WMI какое событие перехватывать и что делать при возникновении такого события. Вы добавляете некое возникновение класса WMI в три класса WMI, как вы обнаружите в этом рецепте для реализации постоянного обработчика события следующим образом:

  • Определяете некий фильтр события. Такой фильтр события определяет то конкретное событие, которое должен обрабатывать WMI. Вы делаете это добавляя новый экземпляр в тот конкретный класс события, который вы бы хотели выявлять WMI. Этот фильтр события по существу тот же самый, что и в нашем предыдущем рецепте, Управление событиями WMI.

  • Определяете потребителя события. На этом шаге вы определяете то действие, которое вы бы хотели предпринять со стороны WMI при возникновении соответствующего события.

  • Связываете полученные фиотр события и потребителя события. При помощи данного шага вы добавляете новое вхождение в связываемом классе события. Данное вхождение направляет WMI на выполнение некого действия (активацию соответствующего потребителя события) всякий раз, когда WMI выявляет что случилось конкретное событие (предписанное в его фильтре события).

Поставщик WMI AD реализует широкий диапазон относящихся к AD событий, на которые вы имеете возможность подписываться. Пространства имён WMI обычно содержат конкретные классы событий, которые можно выявлять при каких бы то ни было изменениях в таком пространстве имён. Пространство имён ROOT/Directory/LDAP обладает системным классом с названием __InstanceModificationEvent. Для постоянного обработчика события вы добавляете некое вхождение в этот класс.

[Замечание]Замечание

Уместно небольшое предостережение. При работе с постоянной обработкой событий WMI вам следует быть крайне внимательным. Рекомендуется разобраться с тем, как вы удаляете те объекты, которые относятся к соответствующему постоянному обработчику события. Обратите внимание, что пока вы не удалите эти записи в явном виде, WMI продолжит отслеживать ваш хост на предмет таких событий, что может приводить к излишнему потреблений ресурсов.

Данный рецепт также демонстрирует полезный подход: создание функций PowerShell для отображения необходимой подписки на событие и последующее полное удаление такой подписки. Наконец, будьте внимательны при изменении времени обновления фильтра события (определяемого в самом фильтре события WMI). Уменьшение времени обновления соответствующего события способно потреблять дополнительно ЦПК и оперативную память. В большинстве случаев, частота обновления раз в секунду или даже каждые 5 секунд, возможно, непомерно перекрывает потребности. Для большинства событий более чем уместна проверка каждые 10 секунд.

Подготовка

В данном рецепте применяется присоединённый к домену хост SRV1. В этом хосте у вас установлены PowerShell 7 и VS Code.

Также проверьте что ваш пользователь Malcolm не выступает участником группы AD Enterprise Admins.

Как это сделать...

  1. Создаём перечень допустимых пользователей для своей группы AD Enterprise Admins

    
    $OKUsersFile = 'C:\Foo\OKUsers.Txt'
    $OKUsers  =  @'
    Administrator
    JerryG
    '@
    $OKUsers | 
      Out-File -FilePath $OKUsersFile
    		
  2. Определяем вспомогательные функции для получения/ удаления постоянных событий

    
    Function Get-WMIPE {
      '*** Event Filters Defined ***'
      Get-CimInstance -Namespace root\subscription -ClassName __EventFilter  |
        Where-Object Name -eq "EventFilter1" |
         Format-Table Name, Query
      '***Consumer Defined ***'
      $NS = 'ROOT\subscription'
      $CN = 'CommandLineEventConsumer'
      Get-CimInstance -Namespace $ns -Classname  $CN |
        Where-Object {$_.name -eq "EventConsumer1"}  |
         Format-Table Name, Commandlinetemplate
      '***Bindings Defined ***'
      Get-CimInstance -Namespace root\subscription -ClassName __FilterToConsumerBinding |
        Where-Object -FilterScript {$_.Filter.Name -eq "EventFilter1"} |
          Format-Table Filter, Consumer
    }
    Function Remove-WMIPE {
      Get-CimInstance -Namespace root\subscription __EventFilter | 
        Where-Object Name -eq "EventFilter1" |
          Remove-CimInstance
      Get-CimInstance -Namespace root\subscription CommandLineEventConsumer | 
        Where-Object Name -eq 'EventConsumer1' |
          Remove-CimInstance
      Get-CimInstance -Namespace root\subscription __FilterToConsumerBinding  |
        Where-Object -FilterScript {$_.Filter.Name -eq 'EventFilter1'}   |
          Remove-CimInstance
    }
    		
  3. Создаём запрос фильтра события

    
    $Group = 'Enterprise Admins'
    $Query = @"
      SELECT * From __InstanceModificationEvent Within 10  
       WHERE TargetInstance ISA 'ds_group' AND 
             TargetInstance.ds_name = '$Group'
    "@
    		
  4. Создаём фильтр необходимого события

    
    $Param = @{
      QueryLanguage =  'WQL'
      Query          =  $Query
      Name           =  "EventFilter1"
      EventNameSpace =  "root/directory/LDAP"
    }
    $IHT = @{
      ClassName = '__EventFilter'
      Namespace = 'root/subscription'
      Property  = $Param
    }        
    $InstanceFilter = New-CimInstance @IHT
    		
  5. Создаём сценарий Monitor.ps1, исполняемый при возникновении соответствующего события WMI

    
    $MONITOR = @'
    $LogFile   = 'C:\Foo\Grouplog.Txt'
    $Group     = 'Enterprise Admins'
    "On:  [$(Get-Date)]  Group [$Group] was changed" | 
      Out-File -Force $LogFile -Append -Encoding Ascii
    $ADGM = Get-ADGroupMember -Identity $Group
    # Display who's in the group
    "Group Membership"
    $ADGM | Format-Table Name, DistinguishedName |
      Out-File -Force $LogFile -Append  -Encoding Ascii
    $OKUsers = Get-Content -Path C:\Foo\OKUsers.txt
    # Look at who is not authorized
    foreach ($User in $ADGM) {
      if ($User.SamAccountName -notin $OKUsers) {
        "Unauthorized user [$($User.SamAccountName)] added to $Group"  | 
          Out-File -Force $LogFile -Append  -Encoding Ascii
      }
    }
    "**********************************`n`n" | 
    Out-File -Force $LogFile -Append -Encoding Ascii
    '@
    $MONITOR | Out-File -Path C:\Foo\Monitor.ps1
    		
  6. Создаём потребителя события WMI

    
    # The consumer runs PowerShell 7 to execute C:\Foo\Monitor.ps1
    $CLT = 'Pwsh.exe -File C:\Foo\Monitor.ps1'
    $Param =[ordered] @{
      Name                = 'EventConsumer1'
      CommandLineTemplate = $CLT
    }
    $ECHT = @{
      Namespace = 'root/subscription'
      ClassName = "CommandLineEventConsumer"
      Property  = $param
    }        
    $InstanceConsumer = New-CimInstance @ECHT
    		
  7. Связываем созданные фильтр и потребителя

    
    $Param = @{
      Filter   = [ref]$InstanceFilter     
      Consumer = [ref]$InstanceConsumer
    }
    $IBHT = @{
      Namespace = 'root/subscription'
      ClassName = '__FilterToConsumerBinding'
      Property  = $Param
    }
    $InstanceBinding = New-CimInstance   @IBHT
    		
  8. Просматриваем подробности регистрации своего события

    
    Get-WMIPE
    		
  9. Добавляем пользователя в свою группу Enterprise Admins

    
    Add-ADGroupMember -Identity 'Enterprise admins' -Members Malcolm
    		
  10. Просматриваем файл Grouplog.txt

    
    Get-Content -Path C:\Foo\Grouplog.txt
    		
  11. Прибираемся

    
    Remove-WMIPE   # invoke this function you defined above
    $RGMHT = @{
     Identity = 'Enterprise admins'
     Member   = 'Malcolm'
     Confirm  = $false
    }
    Remove-ADGroupMember @RGMHT
    Get-WMIPE      # ensure you have removed the event handling
    		

Как это работает...

На Шаге 1 вы создаёте текстовый файл, содержащий два sAMAccountName пользователей, который вы определили как тех, сто должен быть участником вашей группы Enterprise Admins. На Шаге 2 вы создаёте две вспомогательные функции, Get-WMIPE и Remove-WMIPE, для просмотра и удаления соответствующих экземпляров класса WMI, который обрабатывает данное событие. На Шаге 3 вы создаёте запрос фильтра события, который, на Шаге 4, вы добавляете в WMI. Эти шаги не производят никакого вывода.

Когда происходит отслеживаемое постоянно событие WMI и изменяется соответствующее участие в группе, вы хотите исполнить определённый сценарий PowerShell. На Шаге 5 вы создаёте файл C:\Foo\Monitor.ps1, что не производит никакого консольного вывода.

На Шаге 6 вы создаёте нового потребителя события, сообщая WMI о необходимости исполнения сценария наблюдения для выявления такого события. Затем, на Шаге 7 вы связываете созданного потребителя события и соответствующий фильтр события для завершения настройки постоянного обработчика события. Эти два шага не производят никакого вывода.

На Шаге 8 вы пользуетесь своей функцией Get-WMIPE, которую вы определили на Шаге 2 для просмотра подробностей фильтра рассматриваемого события. Вот как выглядит вывод для этого шага:

 

Рисунок 15-46


Изучаем подробности события изменения членства в группе AD

На Шаге 9 вы проверяете настроенную постоянную обработку события,добавляя нового пользователя (Malcolm) в свою группу Enterprise Admins. Данный шаг не производит консольного вывода, поскольку вы не добавили в Monitor.ps1 никаких операторов Write-Host.

На Шаге 10 вы просматриваете свой файл Grouplog.txt с подобным приводимому ниже выводом:

 

Рисунок 15-47


Просматриваем Grouplog.txt (выработанный Monitor.ps1)

На заключительном шаге этого рецепта, на Шаге 11, вы прибираетесь и вызываете свою созданную на Шаге 2 функцию Remove-WMIPE для удаления из WMI подробностей своего события. В самом конце данного шага вы исполняете свою функцию Get-WMIPE чтобы убедиться что вы удалили все экземпляры класса подписки своего события. Вот как выглядит вывод на этом шаге:

 

Рисунок 15-48


Уборка

Есть кое- что ещё...

На Шаге 5 вы создаёте сценарий, который вы бы хотели исполнять WMI всякий раз, когда изменяется членство в вашей группе Enterprise Admins. Этот сценарий записывает подробности в текстовый файл (Grouplog.txt), содержащий значение времени когда произошло данное событие, своего нового участника и содержит ли эта группа теперь каких- то пользователей без авторизации. Вы можете дополнить Monitor.ps1 на отправку электронного письма в почтовый ящик администрации или просто удалять такого пользователя без авторизации. Вы также можете заглянуть в свой журнал событий Безопасности Windows чтобы отыскать самого последнего зарегистрировавшегося в этом сервере пользователя.

На Шаге 10 вы просматриваете вырабатываемый сценарием Monitor.ps1 вывод. Обратите внимание, что для вашего постоянного обработчика события может потребоваться несколько секунд чтобы выполнить этот сценарий.

В данном рецепте вы создали две вспомогательные функции, Get-WMIPE и Remove-WMIPE, для просмотра и удаления подробностей своего постоянного обработчика события. Вы также можете вызывать эти функции в самом конце данного рецепта чт вы не оставили это постоянное событие целиком или частично настроенным.