Windows Server 2008     Windows PowerShell: Automatyzowanie tworzenia i wyposażania użytkowników, część 2

Windows PowerShell: Automatyzowanie tworzenia i wyposażania użytkowników, część 1 Udostępnij na: Facebook

Autor: Don Jones

Opublikowano: 16 listopada 2009

Zawartość strony
Ustalanie celów  Ustalanie celów
Budowanie wstępnej struktury  Budowanie wstępnej struktury
Uzyskiwanie wejścia  Uzyskiwanie wejścia
Rozszerzanie  Rozszerzanie
Ciąg dalszy nastąpi  Ciąg dalszy nastąpi
Windows PowerShell - Pytania i odpowiedzi  Windows PowerShell - Pytania i odpowiedzi
Dodatkowe materiały  Dodatkowe materiały

 

Artykułem tym rozpoczynam czteroczęściową serię na temat wykonania praktycznego, nadającego się do użycia w świecie rzeczywistym skryptu tworzenia i wyposażania (ang. provisioning) kont użytkowników w środowisku Windows PowerShell. W części pierwszej skoncentruję się na budowie struktury skryptu. Za miesiąc pokażę faktyczne tworzenie nowego konta użytkownika z włączoną obsługą poczty elektronicznej w Active Directory i Exchange Server 2007. W kolejnym miesiącu zajmę się tworzeniem katalogów domowych i zastosowaniem dla nich właściwych list kontroli dostępu (Access Control Lists – ACL). Wreszcie w części ostatniej pokażę, jak umieścić nowego użytkownika we właściwych grupach i wypełnić jego dodatkowe atrybuty w Active Directory.

Ustalanie celów

Najbardziej zdradliwą częścią procesu budowy kont użytkowników jest ustalenie, skąd mają pochodzić dane wejściowe. Skąd mamy wiedzieć, jakich użytkowników mamy utworzyć? I skąd mamy wziąć dodatkowe informacje, takie jak nazwiska, adresy e-mail, adresy poczty tradycyjnej, przynależność organizacyjna, numery telefonów itp. itd.?

W każdym środowisku odpowiedzi na te pytania będą inne, zatem postanowiłem utworzyć skrypt, który będzie w stanie zaakceptować dane wejściowe z najrozmaitszych źródeł. Spowoduje to wprawdzie, że będzie on bardziej złożony, ale wysiłek ten opłaci się na dłuższą metę, gdyż skrypt będzie znacznie bardziej elastyczny. Zależy mi również na tym, aby utworzony skrypt był w stanie równie łatwo obsłużyć tworzenie stu nowych kont jak jedno – ostatecznie mówimy o automatyzacji!

 Do początku strony Do początku strony

Budowanie wstępnej struktury

Przeprowadziłem pewną liczbę konsultacji w różnych przedsiębiorstwach, przy czym większość z nich dotyczyła skryptowania i automatyzacji. Nie jest zaskakujące, że spotkałem przy tym wiele skryptów i mogłem zauważyć, że typowym podejściem do zagadnienia, którym postanowiłem zająć się w tym artykule, jest stworzenie pojedynczego skryptu realizującego tworzenie i wyposażanie kont użytkowników.

Z upływem czasu administrator-autor skryptu modyfikuje go, aby obsłużył coraz więcej i więcej zadań. Nie jest to złe podejście, ale poświęcając nieco czasu na początku, aby utworzyć bardziej modularną strukturę, można utworzyć skrypt, który będzie rzeczywiście bardziej elastyczny i łatwiejszy do utrzymania. A co ważne, nie wymaga to wiele dodatkowej pracy.

Postanowiłem rozpocząć od centralnej funkcji, którą nazwałem Provision. W istocie nie będzie ona robiła zbyt wiele; na wejściu będzie akceptować obiekt typu hashtable (albo macierz asocjacyjną) zawierający informacje o nowym użytkowniku, a następnie będzie wywoływać serię podfunkcji, które wykonają właściwe zadania tworzenia i wyposażania użytkownika. Ponieważ środowiskiem jest Windows PowerShell, chcę, aby wszystko to działało w trybie potokowym. Tak więc zamierzam odczytywać jeden obiekt hashtable dla każdego nowego użytkownika i chcę, aby było możliwe zaakceptowanie całego potoku obiektów, aby móc utworzyć wielu użytkowników.

Szablon funkcji podstawowej wygląda następująco:

Function Provision {

  PROCESS {

  }

}

Niezbyt imponujące, prawda? Jednak przy korzystaniu z Windows PowerShell nie trzeba nic więcej. Blok skryptowy PROCESS zostanie wykonany raz dla każdego obiektu, który pojawi się w potoku wejściowym funkcji. Zaś wewnątrz tego bloku użyję zmiennej $_ w celu uzyskania dostępu do bieżącego obiektu z potoku.

 Do początku strony Do początku strony

Uzyskiwanie wejścia

Powodem, dla którego nie chcę, aby funkcja Provision samodzielnie pobierała jakiekolwiek dane, jest to, że dążę do większej elastyczności i nie chcę ponownie przeredagowywać jej za każdym razem, gdy będę potrzebował zaadaptować nowy format danych wejściowych. Zamiast tego stworzę dwie funkcje, które będą pobierać informacje o nowych użytkownikach – jedną czytającą plik CSV, zaś drugą odczytującą bazę danych. Na razie zamierzam pracować tylko z funkcją dotyczącą plików CSV, gdyż pliki takie można łatwo utworzyć w Notatniku czy innym edytorze tekstowym (ale również można je wyeksportować z Microsoft Excel i niemal każdej aplikacji bazodanowej).

Jednym z problemów, które mogą pojawić się przy korzystaniu z plików CSV – szczególnie wówczas, gdy przygotowane będą przez kogo mniej wprawnego technicznie – jest to, że nie możemy polegać na nagłówkach kolumn w pliku i spodziewać się, że będą one poprawnymi atrybutami Active Directory. Mówiąc inaczej, zamiast nazw kolumn, takich jak sn lub samAccountName, bardziej prawdopodobne jest to, że znajdziemy takie nazwy, jak Last Name i Logon Name. Nie stanowi to problemu – Windows PowerShell może wykonać translację. Rozpocząłem od założenia, że plik CSV zawiera następujące kolumny:

  • First Name
  • Last Name
  • City
  • Department
  • Job Title
  • Logon Name
  • Password

Można oczywiście rozszerzyć tę listę – za chwilę pokażę, jak to działa. Przy użyciu powyższej listy przykładowy plik CSV może wyglądać podobnie to poniższego:

First Name,Last Name,City,Department,Job  Title,Logon Name,Password

Don,Jones,Las Vegas,IT,Writer,donj,P@ssw0rd

Greg,Shields,Denver,IT,Administrator,gregs,  P@ssw0rd

Utworzę teraz funkcję o nazwie ProvisionInputCSV, akceptującą nazwę pliku jako parametru wejściowego i nakażę jej po prostu odczytanie pliku CSV:

Function ProvisionInputCSV {

  Param ([string]$filename)

  Import-CSV $filename

}

Mogę uruchomić samą tę funkcję, jak poniżej, po prostu aby się przekonać, że przeczyta ona plik CSV:

ProvisionInputCSV c:\files\myinput.csv

Teraz chciałbym, aby funkcja przetworzyła każdy wiersz pliku CSV na obiekt typu hashtable. Ponadto zamiast zachowywania nazw kolumn z pliku CSV chcę przetłumaczyć te nagłówki na nazwy atrybutów Active Directory. Istnieje wiele sposobów osiągnięcia tego celu w Windows PowerShell, ale zależy mi na utrzymaniu prostoty – użyłem zatem pętli foreach do przetworzenia każdego wiersza pliku CSV, po jednym na raz. Dla każdego wiersza utworzymy obiekt hashtable i zapiszemy go do potoku wyjściowego.

Spójrzmy na funkcję pokazaną na Rysunku 1. Na co warto zwrócić uwagę:

  • Wewnątrz bloku foreach zmienna $user zawiera dane pojedynczego klienta. Konstrukt foreach przechodzi przez każdy wiersz w zmiennej $users i automatycznie wypełnia zmienną $user kolejnym wierszem.
  • Ponieważ niektóre nazwy kolumn zawierają spacje, konieczne było ujęcie ich w znaki cudzysłowu.
  • Dodałem nowy atrybut, displayName, skonstruowany z dwóch kolumn pliku CSV: First Name oraz Last Name.
  • Polecenie Write-Output służy do zapisania każdego obiektu hashtable do potoku wyjściowego.

Rysunek 1: Funkcja wykorzystująca pętlę foreach.

Function ProvisionInputCSV {

  Param ([string]$filename)

  $users = Import-CSV $filename

  foreach ($user in $users) {

    $ht = @{'givenName'=$user."First Name";

            'sn'= $user."Last Name";

            'title'= $user."Job Title";

            'department'= $user.Department;

            'displayName'= $user."First Name" + " " + $user."Last Name";

            'city'= $user.City;

            'password'= $user.Password;

            'samAccountName'= $user."Logon Name"

           }

    Write-Output $ht

  }

}

W praktyce następstwem tych wszystkich działań jest, że mogę użyć jednej funkcji do odczytania pliku CSV i przekształcenia zawartych w nim danych na obiekty hashtable, które następnie można przesłać do funkcji wyposażającej:

ProvisionInputCSV c:\data\myinput.csv | Provision

Ponieważ funkcja Provision akceptuje ustandaryzowany obiekt hashtable, mogę utworzyć różne funkcje generujące poprawne wejście – ProvisionInputDatabase (czytającą z bazy danych), ProvisionInputSpreadsheet (czytającą z arkusza kalkulacyjnego) i tak dalej.

Tak długo, jak długo wynikiem działania tych funkcji będzie standaryzowany obiekt hashtable wypełniony danymi użytkownika, główna funkcja Provision będzie działać poprawnie. Podejście takie gwarantuje, że kiedyś, w przyszłości, będę mógł użyć całkowicie nowych źródeł dla nowych danych – bez konieczności modyfikowania zasadniczej funkcji Provision.

 Do początku strony Do początku strony

Rozszerzanie

Można łatwo dodać więcej kolumn do plików CSV, aby przekazać takie informacje jak numery telefonów lub dowolne inne informacje. Trzeba jedynie zapewnić, że funkcja budująca obiekt hashtable również zostanie rozszerzona, aby zawrzeć te informacje. Dla przykładu załóżmy, że do pliku CSV dodaliśmy kolumny Description oraz Office. Rozszerzenie obiektu hashtable wymaga dodania dwóch wierszy, jak pokazano na Rysunku 2. Mówiąc inaczej, można wykorzystać podstawową strukturę przedstawioną w tym artykule, aby dostosować ją do dowolnych wymagań, jakie konkretne środowisko może stawiać atrybutom katalogowym dla nowych kont użytkowników.

Rysunek 2: Poprawiona funkcja.

Function ProvisionInputCSV {

  Param ([string]$filename)

  $users = Import-CSV $filename

  foreach ($user in $users) {

    $ht = @{'givenName'=$user."First Name";

            'sn'= $user."Last Name";

            'title'= $user."Job Title";

            'department'= $user.Department;

            'displayName'= $user."First Name" + " " + $user."Last Name";

            'city'= $user.City;

            'password'= $user.Password;

            'samAccountName'= $user."Logon Name";

            'office' = $user.office;

            'description' = $user.description

           }

    Write-Output $ht

  }

}

 Do początku strony Do początku strony

Ciąg dalszy nastąpi

W następnym miesiącu zajmę się wypełnianiem głównej funkcji Provision, budując podfunkcję, która będzie tworzyć nowe konta użytkowników. Przygotuję dwie wersje tej podfunkcji – jedną dla tych, którzy używają Exchange Server 2007 (umożliwiający obsługę z poziomu Windows PowerShell), oraz drugą dla tych, którzy go nie mają. W tym momencie skrypt już będzie użyteczny: wprawdzie umożliwi jedynie samo tworzenie kont użytkowników, jednak w istocie jest to najbardziej czasochłonna część procesu, zatem już samo to pozwoli na natychmiastowe oszczędności czasu.

 Do początku strony Do początku strony

Windows PowerShell - Pytania i odpowiedzi

Pytanie: Wyszukałem klasę Win32_OperatingSystem z Windows Management Instrumentation (WMI) w witrynie MSDN i znalazłem tam informację, że klasa zawiera informację o wersji pakietu serwisowego (Service Pack – SP). Jednak gdy uruchomiłem polecenie Get-WmiObject Win32_OperatingSystem w Windows PowerShell, nie została wyświetlona żadna informacja o wersji SP. Dlaczego?

Odpowiedź: Podsystem formatujący Windows PowerShell wybiera do wyświetlenia tylko domyślny podzbiór właściwości dla tej klasy. Jedną z metod wymuszenia wyświetlenia wszystkich właściwości jest użycie formatującego polecenia cmdlet, aby zastąpić te ustawienia domyślne:

Get-WmiObject Win32_OperatingSystem | Format-List *

Alternatywnie można po prostu wskazać żądaną właściwość:

Get-WmiObject Win32_OperatingSystem | Format-List BuildNumber,CSName,ServicePackMajorVersion

 Do początku strony Do początku strony

Dodatkowe materiały

O autorze

Don Jones jest współzałożycielem firmy (i witryny) Concentrated Technology, zawierającej blogi na temat Windows PowerShell, SQL Server, App-V i inne.

 Do początku strony Do początku strony

Windows Server 2008     Windows PowerShell: Automatyzowanie tworzenia i wyposażania użytkowników, część 2