Szerző: Soós Tibor

2010. június 2. 10:48

A Windows PowerShell rejtelmei - 5. rész

Egyelőre a PowerShell cikksorozat utolsó részéhez érkeztünk. Ebben a részben bepillantást adok a PowerShell két legfontosabb két bővítőmoduljának, az Active Directorynak és a Group Policynak a képességeibe.

Csoporttagságok rekurzív felderítése

Sok olyan feladat adódik a rendszergazdák munkája közben a felhasználókkal és a számítógép-környezetük beállításaival kapcsolatban, amelyre a grafikus rendszergazda eszközök nem adnak megoldást. Az egyik ilyen egyszerű feladat annak számbavétele, hogy egy felhasználó vajon mely csoportoknak a tagja? Nem csak egyszerűen a közvetlen tagság érdekel minket, hanem az áttételes is, azaz azok a csoportok is, amelyeknek azáltal tagja a felhasználó, hogy ezeknek valamely tagcsoportjának a tagja.

Ahhoz, hogy az Active Directory-t PowerShell cmdletekkel elérjük, importálni kell az ActiveDirectory modult. Ehhez telepíteni kell a Windows Server 2008 R2-es verzióhoz adott Remote Administration Tools csomag AD DS eszközei között található Active Directory module for Windows PowerShell képességet:

\"\"

Ha esetleg maga a tartományvezérlőnk nem lenne Windows Server 2008 R2 verziójú, akkor sincs gond, a Management Gateway komponenst kell letölteni és telepíteni. Ezután a PowerShell ablakban importáljuk az Active Directory modult:

[1] PS C:\\> Import-Module activedirectory

Ezzel számos új cmdlethez jutunk. De nézzük a megoldandó feladatunkat, a csoporttagság rekurzív felderítésére a következő függvényt állítottam össze:

function get-ADMemberOfRecursive (
	$adobject, 
	$eddig = (new-object Collections.ArrayList)) 
{
$ado = Get-ADObject $adobject -Properties memberof
if($ado.memberof){$ado.memberof | 
	foreach-object {$o = Get-ADObject $_
		if($eddig -notcontains $o.distinguishedname){
			[Void] $eddig.add(($o.distinguishedname))
			get-ADMemberOfRecursive $o $eddig
			$o
		}
	}
}}

A függvény igazából egy paramétert vár adobject néven, a másik csak a rekurzív meghívás miatti esetleges végtelen ciklusok elkerülését szolgálja. Hiszen lehet, hogy a felhasználó tagja A csoportnak, ami tagja B csoportnak, de B csoport meg tagja A csoportnak. A csoporttagság felderítése során tehát észre kell tudni venni, hogy az A csoportot már felderítettük, így azt még egyszer nem kell vizsgálni.

A függvény törzsében az AD objektumot újra lekérdezem a Get-ADObject cmdlettel, hiszen lehet, hogy a paraméterként átadott objektum nem tartalmazza a csoporttagság tulajdonságot, ennek a kifejezésnek a kimenete viszont igen. Ha ez az objektum tagja valamilyen csoportnak, akkor minden ilyen csoportra, ha még nem vizsgáltam, hozzáadom a DistinguishadName tulajdonságát az eddigi csoportok listájához és meghívom erre is a get-ADMemberOfGroup függvényt, de már az eddigi vizsgált csoportok listájával bővítve. Ezután a vizsgált csoport objektumát kiadom a kimenetre. Nézzük ennek futását:

[2] PS C:\\> get-ADMemberOfRecursive (Get-ADUser vj)

DistinguishedName   Name                ObjectClass         ObjectGUID
-   -                -         -
CN=temp,OU=Demó,... temp                group               7cdd95c2-3ee1-4
CN=Ön,OU=Demó,DC... Ön                  group               ef75fe02-8168-4

A VJ nevű felhasználóm tagja az Ön nevű csoportnak, ami pedig a temp csoportnak a tagja, az Ön csoportnak pedig tagja a temp. Ennek ellenére a függvényem nem futott végtelen ciklusba, hanem ezt a két csoportot egyszer kiadta a kimenetére. A következő futtatásban a tipikus csoporttagság példája látható. A soosbence felhasználó tagja a UserGroupnak, ami viszont tagja a ResourceGroupnak:

[3] PS C:\\> get-ADMemberOfRecursive (Get-ADUser soosbence)

DistinguishedName   Name                ObjectClass         ObjectGUID
-   -                -         -
CN=ResourceGroup... ResourceGroup       group               3542d4c7-f045-4
CN=UserGroup,OU=... UserGroup           group               52fe0789-5c76-4

 

Szervezeti diagram AD információk alapján

A következő, orgchart.ps1 szkriptfájl egy kiválasztott felhasználót a manager tulajdonsága, azaz az ő főnökét jelző AD információ alapján behelyezi a szervezeti hierarchiába:

param ($user)
$user = Get-ADUser -Filter {samaccountname -eq $user} -Properties manager

function get-topmanager ($u, $szint=0) {
	if($u.manager) 	{
		$főnök = Get-ADUser $u.manager -Properties manager
		if($u.distinguishedname -ne $főnök.distinguishedname){
			get-topmanager $főnök ($szint-1)}
	}
	$u | Add-Member -Member NoteProperty -Name szint -Val $szint -Pass 
        -force
}

function get-beosztrecursive ($u, $sz = 0){
Get-ADUser -Filter {manager -eq $u} -properties manager | 
	?{$_.distinguishedname -ne $u.distinguishedname} |
	%{if($_){
		$_ | Add-Member -Member NoteProperty -Name szint -Val $sz 
		-Pass -for get-beosztrecursive $_ ($sz+1)}}
}

$tops = @(Get-TopManager $user)
$offset = -$tops[0].szint
$tops | %{$_.szint += $offset}
$beosztottak = get-beosztrecursive $user ($offset+1) 
$tops, $beosztottak | %{$_} | ?{$_} | %{
	if($_.distinguishedname -eq $user.distinguishedname){$s = \"*\"}
	else{$s=\" \"} Write-Host ($s + (\"|`t\")*($_.szint) + \"+ \" + $_.name) 
}

A szkriptben két segédfüggvényt használok: az egyik a get-topmanager, ami a paramétereként megadott felhasználóból kiindulva végigszalad a ranglétrán, és megadja a feletteseket egészen addig, amíg már nincs további főnök, vagy esetleg valaki saját maga főnöke. Minden ilyen főnökhöz egy szint tulajdonságot is hozzárendelek, hogy tudjam, milyen mélységben kell majd ábrázolni a felhasználómat. A másik segédfüggvény rekurzívan kilistázza az adott felhasználó beosztottjait, azaz nem csak a közvetleneket, hanem a beosztottak beosztottjait is, és így tovább. Hogy szép formázott lehessen az eredmény, ehhez is hozzáveszem, hogy az adott beosztott hányadik szintű.

Maga a szkript ezek után nem bonyolult. Elsőként veszem a feletteseket a $tops változóba. Az első elem, azaz a top manager szintjének negáltja adja meg, hogy mennyivel eltolva kell majd az egész hierarchiát megjelenítenem, és gyorsan el is tolom a szinteket, hogy a top manager szintje legyen a kiinduló 0. Ezután veszem rekurzívan a beosztottakat, a szint itt eggyel nagyobbról indul, mint ahova a felettesek szintjeivel eljutottam. A végén az összes felettest és beosztottat kiíratom, az éppen vizsgált felhasználónál egy * karakterrel jelzem ezt, egyébként a szintnek megfelelő darabszámú függőleges vonal és tabulátor karaktert íratok ki, majd egy jelet, majd a felhasználó nevét. Nézzük a kimenetet a Nagy Főnök esetében:

[4] PS C:\\> C:\\munka\\orgchart.ps1 nf
*+ Nagy Főnök
 |      + Al1 Főnök
 |      |       + Kis1 Főnök
 |      |       + Kis2 Főnök
 |      |       |       + Gál András
 |      |       |       + Budai Edina
 |      |       |       + Dolák Fanni
 |      + Al2 Főnök
 |      + Al3 Főnök
 |      |       + Bornai Viktor
 |      |       + Balogh Mária
 |      |       + Budai András

Látható, hogy a teljes hierarchia látható. Nézzük csak azt az ábrát, amiben a Kis2 Főnök nevű felhasználóra vagyok kíváncsi:

[5] PS C:\\> C:\\munka\\orgchart.ps1 kis2f
 + Nagy Főnök
 |      + Al1 Főnök
*|      |       + Kis2 Főnök
 |      |       |       + Gál András
 |      |       |       + Budai Edina
 |      |       |       + Dolák Fanni

Látható, hogy itt már csak az ő al-ága látszik teljesen kibontva, a főnökei irányában csak egy út látszik.

Group Policy objektumok különbségeinek felderítése

A rendszergazdák másik gyakori fejfájása a Group Policy objektumok kezelése. Ha készítünk két GPO-t, akkor elég nehéz az ezekben található beállításokat egymás mellett áttekinteni. Mielőtt az ezen a területen segíteni képes PowerShell függvényt bemutatnám, nézzük először is, hogyan kezelhetjük PowerShell segítségével a GPO-kat. Elsőként importálni kell a GroupPolicy modult:

[6] PS C:\\> Import-Module grouppolicy

Ez a bővítmény – többek között – tartalmaz egy Get-GPOReport cmdletet, amellyel a GPO objektumok tartalmát tudjuk megnézni. Miután én nem csak nézegetni, hanem feldolgozni akarom ezeket az adatokat, ezért érdemes XML formátumú kimenetet kérni. Egy konkrét GPO beolvasása így történhet:

[7] PS C:\\> $fgpo = [xml] (Get-GPOReport -Name FőnökiGépek -ReportType XML)

Ezzel az előálló XML formátumú kimenetet tényleges XML adattípusként töltöm be a $fgpo változóba. Ez azért jó, mert ennek a változónak a tulajdonságain keresztül férek hozzá az adattartalomhoz:

[8] PS C:\\> $fgpo

xml                                     GPO
-                                     -
version=\"1.0\" encoding=\"utf-16\"         GPO

Ez még csak a \"fejléc\", de mehetünk mélyebbre a GPO tulajdonságon keresztül:

[9] PS C:\\> $fgpo.gpo


xsi                 : http://www.w3.org/2001/XMLSchema-instance
xsd                 : http://www.w3.org/2001/XMLSchema
xmlns               : http://www.microsoft.com/GroupPolicy/Settings
Identifier          : Identifier
Name                : FőnökiGépek
IncludeComments     : true
CreatedTime         : 2010-05-20T19:47:56
ModifiedTime        : 2010-05-20T19:56:59
ReadTime            : 2010-05-22T20:40:52.1631171Z
SecurityDescriptor  : SecurityDescriptor
FilterDataAvailable : true
Computer            : Computer
User                : User

Mehetünk tovább a Computer ágban:

[10] PS C:\\> $fgpo.gpo.Computer

VersionDirectory    VersionSysvol       Enabled             ExtensionData
-    -       -             -
14                  14                  true                {Security, Publ

És ennek ExtesionData ágán tovább:

[11] PS C:\\> $fgpo.gpo.Computer.ExtensionData

Extension                               Name
-                               -
Extension                               Security
Extension                               Public Key
Extension                               Registry

Látható, hogy több elkülönülő részben tárolódnak a GPO beállításai. A legtöbb beállítást általában a Registry ágban, azaz a grafikus felületen az Administrative Templates ágban találjuk. Menjünk ebbe az irányba tovább és tovább:

[12] PS C:\\> $fgpo.gpo.Computer.ExtensionData[2]

Extension                               Name
-                               -
Extension                               Registry


[13] PS C:\\> $fgpo.gpo.Computer.ExtensionData[2].extension

q3                  type                Policy              RegistrySetting
-                  -                -              -
http://www.micro... q3:RegistrySettings {Restricts the U... {q3:RegistrySet


[14] PS C:\\> $fgpo.gpo.Computer.ExtensionData[2].extension.policy


Name         : Restricts the UI language Windows uses for all logged users
State        : Enabled
Explain      : This is a setting for computers with more than one UI 
               language installed. If you enable this setting the UI 
               language of Windows menus and dialogs language for systems 
               with more than one language is restricted to the specific 
               language. If the specified language is not installed on the 
               target computer or the policy is disabled, the language 
               selection defaults to the language selected by the local 
               administrator.
Supported    : At least Windows Vista
Category     : Control Panel/Regional and Language Options
DropDownList : DropDownList

És itt jutottam el a tényleges beállításokhoz, ahol is a részletes leírástól kezdve minden hasznos információhoz hozzájuthatok. Ez olyan sok, hogy itt a cikkben jelentősen megvágtam. Ennek ismeretében nézzük, hogy hogyan lehet két GPO objektumot összehasonlítani a compare-gpos.ps1 szkriptem segítségével:

param ($gpo1, $gpo2, $hive = \"User\")
function get-registrypolicy ($gpo, $h)
{
    (([xml] (Get-GPOReport -name $gpo -ReportType XML -ErrorAction stop
		)).GPO.$h.ExtensionData | ?{$_.name -eq \"Registry\"}).exten
                 sion.policy
}

$pol1 = get-registrypolicy $gpo1 $hive
$pol2 = get-registrypolicy $gpo2 $hive
if($pol1 -and $pol2) {
    Compare-Object $pol1 $pol2 -IncludeEqual -Property name, state 
}
elseif($pol1){$pol1 | 
	select-object name, state, @{n=\"SideIndicator\";e={\"<=\"}}}
else{$pol2 | select-object name, state, @{n=\"SideIndicator\";e={\"=>\"}}}

A szkript két GPO nevet vár, illetve, hogy mely ágakat hasonlítsa össze (User vagy Computer).  A belsejében definiálok egy segédfüggvényt, amely a GPO azon részeit szedi csak ki, amely a registry, azaz Administrative Templates ágon belül van. Ennek segítségével beolvasom mindkét GPO beállításait, majd a korábbi cikkekben már látott Compare-Object cmdlettel összehasonlítom ezeket, de csak akkor, ha mindkét GPO tartalmaz ezen az ágon beállításokat. Ha nem, akkor én emulálok hasonló kimenetet, mint amit a Compare-Object adna, mert a \"gyári\" cmdlet üres objektum összehasonlítását nem tudja megtenni. Hogy még áttekinthetőbb legyen a kimenet, nézzük rögtön a grafikus rácsban a TitkárnőiGépek és a FőnökiGépek GPO-k összehasonlítását:

compare-gpos.ps1 TitkárnőiGépek FőnökiGépek Computer | Out-GridView

\"\"

A fenti rácsban láthatjuk, hogy az első két beállítás mindkét GPO-ban megtalálható, míg a 3-4. csak a FőnökiGépekben, és az 5-6. csak a TitkárnőiGépekben.

Végszó

Természetesen mindhárom kis példám továbbfejleszthető, a csoporttagság rekurzív kifejtésére alapozottan könnyen fel lehet deríteni, hogy vajon egy felhasználónknak a fájlkiszolgálón hol van írási joga. Természetesen nem csak a direkt jogmegadást keressük ilyenkor, hanem hogy valamely csoporttagsága által hol vannak jogai. A szervezeti diagram példát abba az irányba lehet továbbfejleszteni, hogy ne csak megjelenítésben lehessen felhasználni, hanem a kimenet maga felhasználói objektumok halmaza legyen. Így egyszerűen meg lehetne oldani például azt, hogy állítsuk át minden olyan felhasználó csoporttagságát, aki Kovács Béla által felügyelt szervezeti egységben dolgozik.

A Group Policy példát úgy lehetne fokozni, hogy egyrészt ne csak a registry-alapú beállítások között vizsgálódjon, hanem más részekben is. Továbbá hasznos lenne az is, ha egy \"etalon\" GPO-hoz képest valamely GPO-ban azzal ellentétes beállítások vannak, akkor azokat igazítsa az etalonhoz. Számtalan ilyen példát lehetne még hozni. Én párat összegyűjtöttem a http://www.iqjb.hu/PSkonzultacio.php oldalra, ha érdekli a tisztelt olvasót ilyen jellegű problémák megoldása PowerShell segítségével, keressen bizalommal!

Soós Tibor (PowerShell MVP, MCT), IQSOFT – John Bryce Oktatóközpont

Nagyon széles az a skála, amin az állásinterjú visszajelzések tartalmi minősége mozog: túl rövid, túl hosszú, semmitmondó, értelmetlen vagy semmi. A friss heti kraftie hírlevélben ezt jártuk körül. Ha tetszett a cikk, iratkozz fel, és minden héten elküldjük emailben a legfrissebbet!

a címlapról