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

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

Autor: Don Jones

Opublikowano: 30 listopada 2009

Zawartość strony
Przykładowe środowisko  Przykładowe środowisko
Tworzenie folderu  Tworzenie folderu
Przypisywanie uprawnień  Przypisywanie uprawnień
Windows PowerShell - Pytania i odpowiedzi  Windows PowerShell - Pytania i odpowiedzi

 

W tym odcinku naszego serialu o Windows PowerShell zaczniemy od miejsca, w którym przerwaliśmy miesiąc temu i będziemy kontynuować budowanie skryptu, ułatwiającego tworzenie i wyposażanie nowych kont użytkowników w środowisku Active Directory. Osoby, które nie czytały poprzednich części, powinny najpierw zapoznać się z nimi przed lekturą bieżącego odcinka.

Jak dotąd utworzyłem główną funkcję skryptu, zawierającą cztery podfunkcje. Funkcja główna wygląda następująco:

Function Provision {

  PROCESS {

    CreateUser $_

    CreateHomeFolder $_

    AddToGroups $_

    UpdateAttributes $_

  }

}

Ponadto utworzyłem już dwie wersje funkcji CreateUser (dla środowiska z Exchange Server 2007 i bez niego). W tym odcinku skoncentruję się na funkcji CreateHomeFolder. Posłuży ona do stworzenia folderów domowych nowych użytkowników i zastosowanie do tych folderów odpowiednich list kontroli dostępu (ACL).

Przykładowe środowisko

Podobnie jak wszystkie inne funkcje podrzędne wywoływane przez funkcję Provision, CreateHomeFolder przyjmuje obiekt hashtable jako wejście. Ponieważ do utworzenia folderu domowego potrzebna jest tylko znajomość nazwy logowania użytkownika, mogę wykorzystać klucz ['samAccountName']. Użyłem tego klucza również w artykule z zeszłego miesiąca, gdyż atrybut samAccountName jest wymagany przy tworzeniu nowego konta użytkownika w Active Directory.

Muszę również wiedzieć, gdzie folder domowy ma zostać utworzony, a także mieć dostęp sieciowy do tej lokalizacji. W tym przykładzie przyjąłem założenie, że wszystkie foldery domowe są przechowywane na określonym serwerze wybieranym na podstawie nazw logowania. Dla przykładu: wszystkie konta zaczynające się na litery od A do D mają swoje foldery na serwerze HomeFolders1, zaczynające się na litery E do H na serwerze HomeFolders2 i tak dalej. Tę przykładową strukturę można będzie łatwo rozszerzyć, aby działała w dowolnym środowisku. Na potrzeby tego wykładu nie zamierzam zajmować się tymi użytkownikami, których nazwy zaczynają się na I lub późniejsze litery alfabetu – schemat jest oczywisty, a zależy mi na zachowaniu prostoty przykładów i oszczędności miejsca.

W moim przykładowym środowisku każdy z wymienionych serwerów zawiera udział administracyjny o nazwie $Homes, zawierający wszystkie foldery domowe. Zakładam też, że administratorzy mają możliwość tworzenia nowych folderów w tym udziale, jak również, że mój skrypt będzie uruchamiany właśnie przez administratora. Na koniec zakładam, że wszystkie serwery wchodzące w zakres działania skryptu znajdują się albo w tej samej domenie, albo w domenach zaufanych, zatem będą ufać kontu administratora użytego do uruchomienia skryptu.

 Do początku strony Do początku strony

Tworzenie folderu

Utworzenie folderu jest stosunkowo proste. Najpierw muszę zdecydować, na którym serwerze mam utworzyć folder, tak jak poniżej:

Function CreateHomeFolder {

        Param($userinfo)

        $server1 = 'a','b','c','d'

        $server2 = 'e','f','g','h'

        Switch ($userinfo['samAccountName'].    substring(0,1).tolower()) {

        { $server1 –contains $_ }

        { $homeserver = '\\homeserver1\'; break; }

        { $server2 –contains $_ }

        { $homeserver = '\\homeserver2\'; break; }

        }

        }

Co naprawdę się tu dzieje?

  • Najpierw utworzyłem tablice początkowych liter, które ma obsługiwać każdy serwer folderów domowych. W tym przykładzie utworzyłem dwie tablice $server1 oraz $server2, przypisując im litery, które mają utrzymywać. Takie podejście pozwoli łatwo zmienić przydział liter w przyszłości, jak również rozszerzyć listę serwerów w razie potrzeby.
  • Następnie użyłem konstruktu Switch do sprawdzenia szeregu możliwych warunków. Wyliczam pierwszy znak atrybutu samAccountName konta użytkownika po przekonwertowaniu znaków na małe litery.
  • Dla każdego serwera domowego dodałem warunek do konstruktu Switch. Warunek ten sprawdza, czy pierwsza litera nazwy logowania użytkownika (czyli atrybutu samAccountName, zawartego w tym momencie w zmiennej specjalnej $_) istnieje w tablicy liter tego serwera. Jeśli tak, zmienna $homeserver jest wypełniana początkiem ścieżki Universal Naming Convention (UNC) tego serwera. Słowo kluczowe "break" powstrzymuje przed sprawdzaniem kolejnych warunków, gdy znaleziono już właściwy serwer. Zwróćmy uwagę, że ten przykład nie zadziała dla nazw użytkowników zaczynających się na litery od I do Z – konieczne jest dodanie warunków dla tego zakresu liter.

Teraz muszę po prostu polecić Windows PowerShell utworzyć folder. Funkcja będzie teraz wyglądała następująco:

Function CreateHomeFolder {

        Param($userinfo)

        $server1 = 'a','b','c','d'

        $server2 = 'e','f','g','h'

        Switch ($userinfo['samAccountName'].

        substring(0,1).tolower()) {

        { $server1 –contains $_ }

        { $homeserver = '\\homeserver1\'; break; }

        { $server2 –contains $_ }

        { $homeserver = '\\homeserver2\'; break; }

        }

        Mkdir ($homeserver + '\$Homes\' + $userinfo['samAccountName'])

        }

Polecenie MkDir – które w rzeczywistości jest wbudowaną funkcją wykorzystującą polecenie cmdlet New-Item – akceptuje ścieżki UNC. Tak więc wystarczy połączyć ustaloną wcześniej nazwę serwera z nazwą udziału administracyjnego $Homes oraz atrybutem samAccountName użytkownika. Jeśli nazwą logowania (samAccountName) jest DonJ, polecenie MkDir utworzy folder \\server1\$Homes\DonJ.

 Do początku strony Do początku strony

Przypisywanie uprawnień

Nowo utworzony folder będzie domyślnie dziedziczył uprawnienia swojego folderu nadrzędnego. Na potrzeby tego przykładu założyłem, że folder nadrzędny ma właściwe uprawnienia bazowe, które powinny zostać przypisane każdemu nowemu folderowi domowemu. Uprawnienia te powinny obejmować na przykład Full Control (Pełna kontrola) dla wbudowanego konta System, jak również dla grupy Domain Admins lub lokalnej grupy Administrators, ale uprawnienia te raczej nie powinny obejmować żadnych innych użytkowników. Zatem wszystko, co muszę jeszcze zrobić, to przypisanie odpowiedniemu kontu użytkownika uprawnień do właśnie stworzonego katalogu.

Proszę się nie złościć, ale muszę powiedzieć, że nie jest to zadanie, do którego wykonania Windows PowerShell jest dobrze przygotowany. Przykro mi. To dlatego, że uprawnienia systemu plików Windows są tak cholernie skomplikowane. Mamy tu listy kontroli dostępu (Access Control List – ACL), które zawierają wpisy kontroli dostępu (Access Control Entries – ACE). Każde ACE składa się z flagi allow/deny (zezwalaj/odmów), uprawnienia, takiego jak Read (Odczyt) lub Write (Zapis) oraz GUID podmiotu zabezpieczeń (użytkownika lub grupy). To naprawdę dużo informacji, które muszą działać razem.

Powłoka udostępnia polecenia cmdlet Get-ACL oraz Set-ACL, ale modyfikowanie ACL wymaga pobrania całego ACL, pomęczenia się z obiektami Microsoft .NET Framework w celu jego zmodyfikowania, po czym ponownego ustawienia zmienionego ACL dla pliku i folderu. Jest to trochę zbyt niskopoziomowe podejście jak na mój gust. Mógłbym też wykorzystać Windows Management Instrumentation (WMI), ale to wymagałoby działania praktycznie w taki sam sposób.

Szczęśliwie Microsoft obudował znaczną część tej złożoności w poręczne, łatwiejsze w użyciu narzędzie Cacls.exe, które – jak mogę się założyć – znane jest każdemu czytelnikowi, jako że jest dostępne „od zawsze”. Wykorzystanie go nie tylko pozwoli oszczędzić niepotrzebnych wysiłków, ale pozwoli również zademonstrować, jak Windows PowerShell może współpracować z zewnętrznymi narzędziami wiersza polecenia (Cacls, Ping, Ipconfig i tak dalej). Jeśli ktoś już wie, jak używać jednego z tych narzędzi, może nadal korzystać z nich w Windows PowerShell. Jedną z najważniejszych zalet Windows PowerShell jest to, że nie musimy od nowa uczyć się wszystkiego.

(Przy okazji – wiem, że Cacls.exe jest uważane za przestarzałe. Ma jednak zauważalnie prostszą składnię niż nowa wersja ICacls.exe, zatem wolałem użyć tego narzędzia. Czytelnik może oczywiście zmodyfikować skrypt, aby użyć ICacls.exe w tym miejscu).

Przeglądając pomoc dla narzędzia Cacls.exe, można ustalić, że potrzebne polecenia powinny wyglądać mniej więcej tak:

cacls \\server1\homes\DonJ /e /g DOMAIN\DonJ:R

        cacls \\server1\homes\DonJ /e /g DOMAIN\DonJ:W

Spowodują one przypisanie uprawnień Read i Write nowemu użytkownikowi bez zastępowania innych uprawnień, które już są przypisane do folderu.

Oczywistym problemem jest to, że chciałbym użyć zmiennych w tych poleceniach, a nie wartości statycznych. Przekazywanie zmiennych powłoki do zewnętrznych poleceń niekiedy może być zdradliwe. Istnieje wiele możliwych rozwiązań, ale moim ulubionym jest umieszczenie całego łańcucha polecenia w zmiennej powłoki i następnie wywołanie Cmd.exe do wykonania tego łańcucha. Będzie to wyglądać tak:

$cacls1 = "cacls "+$homeserver+"$Homes\"

        +$userinfo['samAccountName']+" /E /G "

        +$user+":W"

        $cacls2 = "cacls "+$homeserver+"$Homes\"

        +$userinfo['samAccountName']+" /E /G "

        +$user+":W"

        cmd /c $cacls1

        cmd /c $cacls2

Przyznaję, nie jest to najelegantsze rozwiązanie, ale działa. Przy okazji osiągnąłem cel uboczny – zademonstrować, jak można sparametryzować złożone zewnętrzne narzędzia wiersza polecenia przy użyciu zmiennych powłoki. Te cztery wiersze kodu umieszczę w mojej funkcji po poleceniu MkDir – teraz funkcja jest już gotowa.

Wszystko, co jeszcze pozostało do zrobienia, to utworzenie ostatnich dwóch podfunkcji dla głównej funkcji Provision: AddToGroups oraz UpdateAttributes. Pokażę, jak to zrobić, w kolejnym odcinku opowieści o Windows PowerShell.

 Do początku strony Do początku strony

Windows PowerShell - Pytania i odpowiedzi

Pytanie: Czy Windows PowerShell rozróżnia cudzysłowy pojedyncze od podwójnych przy obramowywaniu łańcuchów znaków?

Odpowiedź: Tak. Z jednym tylko, ale istotnym wyjątkiem, pojedyncze i podwójne cudzysłowy zachowują się identycznie. Oznacza to, ze obydwa poniższe polecenia spowodują przypisanie do zmiennej $var łańcucha "Microsoft":

$var = 'Microsoft'

          $var = "Microsoft"

Jednak w łańcuchach obramowanych podwójnym cudzysłowem powłoka sprawdza obecność znaku dolara ($). Jeśli znak taki wystąpi, powłoka przyjmuje, że znaki następujące bezpośrednio po nim są nazwą zmiennej i zastąpi tę zmienną jej wartością:

$var1 = 'Windows'

          $var2 = "Microsoft $var1"

Po wykonaniu tych dwóch poleceń zmienna $var2 będzie zawierała łańcuch "Microsoft Windows" (zmienna $var1 zostanie zastąpiona przez jej zawartość). W celu uniknięcia nieporozumień zalecane jest stosowanie wyłącznie pojedynczych cudzysłowów, o ile nie zachodzi potrzeba jawnego wykorzystania opisanej funkcjonalności podstawiania zmiennych.

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 PowerShell: Automatyzowanie tworzenia i wyposażania użytkowników, część 2     Windows Server 2008     Windows PowerShell: Automatyzowanie tworzenia i wyposażania użytkowników, część 4