Spis treści:

Kategoria:PowerShell


PowerShell - lokalni użytkownicy i grupy

ADSI - zarządzanie lokalnymi użytkownikami i grupami - ikona grupy

Automatyzacja lokalnego komputera

Korzystając z prywatnego komputera w domu nie zajmujemy się najczęściej takimi drobiazgami jak konta lokalne. Jeżeli jednak na komputerze pracuje więcej niż jedna osoba, warto się nad tym zagadnieniem pochylić. Uruchamiając jakąś aplikację lub usługę również możemy zechcieć uruchomić ją w kontekście jakiegoś użytkownika z mniejszymi uprawnieniami. Można to robić sposobem powszechnym w graficznych interfejsach użytkownika - przy udziale myszki otworzyć właściwe okno, wybrać odpowiednie opcje, nacisnąć właściwe przyciski, znaleźć zakładkę, coś wpisać i zaakceptować. Jeżeli robimy to jednorazowo - nie ma problemu. Jeżeli czynność staje się powtarzalna, wtedy można skorzystać z alternatywnego sposobu. Sposobu mieszczącego się w ramach tematu artykułu - w PowerShellu.

Co to jest ADSI

Do zarządzania kontami użytkowników najwygodniej chyba użyć ADSI, Active Directory Service Interface (interfejs usług katalogowych). ADSI zostało zaprojektowane głównie w celu zarządzania zasobami sieciowymi w systemach rozproszonych, ale nic nie stoi na przeszkodzie, aby te same rozwiązania zastosować w systemie lokalnym. Interfejsy te pozwalają na wykonywanie popularnych zadań administracyjnych, między innymi dodawanie użytkowników i grup, ale także na zarządzanie sprzętem i przydziałem uprawnień do różnych zasobów.

Różni producenci mogą wpinać się w całą infrastrukturę ADSI udostępniając swoje usługi, a mechanizm ogólny Active Directory pozwala te usługi odnaleźć i pracować z nimi w podobny, standaryzowany sposób. Mam odczucie, że większość słysząc hasło Active Directory kojarzy to rozwiązanie z zastosowaniami ściśle serwerowymi. Gdyby mieli to pojęcie opisać, większość poruszałaby się w obszarze Windows Server i jakiegoś komponentu, który trzeba instalować. Samo zaś zarządzanie Active Directory opisywane byłoby jako coś porównywalnego z językiem programowania. Prawda jest taka, że nawet zwykły Windows, ten z rodziny NT, posiada swój własny model usług katalogowych, odpowiednik Active Directory. Oznacza to, że każdy kto posiada system operacyjny Windows XP, Windows Vista, Windows 7, Windows 8 lub Windows 10 może korzystać z pokazanych w dalszej części funkcji.

Przy okazji pozwoliłem sobie sprawdzić datę wydania Windows XP, jednego z najpopularniejszych przedstawicieli rodziny Windows NT - jest to 2001 rok. Już sama ta data powinna wystarczyć za komentarz. Pomijając datę, wróćmy do usług katalogowych. To, co dostępne jest w lokalnym systemie Windows, nie jest dokładnie tym, co dostępne jest w klasycznym Active Directory współpracującym z jego towarzyszem w zakresie protokołów - LDAP. Lokalny Windows nie jest przecież odpowiednikiem Windows Server. Nie zmienia to jednak faktu, że pozwala na bardzo dużo. A pozwala między innymi na wszystko to, co pokazane jest w kolejnych podpunktach. Wszytko dzięki komponentom realizującym model ADSI poprzez dostawcę WinNTPoszukując dalszych informacji na temat zaprezentowanego modelu nieserwerowych wersji Windows warto w wyszukiwarkę dopisywać hasło WinNT, w przeciwnym razie dostaniemy w wynikach zbiór informacji związanych z pełnym modelem Active Directory..

PowerShell i ADSI - tworzenie użytkowników

Pierwszą czynnością na drodze do opanowania sposobem zarządzania użytkownikami jest opanowanie czynności tworzenia użytkowników. Nie jest to może spektakularny skrypt, ale pozwala spokojnie wejść w model ADSI. Popatrzmy na poniższy listing:

$userName = "SampleUser1"
$userPassword = "#cj*9sm&4v"

Write-Host "Creating user '$userName' ..."
$adsi = [ADSI]"WinNT://$Env:ComputerName"
$user = $adsi.Create("User", $userName)
$user.SetPassword($userPassword)
$user.SetInfo()

W pokazywanych przykładach przyjąłem pewną konwencję, że na początku skryptu pojawiają się parametry. Takie podejście pozwala w przyszłości, po uznaniu, że skrypt jest przydatny, na zamknięcie go w funkcji (poleceniu) i wielokrotne wykorzystanie. W pokazanym przypadku parametrami będą login oraz hasło użytkownika. W kolejnych linijkach informujemy wykonawcę skryptu o wykonywanych czynnościach (polecenie Write-Host), tworzymy klasę ADSI i za jej pomocą wykonujemy całe zadanie.

Trzeba wiedzieć, że PowerShell, oprócz swojego zestawu poleceń pozwalającego na realizację wielu podstawowych zdań, pozwala na korzystanie z dowolnych obiektów platformy .NET, ale także z metod udostępnianych przez niezliczone interfejsy COM. ADSI to właśnie przykład takiego interfejsu COM. A w zasadzie zestaw powiązanych ze sobą interfejsów. Pełny opis tych interfejsów można znaleźć na stronach MSDN pod hasłem Active Directory Service Interface i nie zamierzam go tutaj powielać. Dla ciekawych podpowiem tylko, że w pokazanym skrypcie wykorzystuję metody interfejsów IADsContainer (Create - stwórz obiekt Active Directory), IADsUser (SetPassword - ustaw hasło użytkownika) oraz IADs (SetInfo - zapisz wprowadzone zmiany w magazynie danych).

Pokazany skrypt można oczywiście udoskonalać. Zamiast podawać login i hasło można skorzystać z gotowych rozwiązań i poprosić użytkownika o pierwsze zalogowanie się:

$userInfo = Get-Credential

Write-Host "Creating user '$($userInfo.UserName)' ..."
$adsi = [ADSI]"WinNT://$Env:ComputerName"
$user = $adsi.Create("User", $userInfo.UserName)
$user.SetPassword($userInfo.GetNetworkCredential().Password)
$user.SetInfo()

To nie wszystko co można zrobić. API jest bardzo pojemne i można dzięki niemu zrobić chyba wszystko co da się osiągnąć klikaniem w oknach konfiguracyjnych. Często zdarza się, że chcemy uruchomić pewną usługę na oddzielnym koncie systemowym. Hasło takiej usługi nie powinno wygasać i nie powinno być zmieniane. Któż miałby takie hasło zmieniać? Usługa sama sobie przecież zmieniać nie będzie. Można to zrobić w interfejsie użytkownika, ale można również w PowerShellu. Popatrzmy na jeszcze jeden skrypt z tej serii:

$userName = "SampleUser2"
$userPassword = "#cj*9sm&4v"

Write-Host "Creating user '$userName' ..."
$adsi = [ADSI]"WinNT://$Env:ComputerName"
$user = $adsi.Create("User", $userName)
$user.SetPassword($userPassword)
#Set flags: Password never expires (0x10000), User cannot change password (0x40)
$user.UserFlags = $user.UserFlags.Value -bor 0x10000 -bor 0x40
$user.SetInfo()

Wartości flag opisane są w dokumentacji, ale pozwoliłem sobie dodać odpowiedni komentarz.

Przydzielanie użytkowników do grup

O ile samo tworzenie użytkowników nie jest bardzo uciążliwe w graficznym środowisku, o tyle zarządzanie grupami, do których przynależy owy użytkownik, może się wiązać z klikaniem po kilku okienkach. Równie uciążliwe może się okazać weryfikowanie, czy użytkownik przydzielony jest do pożądanych grup. Jeżeli użytkownik jest jeden - nie ma problemu. Gdy jest ich więcej lub musimy zarządzać kilkoma serwerami - znów lepszym rozwiązaniem okazuje się PowerShell. Popatrzmy na przykładowy skrypt:

$userName = "SampleUser1"
$groupName = "Użytkownicy DCOM"
$user = [ADSI]"WinNT://$Env:ComputerName/$userName,User"
$group = [ADSI]"WinNT://$Env:ComputerName/$groupName,Group"
$group.Add($user.Path)

Znów głównym rozgrywającym jest interfejs ADSI. Wykorzystano tu interfejs IADsGroup oraz jego metodę Add. Skrypt może i jest prosty, ale nie jest praktyczny. Wykonanie go drugi raz spowoduje błąd - nie da się dodać użytkownika dwa razy. Błąd może pojawić się również wcześniej - nie mamy przecież pewności, że istnieje zarówno użytkownik jak i grupa. Popatrzmy na sposób weryfikacji istnienia użytkownika:

$userName = "SampleUser3"
$user = [ADSI]"WinNT://$Env:ComputerName/$userName,User"
if ($user.Path) {
    Write-Host "User '$userName' exists"
} else {
    Write-Host "User '$userName' does not exist"
}

Podobne warunki można zastosować w celu sprawdzenia istnienia grup.

Zarządzanie kontami i grupami lokalnego komputera

Moc PowerShell widoczna jest dopiero podczas masowego przetwarzania rekordów. Popatrzmy na kolejny skrypt, w którym pokażę jak dodać 10 użytkowników testowych:

$adsi = [ADSI]"WinNT://$Env:ComputerName" #IADsContainer
$password = "#cj*9sm&4v"
1..10 | % {"Test user $_"} | % {
    $user = [ADSI]"WinNT://$Env:ComputerName/$_,User"
    if ($user.Path) {
        Write-Host "User '$_' already exists..."
    } else {
        Write-Host "Creating user '$_'"
        $user = $adsi.Create("User", $_)
        $user.SetPassword($password)
        $user.SetInfo()
    }
}

Jeżeli żaden z użytkowników nie istniał, utworzonych zostanie 10 użytkowników, a na ekranie zostaną wyświetlone komunikaty następującej treści:

Creating user 'Test user 1'
Creating user 'Test user 2'
Creating user 'Test user 3'
Creating user 'Test user 4'
Creating user 'Test user 5'
Creating user 'Test user 6'
Creating user 'Test user 7'
Creating user 'Test user 8'
Creating user 'Test user 9'
Creating user 'Test user 10'

Jeżeli użytkownicy juz istnieją, również otrzymamy stosowny komunikat.

Skrypt można rozbudować. Jeżeli chcemy, aby każdy z tworzonych użytkowników należał do dwóch grup, możemy to zrobić następująco:

$adsi = [ADSI]"WinNT://$Env:ComputerName" #IADsContainer
$password = "#cj*9sm&4v"
$groups = "Użytkownicy monitora wydajności", "Użytkownicy DCOM"
1..5 | % {"Test user $_"} | % {
    $user = [ADSI]"WinNT://$Env:ComputerName/$_,User"
    if ($user.Path) {
        Write-Host "User '$_' already exists..."
    } else {
        Write-Host "Creating user '$_'"
        $user = $adsi.Create("User", $_)
        $user.SetPassword($password)
        $user.SetInfo()
    }

    $groups | % {
        $group = [ADSI]"WinNT://$Env:ComputerName/$_,Group"
        if ($group.IsMember($user.ADsPath)) {
            Write-Host "User '$($user.Name)' already assigned to group '$_'"
        } else {
            Write-Host "Adding user '$($user.Name)' to '$_' group ..."
            $group.Add($user.Path)
        }
    }
}

Jeżeli skrypt puścimy po założeniu 10 użytkowników skryptem poprzednim, otrzymamy taki oto komunikat:

User 'Test user 1' already exists...
Adding user 'Test user 1' to 'Użytkownicy monitora wydajności' group ...
Adding user 'Test user 1' to 'Użytkownicy DCOM' group ...
User 'Test user 2' already exists...
Adding user 'Test user 2' to 'Użytkownicy monitora wydajności' group ...
Adding user 'Test user 2' to 'Użytkownicy DCOM' group ...
User 'Test user 3' already exists...
Adding user 'Test user 3' to 'Użytkownicy monitora wydajności' group ...
Adding user 'Test user 3' to 'Użytkownicy DCOM' group ...
User 'Test user 4' already exists...
Adding user 'Test user 4' to 'Użytkownicy monitora wydajności' group ...
Adding user 'Test user 4' to 'Użytkownicy DCOM' group ...
User 'Test user 5' already exists...
Adding user 'Test user 5' to 'Użytkownicy monitora wydajności' group ...
Adding user 'Test user 5' to 'Użytkownicy DCOM' group ...

Jeżeli wszystko jest założone, skrypt nas o tym poinformuje:

User 'Test user 1' already exists...
User 'Test user 1' already assigned to group 'Użytkownicy monitora wydajności'
User 'Test user 1' already assigned to group 'Użytkownicy DCOM'
...

Usuwanie użytkowników to również może być proces masowy. Wyobraźmy sobie, że po przeprowadzeniu testów wydajnościowych, w których różne żądania kierowane są od różnych użytkowników, chcemy uporządkować system. Wypadałoby tych istniejących użytkowników usunąć! Można to zrobić na przykład tak:

1..10 | % {"Test user $_"} | % {
    $user = [ADSI]"WinNT://$Env:ComputerName/$_,User"
    if ($user.Path) {
        Write-Host "Removing user '$_'..."
        $user = $adsi.Delete("User", $_)
    } else {
        Write-Host "User '$_' does not exist..."
    }
}

Jeżeli uda się usunąć użytkownika, otrzymamy komunikat:

Removing user 'Test user 1'...
...

W przeciwnym razie komunikat będzie inny:

User 'Test user 1' does not exist...
...

Ci, którzy testowali wszystkie skrypty podczas czytania niniejszego artykułu mogą teraz, mam nadzieję, posprzątać po sobie i usunąć wszystkich użytkowników założonych po drodze. Jeżeli nie, oznaczałoby to, że artykuł nie jest dość jasny. w takim przypadku prosiłbym o ślad w komentarzu.

Podsumowanie

Celem artykułu było pokazanie, że nawet zwykły Windows pozwala na zautomatyzowanie wielu swoich operacji. Każda z tych operacji może być przydatna nie tylko w codziennej pracy administratora jakiejś lokalnej infrastruktury Windows, ale, po odpowiednich modyfikacjach, również podczas pracy z serwerową wersją Windows i protokołem LDAP. Wystarczy w takim przypadku pozmieniać przedrostki konstruktora ADSI WinNT:// na LDAP://. Obsługą naszych żądań zajmie się inny dostawca (ang. provider), ale zrobi to w ramach swojej infrastruktury.

Pokazane powyżej skrypty mogą się przydać nie tylko administratorowi, ale także programiście. Tworzenie konta użytkownika na którym działa IIS czy SSAS to nie są jakieś unikatowe rozwiązania lecz powszechnie przyjęta praktyka. Skrypty bardzo przydają się podczas wdrażania aplikacji. Taki skrypt jest znacznie pewniejszy niż instrukcja użytkownika. Wielokrotnie spotkałem się z przypadkiem, w którym nieprawidłowe działanie aplikacji spowodowane było pominięciem jednego kroku wskazanego w kilkustronicowej instrukcji instalacyjnej, na przykład pominięciem jednej z grup w procesie tworzenia użytkownika. Nie trzeba chyba wspominać o prostocie i powtarzalności. Skrypt zawsze zrobi to samo. Człowiek jest omylny. Gdy robi to samo kilka, kilkanaście czy kilkadziesiąt razy, wielce prawdopodobne jest, że się gdzieś pomyli. Gdy skrypt dojrzeje, podobnie jak zwykła metoda wielokrotnie użyta i przetestowana w wybranym języku programowania - można go używać prawie w ciemno.

Kategoria:PowerShell

, 2015-12-31

Komentarze:

kris (2022-10-07 12:18:48)
Super pomocne Dziękuję Pozdrawiam
Dodaj komentarz
Wyślij
Ostatnie komentarze
Dzieki za rozjasnienie zagadnienia upsert. wlasnie sie ucze programowania :).
Co się stanie gdy spróbuję wyszukać:
SELECT * FROM NV_Airport WHERE Code='SVO'
SELECT * FROM V_Airport WHERE Code=N'SVO'
(odwrotnie są te N-ki)
Będzie konwersja czy nie znajdzie żadnego rekordu?