Microsoft Outlook troubleshooting
Wstęp do PowerShell

Wstęp do PowerShell

autor CodeTwo 2006-08-12 00:00:00 w Inne
Spis treści:
Wstęp
Pierwsze kroki
System plików
Model dostawców danych
Tworzenie i wykorzystanie obiektów .NET
Praca z obiektami COM
Wykorzystanie WMI
Skrypty i funkcje
Podsumowanie
Zasoby


Wstęp

PowerShell, jeszcze do niedawna znany jako Monad, to nowa powłoka i język skryptowy firmy Microsoft. Podobnie jak w innych powłokach można używać go z poziomu linii poleceń, jednak jest kilka cech, które zdecydowanie go wyróżniają, przede wszystkim zastosowano w nim zupełnie nowe podejście do przetwarzania danych. W powłokach znanych do tej pory przetwarzanie oparte jest o format tekstowy - wynik działania poleceń ma format tekstu i w takiej też formie przekazywany jest jako argument lub strumień wejściowy do innych poleceń. PowerShell zbudowany został na platformie .NET Framework i wszystkie jego komendy operują na obiektach .NET, również wynik ich działania to zazwyczaj obiekty. Z jednej strony PowerShell wprowadza bardzo duży zestaw wbudowanych komend, z drugiej - pozwala wykorzystywać wszystkie obiekty dostępne w .NET Framework. Daje to ogromne możliwości zarówno użytkownikom wykorzystującym go do automatyzacji codziennych zadań jak i bardziej ambitnym administratorom, którzy przy jego pomocy mogą tworzyć naprawdę zaawansowane i produktywne skrypty pozwalające zaoszczędzić czas potrzebny do wykonywania żmudnych zadań administracyjnych. PowerShell został stworzony w taki sposób, aby umożliwić zarządzanie niemal wszystkimi aspektami systemu czy organizacji, a umożliwiają to mocno rozbudowane funkcje powłoki i elastyczny język. Można wykorzystywać go zarówno do tworzenia skryptów podobnych do tych, jakie do tej pory tworzone były w powłoce Windows i w VBScript pod Windows Script Host (WSH), ale także do pisania programów z użyciem obiektów .NET, COM i WMI.

Aktualna wersja PowerShell to Release Candidate 1 (RC1), można pobrać ją z adresu http://www.microsoft.com/technet/scriptcenter/topics/msh/download.mspx, najpierw należy jednak zainstalować .NET Framework 2.0 (http://msdn.microsoft.com/netframework/downloads/updates/). PowerShell można używać na systemach Windows XP SP2, Windows 2003, Windows Vista.

W artykule nie zamierzam opisywać składni języka PowerShell, ma to być raczej przegląd podstawowych możliwości nowej powłoki. PowerShell zawiera wbudowaną pomoc oraz komendę pozwalającą odczytać właściwości i funkcje składowe obiektów .NET, na których operuje. Znając tylko podstawy języka powłoki oraz najważniejsze komendy już po kilku godzinach pracy i eksperymentów, sami możemy poznawać nowe komendy i obiekty oraz ich funkcjonalność. Dzięki temu PowerShell jest stosunkowo prosty do poznania i staje się środowiskiem przyjaznym dla administratorów. Zaletą powłoki jest to, że zawiera dużo wbudowanych komend i często, aby wykonać jakieś zadanie administracyjne wystarczy wywołanie pojedynczej komendy z odpowiednimi parametrami. Używając języka powłoki oraz wbudowanych komend, można tworzyć własne parametryzowane skrypty implementujące bardziej zaawansowane zadania. Skrypty można wywoływać w powłoce tak samo jak istniejące komendy języka. W ten sposób można stworzyć własną bibliotekę skryptów, która pozwoli administratorowi przyspieszyć i zautomatyzować zarządzanie organizacją.


Pierwsze kroki

Zobaczmy więc, jak wygląda praca z powłoką PowerShell. Po jej zainstalowaniu skrót do powłoki znajdziemy w menu Start | Programy | Windows PowerShell. Zacznijmy od jakiegoś prostego zadania, zajmijmy się listą usług systemowych (serwisów) zainstalowanych na lokalnym komputerze. Komenda wyświetlająca listę wszystkich serwisów to get-service. Po wpisaniu jej w konsoli i naciśnięciu klawisza Enter wyświetlone zostaną wszystkie zainstalowane serwisy wraz z podstawowymi informacjami, jak: status, nazwa, nazwa wyświetlana:
PS C:\> get-service

Status   Name               DisplayName
------   ----               -----------
Running  ADTopologyService  Microsoft Exchange Active Directory...
Running  AeLookupSvc        Application Experience Lookup Service
Stopped  Alerter            Alerter
Stopped  ALG                Application Layer Gateway Service
Stopped  AppMgmt            Application Management
Stopped  aspnet_state       ASP.NET State Service
Stopped  AudioSrv           Windows Audio
...
Teksty wyświetlane w konsoli pisał będę czcionką o stałej szerokości, dodatkowo polecenia wpisywane przez użytkownika będą pisane czcionką pogrubioną. Komenda w PowerShell w języku anglielskim nazywana jest cmdlet - wymawiane commandlet. Ja będę używał polskiego określenia: komenda.

Wszystkie komendy w PowerShell nazywane zostały w jednolity sposób w konwencji: czasownik-rzeczownik. Czasownik określa, jaka akcja ma zostać wykonana na obiekcie opisanym przez rzeczownik. Podejście takie bardzo ułatwia wyszukiwanie komend dostępnych w powłoce oraz ich funkcjonalności. Bardzo przydatna jest komenda get-command, która zwraca listę wszystkich komendy powłoki. Aby znaleźć komendy operujące na serwisach systemowych, wywołamy ją w następujący sposób:
PS C:\> get-command *-service

CommandType     Name         		Definition
-----------     ----         		----------
Cmdlet          get-service  		get-service [[-ServiceName] String[]] ...
Cmdlet          new-service  		new-service [-ServiceName] String [-Pa...
Cmdlet          restart-service		restart-service [-ServiceName] String[...
Cmdlet          resume-service		resume-service [-ServiceName] String[]...
Cmdlet          set-service			set-service [-ServiceName] String [-Di...
Cmdlet          start-service		start-service [-ServiceName] String[] ...
Cmdlet          stop-service		stop-service [-ServiceName] String[] [...
Cmdlet          suspend-service		suspend-service [-ServiceName] String[...
Powyższe wywołanie zwraca listę wszystkich komend kończących się ciągiem znaków "-service". Jak widzimy argumenty komend mogą zawierać podstawowe znaki dopasowania, takie jak na przykład: *, ?, [a-z0-9], [as] (w PowerShell można używać również wyrażeń regularnych).

Aby uzyskać pomoc na temat komendy, wystarczy wywołać ją z parametrem -? lub użyć komendy get-help przekazując jej nazwę interesującej nas komendy. Jeśli chcemy zobaczyć, do czego możemy wykorzystać polecenie set-service, należy wpisać:
PS C:\> set-service -?
lub
PS C:\> get-help set-service
Polecenie get-help wyświetla pomoc na wiele różnych tematów dotyczących powłoki, od opisu komend, aż do gramatyki języka. Wystarczy wpisać get-help *, aby wyświetlić listę wszystkich tematów dostępnych w pomocy.

Jak wcześniej wspomniałem, PowerShell operuje na obiektach .NET. Wykorzystana przez nas komenda get-service zwraca tak naprawdę kolekcję obiektów typu System.ServiceProcess.ServiceController.

Jedną z najbardziej pomocnych komend pozwalających dowiedzieć się, jakiego typu obiekty zwraca dana komenda oraz jakie właściwości i metody posiada obiekt, jest komenda get-member:
PS C:\> get-service SMTPSVC | get-member

    TypeName: System.ServiceProcess.ServiceController

Name                      MemberType    Definition
----                      ----------    ----------
...
Start                     Method        System.Void Start(), System.Void Start(String[] args)
Stop                      Method        System.Void Stop()
...
DependentServices         Property      System.ServiceProcess.ServiceController[] DependentS
DisplayName               Property      System.String DisplayName {get;set;}
MachineName               Property      System.String MachineName {get;set;}
ServiceHandle             Property      System.Runtime.InteropServices.SafeHandle ServiceHan
ServiceName               Property      System.String ServiceName {get;set;}
ServicesDependedOn        Property      System.ServiceProcess.ServiceController[] ServicesDe
ServiceType               Property      System.ServiceProcess.ServiceType ServiceType {get;}
Site                      Property      System.ComponentModel.ISite Site {get;set;}
Status                    Property      System.ServiceProcess.ServiceControllerStatus Status
W powyższym przykładzie użyliśmy jej, aby sprawdzić, jakiego typu obiekty zwraca komenda get-service i jakie są ich składowe. Teraz możemy zobaczyć, że jest ich zdecydowanie więcej niż podstawowe informacje o nazwie, stanie i opisie serwisu wyświetlone na początku przez funkcję get-service. Gdy obiekty lub obiekt będący wynikiem działania komendy wyświetlony ma zostać w konsoli, to używany jest standardowy "formater", który dla znanych sobie obiektów (a takim jest obiekt ServiceController), wyświetla dla wygody użytkownika tylko podstawowe ważne informacje. Jeśli chcemy poznać wszystkie metody i właściwości obiektu, powinniśmy zastosować właśnie komendę get-member na zwracanym obiekcie. Jeśli chcemy otrzymać tylko listę właściwości obiektu należy użyć parametru -membertype z wartością "property":
PS C:\ > get-service | get-member -membertype property
Jeśli chcemy dostać tylko metody obiektu, zamiast "property" powinniśmy użyć wartości "method".

W tym przykładzie pojawia się również znak przekazania strumienia |, który jest dobrze znany z innych powłok. Pozwala on łączyć ze sobą wiele komend przekazując wynik z poprzednio wykonanej komendy do następnej. Przykładowo poniższa komenda wyświetla w tabeli wszystkie serwisy pogrupowane alfabetycznie według statusu a potem nazwy.
PS C:\> get-service | sort-object status,name | format-table status,name,ServicesDependedOn

Status  Name
------  ----
Stopped Alerter                     {LanmanWorkstation}
Stopped ALG                         {}
Stopped AppMgmt                     {}
Stopped aspnet_state                {}
Stopped AudioSrv                    {RpcSs, PlugPlay}
Stopped BITS                        {EventSystem, RpcSs}
Stopped CiSvc                       {RPCSS}
Stopped ClipSrv                     {NetDDE}
...
Running Dhcp                        {Tcpip, Afd}
Running dmserver                    {RpcSs, PlugPlay}
Running DNS                         {Tcpip, RpcSs, Afd}
Running Dnscache                    {Tcpip}
Running EdgeTransportSvc            {ADTopologyService}
Running ERSvc                       {RpcSs}
Running Eventlog                    {}
Running EventSystem                 {RPCSS}
...
Najpierw get-service zwraca obiekty wszystkich serwisów, sort-object sortuje je po statusie i nazwie, a na końcu komenda format-table wyświetla je w tabeli zawierającej 3 kolumny: status, name i ServicesDependedOn. ServicesDependedOn zawiera listę serwisów, od których zależny jest dany serwis. Wiemy, że właściwość ta istnieje w obiekcie ServiceController dzięki temu, że wywołaliśmy na nim komendę get-member. ServicesDependedOn jest tablicą obiektów typu ServiceController. Jeśli chcemy znaleźć dokładny opis każdej z metod czy właściwości danego obiektu, wystarczy zaglądnąć do dokumentacji .NET Framework na stronie http://msdn2.microsoft.com/en-us/library/default.aspx. Przykładowo klasa ServiceController jest opisana na stronie http://msdn2.microsoft.com/en-us/library/system.serviceprocess.servicecontroller.aspx

Jak widzimy, znając dwie podstawowe komendy get-help i get-member oraz korzystając z dokumentacji biblioteki .NET, sami możemy dużo nauczyć się o funkcjonalności innych komend PowerShell.

Kolejną ważną komendą bardzo często używaną w poleceniach i skryptach PowerShell jest where-object. Umożliwia ona wykonywanie operacji na obiektach i ich filtrowanie, gdy przesyłane są przez strumień z jednej komendy do kolejnej. Jeśli chcemy uzyskać listę wszystkich procesów, które są obecnie uruchomione w systemie powinniśmy wykonać następujące polecenie:
PS C:\> get-service | where-object {
>> $_.status -eq "running" } |
>> format-table displayname
>>

DisplayName
-----------
Application Layer Gateway Service
Apache
Windows Audio
Background Intelligent Transfer Service
COM+ System Application
Cryptographic Services
DCOM Server Process Launcher
DHCP Client
Logical Disk Manager
DNS Client
Error Reporting Service
Event Log
...
Pierwsza rzecz, na jaką należy zwrócić tu uwagę to, że polecenia nie zostały wpisane w jednej linii. Jeśli kończymy linię odpowiednim znakiem, na podstawie którego powłoka może wykryć, że jeszcze będą wpisywane kolejne polecenia, to po naciśnięciu Entera, przechodzi do nowej linii i wyświetla znak >> pozwalający na kontynuację. Wykonanie wszystkich wpisanych poleceń następuje po dwukrotnym wciśnięciu klawisza Enter.

W powyższym przykładzie get-service zwraca kolekcję obiektów i każdy z nich po przesyłaniu przez strumień jest po kolei przetwarzany przez komendę where-object. Zmienna $_ umożliwia nam odwołanie się do obecnie przetwarzanego obiektu w strumieniu. Tutaj korzystamy z właściwości status obiektów ServiceController reprezentujących serwisy i sprawdzamy czy ma wartość "running". Jeśli tak, to instrukcje umieszczone w nawiasach klamrowych komendy where-object generują wartość logiczną prawda i obiekty i są przesyłane dalej w strumieniu. Jeśli status serwisu nie jest równy "running", to obiekt nie zostanie przekazany do komendy format-table i nie zostanie wyświetlony w wynikach na konsoli.

Kolejne polecenie, właściwie można nazwać je już krótkim skryptem, jest nieco bardziej skomplikowane - wyświetla listę wszystkich serwisów, które zależne są od usługi RpcSs (Remote Procedure Call - RPC):
PS C:\> get-service | where-object {
>>   foreach( $service in $_.ServicesDependedOn ) {
>>      if( $service.name -eq "RpcSs" ) {
>>         return $true 
>>      }
>>   }
>> } |
>> sort-object displayname |
>> format-table displayname, name, status -autosize
>>

DisplayName                                 Name               Status
-----------                                 ----               ------
Background Intelligent Transfer Service     BITS              Running
COM+ Event System                           EventSystem       Running
COM+ System Application                     COMSysApp         Running
Cryptographic Services                      CryptSvc          Running
Distributed Link Tracking Client            TrkWks            Running
Distributed Transaction Coordinator         MSDTC             Running
Error Reporting Service                     ERSvc             Running
GhostStartService                           GhostStartService Stopped
Help and Support                            helpsvc           Running
Human Interface Device Access               HidServ           Stopped
Indexing Service                            CiSvc             Stopped
IPSEC Services                              PolicyAgent       Running
Logical Disk Manager                        dmserver          Running
...
Do wyszukania serwisów zależnych od RPC wykorzystamy znaną nam już właściwość ServicesDependedOn - jest to tablica serwisów (obiektów typu ServiceController), od których dany serwis jest zależny. Powinniśmy wyświetlić więc każdy serwis, który w ServicesDependedOn zawiera serwis RpcSS. Listę wszystkich serwisów zwraca get-service. where-object tutaj zawiera instrukcję języka foreach - jest to iterator, który przesuwa się kolejno po elementach kolekcji lub tablicy i pozwala na ich przetwarzanie, w tym wypadku jest to tablica ServicesDependedOn, która zawiera obiekty typu ServiceController. Każdy obiekt-serwis z tablicy jest kolejno przypisywany do zmiennej $service - zmienne w PowerShell poprzedza się znakiem $. Sprawdzamy więc, czy właściwość name (właściwość obiektu ServiceController) ma wartość RpcSs. Jeśli tak, to warunek w where-object jest spełniony dla danego serwisu przekazanego z komendy get-service i serwis zostanie dalej przekazany w strumieniu do następnej komendy. Dwie ostatnie linijki skryptu to już tylko zabiegi kosmetyczne. Najpierw sortujemy obiekty po ich nazwie, a następnie wyświetlamy je w tablicy. Użyliśmy parametru -autosize dla komendy format-table , aby kolumny zostały dokładnie dopasowane do zawartości tabeli.

Powyższy skrypt ma jedynie wartość pokazową, jego celem jest zaprezentowanie konstrukcji językowych w PowerShell. Jeśli bliżej przyjrzymy się składowym obiektu ServiceController, zauważymy, że to samo możemy osiągnąć używając właściwości DependentServices, która zawiera wszystkie serwisy zależne od danego serwisu. Aby osiągnąć te same wyniki, wystarczy więc użyć następującego polecenia:
PS C:\> (get-service RpcSs).DependentServices | sort-object displayname | 
>> format-table displayname, name, status -autosize
Podobnie jak właściwości, możemy używać metod obiektów .NET. Obiekt ServiceController zawiera funkcje pozwalające na zatrzymywanie i uruchamianie serwisów: Start, Stop, Pause, Continue. Poniższy przykład zatrzymuje serwis "Automatic Updates":
PS C:\> $service = get-service -displayname "automatic up*"
PS C:\> $service.Stop()
PS C:\>
Ponieważ nie znamy nazwy serwisu, wybieramy serwis po jego nazwie wyświetlanej "displayname", a że nie chce nam się pisać pełnej jego nazwy to używamy dopasowania "automatic up*". Należy zauważyć, że -displayname jest tutaj parametrem komendy get-service, a nie właściwością obiektu ServiceController. Dlatego nie możemy użyć na przykład następującej konstrukcji: get-service -status "running". Dla przejrzystości serwis przypisywany jest najpierw do zmiennej $service, na której w następnej linii wywołujemy metodę Stop, aby zatrzymać serwis. Powłoka nie zwraca żadnego błędu, co oznacza, że metoda została wywołana i wykonana pomyślnie. Jeśli chcemy ponownie uruchomić serwis, wystarczy teraz wywołać $service.Start().

Powyższe przykłady pozwoliły nam zobaczyć jak wygląda praca z PowerShell. Wiemy już, że podstawową rolę spełniają obiekty .NET Framework, znamy także podstawowe komendy powłoki oraz zapoznaliśmy się z pojęciem strumienia danych i przekazywania w nim wyników dla kolejnych komend. Teraz przyjrzymy się kilku podstawowym obszarom działań często wykorzystywanym przez administratorów i zobaczymy jak używać w nich PowerShell.


System plików

Jednym z podstawowych zadań w codziennej pracy z komputerem są operacje na plikach. Podobnie jak inne powłoki PowerShell udostępnia odpowiednie komendy pozwalające na taką pracę. Wszystkim znane są takie polecenia jak CD czy DIR wykorzystywane w obecnej powłoce systemu Windows. W PowerShell ich odpowiednikami są set-location i get-childitem. set-locaiton zmienia obecne położenie w systemie plików. Aby zmienić obecne położenie na inny dysk należy zawołać set-location D: (wpisanie samego D: również zadziała), natomiast aby zmienić obecne położenie na folder znajdujący się wyżej w hierarchii należy wpisać set-location .. .

get-childitem, gdy użyte bez dodatkowych parametrów, wypisuje pliki i foldery dostępne w obecnym folderze. Jeśli użyjemy parametru -recurse otrzymamy wszystkie pliki i foldery dostępne w obecnym folderze i jego podfolderach. get-childitem podobnie jak inne komendy PowerShell zwraca obiekty .NET. Są one typu System.IO.FileInfo dla plików oraz System.IO.DirectoryInfo dla folderów. Wystarczy użyć komendy get-member, aby dowiedzieć się, jakie właściwości i metody udostępniają.

Jeśli interesują nas informacje dotyczące pojedynczego pliku lub foldera można użyć funkcji get-item z parametrem -path określającą dany element.

Inne często używane funkcje służące do operacji na plikach i folderach to:
- new-item - tworzy nowe elementy
- remove-item - usuwa istniejące elementy
- copy-item - kopiuje elementy
- move-item - przenosi elementy
- rename-item - zmienia nazwę elementów
- invoke-item - uruchamia program lub otwiera plik przy pomocy skojarzonego programu

Poniższa komenda tworzy plik test.txt w obecnym folderze C:\
PS C:\> new-item test.txt -type file

Directory: Microsoft.PowerShell.Core\FileSystem::C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2006-06-25     16:39          0 test.txt
Poniższa komenda usuwa z foldera c:\temp\ i wszystkich jego podfolderów pliki z rozszerzeniem "tmp":
PS C:\> get-childitem c:\temp\* -include *.tmp -recurse | remove-item
Aby przeczytać zawartość pliku należy użyć komendy get-content, a out-file, aby zapisać dane lub wynik jakiejś komendy do pliku.

Komendy export-csv i import-csv pozwalają zapisywać i wczytywać dane z plików CSV, gdzie poszczególne wartości rozdzielone są znakiem przecinka. Dzięki nim można w prosty sposób dane wygenerowane w PowerShell przetwarzać później w innej aplikacji, na przykład w Excel'u.
PS C:\> get-service | export-csv services.csv -NoTypeInfo
Powyższy skrypt wyeksportuje wszystkie informacje o serwisach do pliku c:\services.csv. Parametr -NoTypeInfo użyliśmy po to, aby w wynikowym pliku nie pojawiła się w pierwszej linii informacja o typie eksportowanych obiektów, która w tym wypadku zawierałaby tekst:
#TYPE System.ServiceProcess.ServiceController
Komenda export-csv nie ma żadnego parametru, który pozwalałby określić, jakie właściwości obiektów powinny zostać wyeksportowane. W tym celu możemy jednak użyć komendy select-object, która wybiera z obiektu tylko określone właściwości i tylko je przesyła dalej w strumieniu - zawęża więc zbiór właściwości dostępnych dla obiektu.
PS C:\> get-service | select-object name, displayname, status | 
>> export-csv services.csv -notypeinfo
Powyższa komenda eksportuje do pliku C:\services.csv tylko właściwości name, displayname i status dla wszystkich serwisów w systemie.


Model dostawców danych

Komenda get-psdrive wyświetla wszystkie dyski logiczne dostępne w systemie. Po jej wywołaniu otrzymamy tego typu wynik:
PS C:\> 

Name       Provider      Root
----       --------      ----
A          FileSystem    A:\
Alias      Alias
C          FileSystem    C:\
cert       Certificate   \
D          FileSystem    D:\
E          FileSystem    E:\
Env        Environment
F          FileSystem    F:\
Function   Function
G          FileSystem    G:\
H          FileSystem    H:\
HKCU       Registry      HKEY_CURRENT_USER
HKLM       Registry      HKEY_LOCAL_MACHINE
I          FileSystem    I:\
J          FileSystem    J:\
O		   FileSystem    O:\
Variable   Variable
Możemy zauważyć, że oprócz dysków systemu plików wyświetlone zostały także takie lokalizacje jak: Alias, cert, Env, Function, HKCU, HKLM, Variable. Są to inne bazy danych, które mogą być przeglądane w taki sam sposób jak system plików, przy użyciu funkcji opisanych w poprzednim podrozdziale: set-location, get-item, get-childitem, itp. Ten sposób dostępu do danych realizowany jest przez tak zwanych dostawców danych. Aby otrzymać ich listę należy wywołać komendę get-psprovider:
PS C:\> get-psprovider

Name                 Capabilities
----                 ------------
Alias                ShouldProcess
Environment          ShouldProcess
FileSystem           Filter, ShouldProcess
Function             ShouldProcess
Registry             ShouldProcess
Variable             ShouldProcess
Certificate          ShouldProcess
Wyświetleni zostali standardowi dostawcy danych, którzy pozwalają na proste przeglądanie i edycję takich danych jak: zdefiniowane aliasy, zmienne środowiskowe systemu, system plików, zdefiniowane funckje, rejestr, zmienne powłoki, certyfikaty. Szczególnie warto zwrócić tu uwagę na dostawców Registry i Certificate. Pierwszy z nich pozwala przeglądać dwie gałęzie rejestru HKEY_CURRENT_USER i HKEY_LOCAL_MACHINE, drugi - certyfikaty zainstalowane na lokalnym komputerze.

W przykładzie poniżej najpierw zmieniamy lokalizację na gałąź rejestru HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Outlook\Security, a później przy pomocy komendy new-itemproperty tworzymy nowy klucz w rejestrze o nazwie Level1Remove i wartości ".asp;.dll;.exe;.pst". W ten sposób odblokowujemy niektóre potencjalnie niebezpieczne załączniki w programie Microsoft Outlook 2003:
PS C:\> set-location HKCU:
PS HKCU:\> set-location Software\Microsoft\Office\11.0\Outlook\Security
PS HKCU:\Software\Microsoft\Office\11.0\Outlook\Security> new-itemproperty -path . -name Level1Remove -value ".asp;.dll;.exe;.pst"

PSPath       : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Outlook\Security
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_CURRENT_USER\Software\Microsoft\Office\11.0\Outlook
PSChildName  : Security
PSDrive      : HKCU
PSProvider   : Microsoft.PowerShell.Core\Registry
Level1Remove : .asp;.dll;.exe;.pst
Spróbujmy teraz przyjrzeć się zawartości lokalizacji Alias:
PS c:\> set-location Alias:
PS Alias:\> get-childitem | format-table -autosize

CommandType Name    Definition
----------- ----    ----------
Alias       ac      Add-Content
Alias       asnp    Add-PSSnapin
...
Alias       gm      Get-Member
Alias       dir     Get-Childitem
...
Alias       cd      Set-Location
Aliasy to po prostu zamienniki nazw komend. Zazwyczaj są to skróty stosowane w celu szybszego pisania skryptów. Jak widzimy w PowerShell możemy stosować DIR do wyświetlania zawartości foldera czy CD do zmiany obecnej lokalizacji, ponieważ są to domyślne zamienniki (aliasy) komend get-childitem i set-location.

Listę wszystkich aliasów można również otrzymać wywołując komendę get-alias. Aby utworzyć nowy alias należy użyć komendy set-alias.

Nie jest zalecane stosowanie aliasów w skryptach udostępnianych szerszemu gronu użytkowników, ponieważ może prowadzić to do niejednoznaczności i potencjalnych błędów. Każdy może mieć zdefiniowane w swoim środowisku inne aliasy i skrypty pochodzące od innych użytkowników mogą nie działać poprawnie. Używanie pełnych nazw komend może wymagać nieco więcej wysiłku, ale za to powoduje, że skrypty są bardziej przejrzyste i zrozumiałe.

Przyjrzyjmy się jeszcze zawartość lokalizacji Function, która zwiera wszystkie funkcje zdefiniowane obecnie w powłoce. Funkcje w PowerShell pełnią taką samą rolę jak w językach programowania, są to wydzielone algorytmy napisane przy użyciu komend i konstrukcji języka, które realizują pewne dowolnie określone wyodrębnione zadania. Funkcje wywoływane są podobnie jak komendy, zazwyczaj na wejściu przekazywane są im parametry i po wykonaniu obliczeń zwracają określony wynik. Użytkownik może definiować własne funkcje, które potem wykorzystywane są w pracy z powłoką.
PS HKCU:\Software\Microsoft\Office\11.0\Outlook\Security> set-location Function:
PS Function:\> get-childitem

CommandType     Name                                    Definition
-----------     ----                                    ----------
Function        prompt                                  'PS ' + $(Get-L ...
Function        TabExpansion                            ...
Function        Clear-Host                              $spaceType = [S ...
Function        more                                    param([string[] ...
Function        help                                    param([string]$ ...
Function        man                                     param([string]$ ...
Function        mkdir                                   param([string[] ...
Function        md                                      param([string[] ...
Function        A:                                      Set-Location A: 
Function        B:                                      Set-Location B: 
...
Function        Z:                                      Set-Location Z: 
Przeglądając wyniki widzimy kilka ciekawych standardowych funkcji. Między innymi funkcję prompt, która definiuje postać znaku zachęty wyświetlanego przez powłokę. Widzimy także kilka funkcji implementujących komendy znane ze "starej" powłoki Windows'a: more, help, mkdir. Funkcje A: do Z: implementują prostą komendę set-location, dzięki temu możemy zmieniać lokalizację na różne dyski systemu plików używając krótkiego wyrażenia <nazwa dysku><znak dwukropka>, o czym wspominałem wcześniej. TabExpansion jest funkcją wywoływaną po wciśnięciu znaku tabulacji. Domyślnie zaimplementowana jest jako podpowiadacz komend powłoki. Jeśli wpiszemy część nazwy komendy i wciśniemy klawisz tabulacji to powłoka spróbuje wyszukać nazwę odpowiedniej komendy i ją uzupełnić, często znacznie przyspiesza to pisanie poleceń i skryptów. Jeśli ktoś potrzebuje, to może nadpisać domyślną definicję standardowych funkcji lub może je usunąć używając komendy remove-item.

W wyżej wyświetlonych wynikach nie możemy w pełni odczytać definicji wszystkich funkcji, ponieważ szerokość okna konsoli jest zbyt mała. Użyjemy następującego polecenia, aby odczytać definicję funkcji mkdir:
PS Function:\> get-item mkdir | get-content
param([string[]]$paths); New-Item -type directory -path $paths
Jak widzimy funkcja używa komendy new-item do utworzenia nowego foldera w lokalizacji określonej przez argument $paths przekazywany do funkcji podczas jej wywołania. Jeśli koś chciałby zobaczyć bardziej zaawansowany skrypt PowerShell, polecam lekturę funkcji TabExpansion.

Możemy zauważyć, że w przytoczonych tu przykładach operujących na elementach dostawców Registry i Function używamy tych samych komend, co w przykładach dotyczących systemu plików. Omówione komendy takie jak: set-location, get-item, new-property służą więc ogólnie do pracy z danymi udostępnianymi przez dostawców danych, a nie tylko do manipulacji obiektami odnoszącymi się to systemu plików, po prostu system plików jest udostępniany przez jednego z dostawców o nazwie FileSystem . Przykładowo funkcja get-content służy do pobierania zawartości elementu i równie dobrze przy jej pomocy można odczytać zawartość pliku jak i definicję funkcji.


Tworzenie i wykorzystanie obiektów .NET

Ponieważ PowerShell zbudowany jest na platformie .NET, operuje na danych, które w przeważającej większości są obiektami bibliotek .NET. Do tej pory widzieliśmy kilka przykładów działających na obiektach zwróconych jako wynik wywołania wbudowanych komend. Jednak użytkownik ma również możliwość tworzenia (instancjowania) nowych obiektów .NET przy użyciu komendy new-object. Przykładowo poniższy skrypt tworzy nowy obiekt System.Net.WebClient i przypisuje go do zmiennej $webClient.
PS C:\ > $webClient = new-object System.Net.WebClient
Obiekt możemy wykorzystać teraz do pobrania zawartości jakiejś strony internetowej. Przeczytajmy ostatnie aktualności z witryny wss.pl:
PS C:\ > $rssData = $webClient.DownloadString("http://www.wss.pl/RSS/News.aspx") 
Pobraną zawartość z agregatora RSS przypisujemy do zmiennej $rssData, która otrzymuje typ łańcucha tekstowego. Aby móc wygodnie czytać zawartość RSS, powinniśmy utworzyć dokument XML i wczytać do niego dane:
PS C:\> $xmlDoc = new-object System.Xml.XmlDocument
PS C:\> $xmlDoc.LoadXML( $rssData )
Teraz możemy wykorzystać możliwości powłoki pozwalające w bardzo prosty sposób przeglądać dokumenty XML. Poniżej korzystając ze standardowych elementów RSS odczytujemy ostatnio dodanego newsa:
PS C:\> $xmlDoc.rss.channel.item[0]

title       : Windows Vista Step-by-Step Guides for IT Professionals 2.8
link        : http://wss.pl/News/7795.aspx
description : Microsoft udostępnił kolejną wersję kilkunastu poradników...
author      : anonymous@wss.pl (Leszek Kędzior)
pubDate     : Sun, 25 Jun 2006 00:53:44 +0200 
W powyższym przykładzie widzimy jak łatwo można poruszać się po zawartości plików XML. Aby dotrzeć do kolejnego elementu w dokumencie wystarczy wpisać po kropce jego nazwę.

Jeśli chcielibyśmy wyświetlić wszystkie newsy znajdujące się obecnie w RSS powinniśmy użyć następującego polecenia:
PS C:\> $xml.getElementsByTagName("item") | format-table title

title
-----
Windows Vista Step-by-Step Guides for IT Professionals 2.8
BizTalk 2006 Documentation Update
Zrzuty ekranowe polskiej edycji Windows Vista
ActiveX będą mogli instalować użytkownicy standardowi
Użytkownicy WSS szturmują MTS 2006
Kolejna luka w Excelu
100 narzędzi by zbadać bezpieczeństwo sieci


Praca z obiektami COM

W PowerShell możemy również tworzyć i wykorzystywać obiekty COM. Obiekty COM można instancjować wykorzystując tą samą komendę new-object, której używa się do instancjowania obiektów .NET. Należy wywołać ją tylko z dodatkowym parametrem -ComObject. Poniższy przykład tworzy instancję aplikacji Microsoft Outlook i wykorzystując model obiektowy Outlook'a wyświetla liczbę nieprzeczytanych wiadomości znajdujących się w skrzynce odbiorczej użytkownika:
PS C:\> $outlook = new-object -ComObject "Outlook.Application"
PS C:\> $unreadCount = $outlook.GetNamespace("MAPI").GetDefaultFolder(6).UnReadItemCount
PS C:\> $unreadcount
4


Wykorzystanie WMI

PowerShell posiada specjalną komendę get-wmiobject pozwalającą wykorzystywać obiekty WMI do pobierania informacji i zarządzania wieloma aspektami systemu. Poniższa komenda wyświetla informacje o procesorach komputera o nazwie ALFA:
PS C:\> get-wmiobject win32_processor -ComputerName ALFA

AddressWidth                : 32
Architecture                : 0
Availability                : 3
Caption                     : x86 Family 15 Model 0 Stepping 7
ConfigManagerErrorCode      :
ConfigManagerUserConfig     :
CpuStatus                   : 1
CreationClassName           : Win32_Processor
CurrentClockSpeed           : 1400
...
__SERVER                    : ALFA
__NAMESPACE                 : root\cimv2
__PATH                      : \\ALFA\root\cimv2:Win32_Processor.DeviceID="CPU0"
Kolejny skrypt wyświetla informacje o procesie OUTLOOK.EXE uruchomionym na lokalnym komputerze:
PS C:\> get-wmiobject win32_process -filter "name='outlook.exe'"

ProcessName                : OUTLOOK.EXE
Handles                    : 1067
VM                         : 470216704
WS                         : 72015872
Path                       : C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE
Caption                    : OUTLOOK.EXE
CommandLine                : "C:\Program Files\Microsoft Office\OFFICE11\OUTLOOK.EXE"
CreationClassName          : Win32_Process
CreationDate               : 20060627153100.218750+120
CSCreationClassName        : Win32_ComputerSystem
...
Podobnie jak przy pracy z innymi obiektami możemy odwoływać się do poszczególnych właściwości obiektów WMI:
PS C:\> (get-wmiobject win32_process -filter "name='outlook.exe'").Handles
1121
Jeśli wywołamy komendę get-member na obiekcie WMI Win32_Process możemy odczytać wszystkie właściwość obiektu WMI, jednak zauważymy, że nie mamy dostępu do funkcji obiektu WMI:
PS C:\> get-wmiobject win32_process -filter "name='outlook.exe'" | get-member

    TypeName: System.Management.ManagementObject#root\cimv2\Win32_Process

Name                       MemberType            Definition
----                       ----------            ----------
Handles                    AliasProperty         Handles = Handlecount
ProcessName                AliasProperty         ProcessName = Name
VM                         AliasProperty         VM = VirtualSize
WS                         AliasProperty         WS = WorkingSetSize
add_Disposed               Method                System.Void add_Dispo...
Clone                      Method                System.Object Clone()
CompareTo                  Method                System.Boolean Compar...
CopyTo                     Method                System.Management.Man...
CreateObjRef               Method                System.Runtime.Remoti...
Delete                     Method                System.Void Delete(),...
Dispose                    Method                System.Void Dispose()
...
Caption                    Property              System.String Caption 
CommandLine                Property              System.String CommandLi...
CreationClassName          Property              System.StringreationCla...
CreationDate               Property              System.String CreationD... 
CSCreationClassName        Property              System.String CSCreatio...
CSName                     Property              System.String CSName {g...
Description                Property              System.String Descripti...
ExecutablePath             Property              System.String Executabl...
...
InvokeMethod               Method                System.Object InvokeMet...
...
Metody wyświetlone powyżej nie są metodami obiektu WMI Win32_Process, lecz metodami obiektu .NET typu System.Management.ManagementObject. Metody Win32_Process to GetOwner, GetOwnerSid, SetPriority, Terminate, które nie są tu widoczne. Obecnie można uzyskać do nich dostęp tylko poprzez funkcję InvokeMethod przekazując jej parametry funkcji docelowej w tablicy, co nie jest wygodnym rozwiązaniem, jakiego oczekiwalibyśmy od rozbudowanego środowiska skryptowego. To niepełne wsparcie dla obiektów WMI zostanie uzupełnione w wersji RC2 PowerShell'a zapowiadanej na koniec lata, dzięki czemu stanie się on efektywniejszym środowiskiem do używania WMI niż VBScript i WSH.


Skrypty i funkcje

W PowerShell użytkownik może tworzyć własne skrypty wykorzystując konstrukcje języka, komendy i funkcje powłoki, a także funkcje oraz inne skrypty zdefiniowane przez użytkownika. W ten sposób można utworzyć własną bibliotekę implementującą najczęściej wykonywane zadania administracyjne i używać jej do szybszego i efektywniejszego zarządzania systemami. Skrypt tworzymy w osobnym pliku tekstowym i zapisujemy go z rozszerzeniem ps1. Skrypt może realizować jakieś zadanie i wyświetlać jego wyniki, może też zawierać tylko definicję jednej lub wielu funkcji, które wykorzystywane są później przez inne skrypty lub przez użytkownika w bezpośredniej pracy z powłoką. Zobaczmy, jak wyglądają skrypty na dwóch prostych przykładach. Pierwszy z nich to funkcja wyświetlająca wszystkie adresy IP danego komputera, drugi - wyświetla okres czasu, który upłynął od ostatniego włączenia komputera.
# Komentarze w PowerShell poprzedzone są znakiem #
#
# Funkcja pobiera wszystkie adresy komputera o nazwie 
# określonej w parametrze $hostname. Jeśli parametr
# $format ma wartość "all", zwracane są szczegółowe
# informacje o adresie, w przeciwnym wypadku - tylko
# lista adresów IP.
#
function get-ipaddress()
{
	param($hostname, $format)

	if( !$hostname )
	{
		$hostname = [System.Net.Dns]::GetHostName()
	}

	$addresses = [System.Net.Dns]::GetHostAddresses( $hostname )

	if( $format -eq "all" )
	{
		$addresses
	}
	else
	{
		foreach( $address in $addresses )
		{
			$address.IPAddressToString
		}
	}
}
Powyższy skrypt zapiszmy w pliku get-ipaddress.ps1. Warto przy tworzeniu własnych funkcji i skryptów zachowywać konwencję nazewniczą czasownik-rzeczownik przyjętą w PowerShell. Jest to szczególnie pomocne, gdy skrypty używane są nie tylko przez ich autora. Innym użytkownikom znacznie łatwiej jest wtedy, zwłaszcza po dłuższym czasie, stwierdzić, jakie jest przeznaczenie skryptu. W skrypcie get-ipaddress.ps1 zaimplementowana jest funkcja pobierająca wszystkie adresy IP podanego komputera. Konstrukcja param($hostname, $format) określa, że funkcja może przyjmować dwa parametry o nazwach hostname i format. Parametr hostname to nazwa komputera, którego adresy powinna zwrócić funckja. Jeśli parametr ten nie zostanie podany, to funkcja pobiera adresy IP lokalnego komputera. Parametr format określa, w jakim formacie powinny zostać zwrócone wyniki. Jeśli ma on wartość "all"", to zwracana jest pełna informacja o adresach, w takiej formie jak zwraca je funckja GetHostAddresses - w przeciwnym wypadku zwracane są tylko adresy IP komputera. Do pobrania nazwy obecnego komputera oraz jego adresów IP używamy w skrypcie statycznych funkcji obiektu System.Net.Dns z biblioteki .NET.

Skrypt z definicją funkcji najwygodniej jest umieścić w bieżącej lokalizacji, w naszym przypadku będzie to bezpośrednio na dysku C:\. Jeśli wpiszemy teraz w konsoli nazwę pliku get-ipaddress.ps1, to zauważymy, że nie otrzymamy żadnego wyniku. Żadnego rezultatu nie daje również wpisanie samej nazwy funkcji. Aby funkcja była dostępna w powłoce musimy wpisać ścieżkę do pliku zawierającego jej definicję poprzedzając ją kropką i spacją:
PS C:\> . get-ipaddress.ps1
Pamiętajmy, że plik znajduje się w bieżącej lokalizacji, w przeciwnym wypadku musielibyśmy wpisać pełną ścieżkę do pliku. Jeśli teraz zmienimy lokalizację na Function: (set-location Function:), i zawołamy komendę get-childitem, to zauważymy, że na liście dostępnych funkcji znajduje się również nasza get-ipaddress. Teraz możemy wywoływać ją w powłoce:
PS C:\> get-ipaddress
192.168.100.1
192.168.195.1
192.168.152.1
Tutaj wywołaliśmy ją bez żadnych parametrów, więc zwróciła same adresy IP lokalnego komputera. Aby podać nazwę komputera i określić format wyniku powinniśmy zawołać funkcję w następujący sposób:
PS C:\> get-ipaddress www.outlook.pl all

IPAddressToString : 80.237.152.83
Address           : 1402531152
AddressFamily     : InterNetwork
ScopeId           :
IsIPv6Multicast   : False
IsIPv6LinkLocal   : False
IsIPv6SiteLocal   : False
Parametry można również przekazywać jawnie określając ich nazwę, wtedy nie musi być zachowana ich kolejność:
PS C:\> get-ipaddress -format all -hostname www.outlook.pl

IPAddressToString : 80.237.152.83
Address           : 1402531152
AddressFamily     : InterNetwork
ScopeId           :
IsIPv6Multicast   : False
IsIPv6LinkLocal   : False
IsIPv6SiteLocal   : False
Wynik zwracany przez naszą funkcję możemy przekazywać do standardowych komend PowerShell w celu dalszego przetwarzania, na przykład:
PS C:\> get-ipaddress www.outlook.pl all |
>> format-table IPAddressToString, AddressFamily -autosize
>>

IPAddressToString AddressFamily
----------------- -------------
80.237.152.83      InterNetwork 

Drugi przykładowy skrypt wyświetla czas, który upłynął od ostatniego uruchomienia komputera.
# Skrypt wyświetla czas, jaki upłynął od ostatniego 
# uruchomienia komputera.

function ParseDate
{
	param( $dateWMI )

	$year = [int]$dateWMI.substring(0,4)
	$month = [int]$dateWMI.substring(4,2)
	$day = [int]$dateWMI.substring(6,2)
	$hour = [int]$dateWMI.substring(8,2)
	$minute = [int]$dateWMI.substring(10,2)
	$second = [int]$dateWMI.substring(12,2)
	
	return new-object DateTime $year, $month, $day, $hour, $minute, $second
}

$lastBootTime = ParseDate $(get-wmiobject Win32_OperatingSystem).LastBootUpTime
$span = [TimeSpan]((get-date) -  $lastBootTime)
$span.ToString()
Powyższy skrypt pobiera czas ostatniego uruchomienia komputera wykorzystując obiekt WMI Win32_OperatingSystem i jego właściwość LastBootUpTime. Aby obliczyć różnicę czasu pomiędzy obecnym czasem a ostatnim uruchomieniem komputera, musimy zamienić datę zapisaną we właściwości obiektu WMI na obiekt DateTime z biblioteki .NET. Używamy do tego napisanej przez nas funkcji ParseDate. Różnicę czasu zapisujemy w zmiennej typu TimeSpan i wyświetlamy jej wartość w formacie tekstowym używając funkcji ToString na ekranie.

Zapiszmy skrypt w pliku C:\get-uptime.ps1. Aby go wywołać, wystarczy wpisać w konsoli jego nazwę, nie musimy wpisywać rozszerzenia pliku.
PS C:\> get-uptime
11:48:34.5156250


Podsumowanie

Mocno rozbudowane środowisko PowerShell z wieloma wbudowanymi komendami, wykorzystaniem obiektów .NET, możliwością korzystania z obiektów COM i WMI i innymi funkcjonalnościami, które nie zostały tu przedstawione, jak choćby możliwość korzystania z dzienników systemowych czy wyrażeń regularnych, staje się naturalnym wyborem, jeśli chodzi o programowanie codziennych i bardziej złożonych zadań administracyjnych. Zadania, które do tej pory realizowane były przy pomocy skryptów WSH, skryptów powłoki a nawet prostych programów, mogą zostać obsłużone przy wykorzystaniu PowerShell. PowerShell staje się jednolitą i centralną konsolą pozwalającą zarządzać niemal wszystkimi komponentami systemu czy sieci. Znacznie ułatwia to zwięzły język powłoki oraz rozbudowane komendy, potrafiące zastąpić w jednym wywołaniu wiele kliknięć myszki w graficznym interfejsie użytkownika. Co więcej, w przyszłości niektóre zadania będą możliwe do wykonania jedynie przy użyciu PowerShell. Prędzej, czy później zetknie się z nim więc każdy administrator. Dlatego warto przyswoić sobie jego podstawy.

PowerShell jest środowiskiem rozszerzalnym, pozwala na instalowanie w powłoce własnych komend (cmdlet) oraz dostawców danych (provider). To dodatkowo zwiększa jego siłę i w przyszłości powinno doprowadzić do powstawania różnych odmian powłoki przystosowanych do zarządzania różnymi komponentami czy konfiguracjami systemu. Pierwszą z takich rozszerzonych instalacji PowerShell jest Exchange Management Shell instalowana razem z serwerem Exchange 2007 - wprowadza ona do standardowej powłoki ponad 300 nowych komend służących do zarządzania organizacją opartą o serwery Exchange.

Z drugiej jednak strony bardzo duża liczba rozbudowanych komend i obiektów wymaga często zaawansowanej wiedzy programistycznej, aby utworzyć skrypt realizujący bardziej skomplikowane i wielokrokowe zadanie. Myślę jednak, że z czasem, gdy PowerShell będzie stawał się coraz bardziej popularny, powstawać też będzie coraz większa liczba ogólnodostępnych skryptów czy nawet całych zbiorów lub bilbliotek skryptów gotowych do natychmiastowego wykorzystania przez administratorów, którzy nie mają czasu wgłębiać się w tajniki powłoki.


Zasoby

Program instalacyjny PowerShell
.NET Framework 2.0
Oficjalna strona programu
PowerShell na stronach TechNet
Zbiór skryptów na stronach TechNet
Dokumentacja
Oficjalny blog PowerShell
Zbiór ciekawych odnośników i zasobów

Informacje dodatkowe

Jeśli podczas uruchamiania skryptu wyświetlony zostaje błąd:
"System.Management.Automation.PSSecurityException: The file D:\get-uptime.ps1 cannot be loaded. The execution of scripts is disabled on this system."
należy zmienić poziom zabezpieczeń uruchamiania skryptów w następujący sposób:
Set-ExecutionPolicy RemoteSigned
lub
Set-ExecutionPolicy Unrestricted
Więcej informacji na ten temat można znaleźć w pomocy PowerShell wywołując komendę:
get-help about_signing

Jeśli masz jakieś pytania lub komentarze dotyczące tego artykułu, napisz na naszym forum.

(c) CodeTwo. Wszelkie prawa zastrzeżone.



© Wszelkie prawa zastrzeżone. Żadna część ani całość tego artykułu nie może być powielana ani publikowana bez zgody autora.
wyświetleń: 11551