WMI, czyli Windows Management Instrumentation, jest mechanizmem pozwalającym na zarządzanie wszelakimi zasobami komputera w jednolity sposób, niezależnie od rodzaju informacji jakiej potrzebujemy: zapytania do Active Directory, informacje o sterownikach, o urządzeniach, zainstalowanym oprogramowaniu, operacje na rejestrach, a nawet badanie wskaźników wydajnościowych, dziennika zdarzeń [event log] czy operacji SNMP [Simple Network Management Protocol]. Dużo?

Osoby, które jeszcze nie zwróciły uwagi, niech zastanowią się jak to możliwe, żeby do informacji tak od siebie odległych można było dostawać się w jednolity sposób, i za pomocą jednego tylko interface’u. Interface’m tym jest właśnie WMI. Ideę tego jak to wszystko funkcjonuje najlepiej oddają dwa rysunki:

wbem.jpg
[rys.1 WBEM common model for network management]
wmiarchitecture.jpg
[rys.2 WMI architecture]

Nie trzeba być naddto spostrzegawczym, żeby zauważyć, że na obu rysunkach pojawiło się kilka dziwnych skrótów. Zanim zacznie się pisać skrypty [czy też programy] warto mieć ogólne pojęcie teoretyczne co i jak działa. Postaram się oszczędzić Niecierpliwych i opisać to możliwie zwięźle:

  • WBEMWeb-Based Enterprise Management: jest standardem opracowanym przez kilka firm [Microsoft, Compaq, BMC, Cisco, Intel], definiującym uniwersalny i jednolity sposób dostępu do informacji pomagających zarządzać sprzętem/oprogramowaniem w całej strukturze korporacji, bez względu na to, o jakie urządzenie czy też system chodzi. WMI jest Microsoft’ową implementacją WBEM – czyli mówiąc w skrócie WMI to WBEM by Microsoft.
  • CIMCommon Information Model: mówiąc w skrócie jest to zorientowany obiektowo schemat [repozytorium] zarządzanych obiektów. Czyli po ludzq: żeby się czegoś dowiedzieć, warto najpierw kogoś/czegoś zapytać o co wogóle można się pytać. Rolę takiego informatora pełni właśnie CIM. A konkretnie CIMOM [CIM Object Manager].

Oba rysunki w zasadzie przedstawiają to samo, z trochę innej perspektywy – rys. 1. pokazuje cały mechanizm na wyższym poziomie abstrakcji – samą ideę działania. Rys. 2. dotyczy już WMI [czyli konkretnej implementaji WBEM]. Razem tworzą pełny obraz 'co i jak’. Czyli:

  • są sobie jakieś zasoby [rys.2 – Managed System]
  • tymi zasobami mogą być różne części systemu [rys.1 – PC Hardware, Network…]
  • każdy zasób jest reprezentowany w systemie przez jakiś driver [inaczej system nie potrafiłby go obsłużyć] [rys.1 WDM Kernel Object, SNMP Object…] a dostęp do tegoż drivera umożliwia nam konkretny provider za pomocą jakiegoś tam API [rys. 2. – Provider]
  • taraz dla zmyłki od góry – jest sobie klient, konsument czy też po prostu osoba zainteresowana pewnymi informacjami reprezentowana przez jakąś aplikację czy też skrypt WMI [tak jak sprzęt jest ogniwem łączącym pomiędzy światem fizycznym a systemem operacyjnym, tak aplikacja jest łącznikiem między użytkownikiem a systemem] [rys. 2 – Management Application]
  • po uruchomieniu aplikacja odwołuje się do CIM żeby dowiedzieć się jakie obiekty są dostępne. Możemy oczywiście odwoływać się od razu do konkretnego obiektu, jeśli wiemy, że taki jest i jak się nazywa.
  • no i tu się wszystko zbiega – CIMOM to repozytorium dostępnych klas obiektów, ich providerzy natomiast są podpięci do CIMOM za pomocą interface’ów COM. Jeśli ktoś nie bardzo wie jak jest zbudowany windows to może się to wydawać trochę namieszne. Nie należy się zrażać – po prostu trzeba przyjąć, że każdy provider udostępnia pośrednika – obiekt COM – za pomocą którego może dogadać się z CIMOM [tak btw. to polecam lekturę artykułu o WSH jeśli jeszcze tego nie zrobił[a]eś. w zasadzie można go potraktować jako poprzedzającą część tego co masz teraz przed oczami].

Teraz coś co powinno pomóc tym, którzy się trochę pogubili, czyli kila standardowych provider’ów w windowsie:

  • Win32 Provider – który jest odpowiedzialny za dostarczanie informacji o systemie informacyjnym [typu: jaki to jest OS, którego ma Service Packa etc], urządzeniach peryferyjnych, systemu plików, informacje o bezpieczeństwie.
  • Registry Provider – pozawalający na wszelkiego rodzaju operacje na rejestrze systemu.
  • Event Log Provider – umożliwiający odczytywanie/backup’owanie informacji w Event Log’u [dzienniku zdarzeń] oraz configuracji jego parametrów.

To oczywiście tylko kilka – żeby jak najszybciej przejść do praktyki, a więcej można sobie zawsze potem doczytać. Teraz kilka przykładów:

Przykład 1.

Jak wyświetlić wszystkie service’y w systemie:

Set ServiceSet = GetObject("winmgmts:").InstancesOf("win32_Service")
for each Service in ServiceSet
    WScript.echo Service.Description
next

Skrypcik jest banalny, ale należy się trochę teorii i wyjaśnień:

  1. tworzymy obiekt typu winmgmt [Window Management]. Jeśli zobaczymy na wynik tego skryptu, pojawił się tam m.in. „Windows Management Instrumentation” – jest to service pełniący rolę CIMOM. Dla tego podłączamy się właśnie do niego [rys. 2.]. Pliki odpowiedzialne za WMI znajdują się w katalogu %windir%system32wbem.
  2. winmgmts:” to najprostrzy z możliwych monikerów – informacje o tym jak w pełni zbudowany jest moniker znajdują się dalej. Ale proponuję czytać po kolei.
  3. dalej jest „InstancesOf„, które świadczy o obietowej budowie CIM. Jest to żądanie dostępu do instancji obiektów win32_Service. W wyniku otrzymujemy kolekcję [zbiór] obiektów typu win32_Services.
  4. no i pozostaje dla każdego obiektu [for each Service] w kolekcji obiektów jaką zwróciła metoda „InstancesOf” [in ServiceSet] wyświetlić jego opis [WScript.echo Service.Description]

Przykład 2.

Trochę o WQL, filtrowaniu i kolejny moniker

queryString = "select * from Win32_Service where State='Stopped' and StartMode='Auto'"
Set ServiceSet = GetObject("winmgmts://.").ExecQuery(queryString)
if ServiceSet.Count <> 0 then
	for each Service in ServiceSet
		WScript.echo Service.Description
	next
else
	WScript.echo "no services stopped, with 'auto' state mode."
end if
  1. najłatwiejszym sposobem tworzenia zapytań i filtrowania jest WQLWMI Query Languange – stworzony na wzór i podobieństwo swojego ojca – SQL’a. W powyższym przykładzie przedstawiono sposób na wylistowanie tych service’ów, które mają ustawioną właściwość startu na 'Auto’ , a z jakiegoś powodu w tym momencie nie działają [where State='Stopped' and StartMode='Auto'].

    Jeśli ktoś zna SQL’a – to 'jest w domu’ ponieważ WQL jest jego uproszczoną wersją. Osoby, które z nim do czynienia jeszcze nie miały, nie mają również powodu do zmartwienia – język jest dość intuicyjny i większość podstawowych rzeczy będzie się w stanie zrobić na podstawie przykładów z np. tego artykułu.

  2. pojawił się również nieco wzbogacony moniker – „//” oddziela typ obiektu od reszty zapisu. Ponieważ w poprzednim przykładzie 'resza zapisu’ nie wystąpiła, nie było konieczności, żeby to wstawiać. Tutaj reszta zapisu jest reprezentowana przez „.”, oznaczającą komputer lokalny. Jeśli zastąpimy ją ciągiem „jakiskompwsieci”, to dostaniemy listę service’ów na komputerze „jakiskomputerwsieci”.
  3. wniosek z tego taki, że „.” jest defaultowym kontextem i jeśli chodzi nam o komputer lokalny to możemy sobie ją pominąć. Prawidłowy więc będzie również moniker: „winmgmts://”

Przykład 3.

wolne miejsce na dyskach czyli bardziej zaawansowane zapytanie WQL, no i oczywiście kolejny moniker

queryString = "select FreeSpace,Size,Name from Win32_LogicalDisk where DriveType=3"
strCompName="."

Set diskSet=GetObject("winmgmts:{impersonationLevel=impersonate}//"&strCompName).ExecQuery(queryString)
for each disk in diskSet
	tSize = cstr( cint(Disk.Size/1024/1024) )
	prcFree = "( "+cstr(cint( (Disk.FreeSpace/Disk.size)*100) )+"% )"
    WScript.echo "Drive " + Disk.Name + " total: " + tSize + " Mb free: " + prcFree
next
  1. w tym przykładzie zapytanie zostało zoptymalizowane, poprzez wyselekcjonowanie konkretnych atrybutów z których będzie korzystał sktypt – FreeSpace, Size i Name – dzięki czemu powinno być szybsze i 'mniejsze’ – co ma znaczenie przy zapytaniach do zdalnej maszyny.
  2. pojawiło się również kolejne rozszeżenie monikera – które jak każde do tej pory nic nie wnosi, ponieważ po prostu jawnie wpisywane są ustawienia default’owe. „impersonationLevel” jest atrybutem związanym z kontextem modelu bezpieczeństwa w WMI. Ponieważ zmieniając nazwę komputera z „.” na nazwę jakiegoś komputera w sieci możemy zarządać informacji od innego systemu, pojawia się problem uprawnień – zupelnie niefajnie by było, gdyby każdy chętny mógł się dowiedzieć wszystkiego o naszym systemie. Standardowo uprawnienia do połączenia się z service’m „Windows Management Instrumentation” mają użytkownicy należący do grupy administratorów. Słowo „impersonate” oznacza, że skrypt będzie wykonywany w kontexcie użytkownika, który go uruchamia.
  3. DriveType=3” oznacza ich chodzi nam wyłącznie o dyski stałe [nie CD, nie flopy ani inne wynalzki]

Przykład 4.

Pierwsza zmiana… i kolejny moniker

queryString="select * from win32_computerSystem"
strCompName="."
credentials="{impersonationLevel=impersonate}"

set oCollection=GetObject("winmgmts:"&credentials&"//"&strCompName&"/root/CIMv2").ExecQuery(queryString)
for each o in oCollection
	o.SystemStartupDelay=3
	o.put_()
next
  1. Do tej pory przykłady pokazywały wyłącznie sposób na wydobycie jakiejś informacji – czyli pobierały wartość atrybutu [Property]. Żeby pokazać, iż WMI daje również możliwość modyfikacji parametrów oto przykład zmieniający czas oczekiwania na wybór systemu podczas startu komputera – do czego wykorzystuje się metodę [Method] „put_()„.
  2. pomimo, że na komputerze może działać tylko jeden system operacyjny na raz [tak – ja wiem, że jest np. VMware ale WMI nie wie], to należy wykonać iterację przez wszystkie jeden obiekt – ponieważ przy takim zapytaniu zwracany jest zawsze obiekt typu 'collection’.
  3. staram się pisać skrypty tak, żeby na początek wyrzucać rzeczy, które już były, więc są mniej ciekawe. Poza tym forma skryptów staje się coraz bardziej uniwersalna – ponieważ mam nadzieję poza WMI przybliżyć sposób pisania [IMHO] porządnych skryptów.
  4. moniker rozrósł się o kolejny nieprzydatny fragment – „/root/CIMv2„. Tym razem jest to określenie domeny nazewniczej [namespace]

Przykład 5 i 6.

Backup EventLoga i reboot zdalnej maszyny czyli jeszcze troche o monikerach

[topic extremalnie po polsku (;]

ON ERROR RESUME NEXT
queryString="select * from win32_NTEventLogFile where LogFileName='Application'"
strCompName="."
specRight="(Backup)"
credentials="{impersonationLevel=impersonate,"&specRight&"}"
cimContext="/root/CIMv2"

set oCollection=GetObject("winmgmts:"&credentials&"//"&strCompName&cimContext).ExecQuery(queryString)
for each o in oCollection
	errHandle( o.BackupEventLog("c:tempApplicationEventLogBackup.evt") )
	errHandle( o.ClearEventLog() )
next

Sub errHandle(errLevel)
	if errLevel = 0 then
		WScript.echo "DONE."
	else
		WScript.echo "error: "&errLevel
		WScript.quit
	end if
End Sub

  1. z mniej ważnych [w kontexcie tego artu] doszła prymitywna procedurka do obsługi błedów
  2. wywołane zostają dwie metody dostępne dla obiektu typu ’NTEventLogFile’: BackupEverntLog oraz ClearEventLog. Nazwy metod same mówią o swoim przeznaczeniu…
  3. no i oczywiście moniker – tym razem rozwinięty o coś niezbędnego – żądanie specjalnego prawa dostępu „(Backup)” pozwalającego na wykorzystanie w/w metod.

I przykładzik pokazujący jak zshutdown’ować kompa – również wymaga uaktywnienia specjalnego prawa systemowego – tym razem „(Shutdown)” [przyczytaj art do końca, zanim przestujesz ten skrypt (;]:

queryString="select * from win32_OperatingSystem"
strCompName="."
specRight="(Shutdown)"
credentials="{impersonationLevel=impersonate,"&specRight&"}"
cimContext="/root/CIMv2"

set oCollection=GetObject("winmgmts:"&credentials&"//"&strCompName&cimContext).ExecQuery(queryString)
for each o in oCollection
	'o.shutdown()
	o.reboot()
next
  1. ciekawostką jest to, że w literaturze można również natknąć się na prawo „(RemoteShutdown)„. Ale wtedy nie można zrebootować lokalnego systemu. Warto z tego korzystać jesli mamy uniwersalny skrypcik bootujący i chcemy zabezpieczyć się przed tym, żeby przypadkiem nie zbootować lokalnego systemu – to jak z podcinaniem gałezi na której się siedzi.
  2. linijka o.shutdown jest wykomentowana ponieważ shutdown bez parametru doprowadza system do momentu „you can safely…”…

Przykład 7.

Troche o zdarzeniach – czyli badanie wydajności

Option Explicit
ON ERROR RESUME NEXT
Dim queryString,strCompName,credentials
Dim evt
Dim i, NTevent

queryString="select * from __instanceModificationEvent within 1 where targetInstance isa 'Win32_Processor'"
strCompName="."
credentials="{impersonationLevel=impersonate}"

set evt = GetObject("winmgmts:{impersonationLevel=impersonate}").ExecNotificationQuery(queryString)
errHandle(err)
i=0
do while i<10
    set NTevent=evt.nextevent
    errHandle(err)
    performAction(NTevent)
    i=i+1
loop

Sub errHandle(oErr)
    if oErr<>0 then
        Wscript.echo oErr.Number, oErr.description, oErr.source
        wscript.quit
    end if
End Sub

Sub performAction(e)
    wscript.echo e.targetInstance.deviceID&": "&e.targetInstance.loadPercentage&"%"
    if e.targetInstance.loadPercentage>80 then performCritical(e)
end Sub

Sub performCritical(e)
    wscript.echo "CRITICAL!"
end sub
  1. było do tej pory o atrybutach [Properties] oraz metodach [Methods] nadszedł czas na prezentację kolejnego mechanizmu: obsługi zdarzeń [Event Handling]. Jest to dość potęrzny mechanizm jeśli chodzi o możliwość praktycznego zastosowania – ustawia się 'pułapkę na zdarzenie’ i w razie jeśli wystąpi wykonywana jest jakaś akcja. W niniejszym przykładzie queryString jest tak sformuowany, iż akceptuje wszystkie zdarzenia. Jest to przydatne do ciągłego monitorowania. Poza tym w obsłudze akcji jest warunek krytyczny i wykonanie akcji krytycznej – moża tam np. wstawić wysyłanie sms’a na komórkę.
  2. z ciekawych rzeczy w queryStringu pojawiło się słówko 'whithin’ określające częstość wykonywania akcji w sekundach. Jeśli potestujecie nie zdziwcie się jak różną długość mają sekundy (;
  3. w odróżnieniu od poprzednich przykładów wykonywane jest ’ExecNotificationQuery’ czyli żądanie powiadomienia.
  4. pętla jest ustawiona na 10 zdarzeń w celach testowych. Można wyrzucić ten warunek ustawiając ją na nieskończoną.
  5. jeśli nie interesuje nas ciągły monitoring, a jedynie sprawdzanie wartości krytycznych, można zoptymalizować program modyfikując queryString na "select * from __instanceModificationEvent within 1 where targetInstance isa 'Win32_Processor' and targetinstance.LoadPercentage>80" co spowoduje, że skrypt będzie otrzymywał jedynie zdarzenia krytyczne [czyli niepotrzebna jest również jedna procedurka].
  6. nie trudno się domyśleć, że w ten sposób można modyfikować wszystko to, co można również robić za pomocą performanceMonitora
  7. jedyną uwagą z praktyki jest to, że czasem dość dziwnie się to zachowuje – czasem się jakby blokuje, sekundy mają różną długość i tym podobne… no ale to juz pozostawiam bez dodatkowych komentarzy. 'wyjdzie w praniu’.

Skąd brać informacje o obiektach i ich właściwościach

   Jak do tej pory wszystko ładnie – tylko brakuje nadal bardzo ważnego elementu. Teraz warto byłoby napisać cos samemu – i skąd wziąć informacje na temat obiektów, ich nazw i właściwości. Na to jest wiele sposbów.
Jako pierwszy podam bardzo przydatny obrazek [>700kb!], który warto sobie powiesić nad biurkiem, jeśli będzie się pracowało z WMI. Obrazek ten to część schematu obiektów WMI. Nie jest to oczywiście pełny schemat ale są na nim najważniejsze rzeczy.

Bardziej przydatnym zestawem narzędzi jest WMI SDK. SDK czyli Software Developement Kit jest dodawany do większości produktów Microsoftu w celu ułatwienia develeperom wykorzystanie istniejących mechanizmów. Wszelkie niezbędne narzędzia, które ułatwią pracę z WMI można zassać ze stron Microsoftu. Jest to zestaw narzędzi składający się z CIM viewer’a i Object viewer’a – przydatnego do sprawdzenia jakie są obiekty, ich metody etc, Event Viewera i Event Registrator’a – pomocnego przy pracy ze zdarzeniami.

Bardzo śmiesznym i przydatnym narządkiem, od którego polecam zacząć, jest scriptomatic. Mała aplikacja hta jest dostępna ze stron microsoftu. Programik służy do automatycznego generowania skryptów WMI. Cały pliczek wraz z komentarzami [ok. 50%] zajmuje 6oKb więc nie należy liczyć na nic rozbudowanego. Zarazem zdumiewa pomysłowością i jest wspaniałym przykładem na to, co i jak można zrobić za pomocą WMI. Pokazuje jak samemu – bez dodatkowych narzędzi – można dowiedzieć się jakie są klasy, obiekty i parmetry. Polecam przejrzeć źródło. Wraz ze skryptem dostarczona jest dokumentacja napisana przez jego twórców.

Monikery

   Pozostaje skończyć wątek, który wplatał się gdzieś w międzyczasie – czyli jeszcze kilka słów o monikerach. Wszystkie możliwości monikera przedstawia poniższy skrypt:

auth=",authority=ntlmdomain:mydomain"
authLv=",authenticationLevel=pktPrivacy"
specRight=",(Debug,!Shutdown)"
impLv="{impersonationLevel=impersonate" & authLv & auth & specRight & "}"
strLocale="[locale=ms_409]"
strCompName="."
strNameSpace="ROOTCIMV2"
strClass="Win32_LogicalDisk=""C:"""

strMoniker= impLv & strLocale & "!\" & strCompName & strNameSpace & ":" & strClass

Set monikerTest = GetObject("winmgmts:" & strMoniker )
wscript.echo monikerTest.size

W przykładzie tym widać chyba wszystkie możliwe części monikera – część dotycząca autoryzacji, specjanych uprawnień, ustawienie lokali [strona kodowa], nazwa systemu, kontext nazewniczy oraz klasa do której się dołączamy wraz z konkretną instancją obiektu.

Resumeé

   Także na pytanie „co to jest WMI” odpowiedź jest następująca:
WMI jest implementacją WBEM zatwierdzoną przez DMTF i opartą na CIMOM, który pośredniczy pomiędzy konsumentem WMI a provider’em konkretnego zasobu-obiektu poprzez obiekt COM.

A bardziej poważnie:

WMI jest mechanizmem o bardzo prostej idei a dającej potężne możliwości – zwłaszcza osob zajmującym się na codzień administracją systemów. Czyli nie będących stricte developerami, ale potrzebującymi narzędzi, których brakuje. WMI daje możliwość napisania sobie narzędzi samemu w stosunkowo prosty sposób.

Linki/referencje

-o((:: sprEad the l0ve ::))o-