Ebben a 2.21 -es fejezetben az előzőekben az ADSI interfészt használtuk az AD adatok kezelésére. Ezt viszonylag könnyű használni, sok minden a háttérben automatikusan lezajlik, mint például a kapcsolódás a tartományvezérlőhöz és az autentikáció. Néha jól jöhet, ha ezeket is kézben tudjuk tartani és mi magunk kezelhetjük. A másik eset, amikor „kinőjük” az ADSI-t, amikor nem egyszerű adattípust tároló attribútumokat szeretnénk kezelni. Nézzünk erre egy példát, a felhasználómnak van egy maxStorage nevű attribútuma:
$o = [ADSI] "LDAP://CN=Vegetári János,OU=Test,DC=ps,DC=local"
$o.Properties | ft -Property propertyname, value
Ennek eredménye:
PropertyName Value
------------ -----
objectClass {top, person, organizationalPerson, user}
cn Vegetári János
sn Vegetári
givenName János
distinguishedName CN=Vegetári János,OU=Test,DC=ps,DC=local
instanceType 4
whenCreated 2018. 10. 21. 20:52:56
whenChanged 2018. 10. 21. 21:12:56
displayName Vegetári János
uSNCreated System.__ComObject
uSNChanged System.__ComObject
nTSecurityDescriptor System.__ComObject
name Vegetári János
objectGUID {237, 100, 82, 153...}
userAccountControl 512
badPwdCount 0
codePage 0
countryCode 0
badPasswordTime System.__ComObject
lastLogoff System.__ComObject
lastLogon System.__ComObject
maxStorage System.__ComObject
pwdLastSet System.__ComObject
primaryGroupID 513
objectSid {1, 5, 0, 0...}
accountExpires System.__ComObject
logonCount 0
sAMAccountName vj
sAMAccountType 805306368
userPrincipalName vj@ps.local
objectCategory CN=Person,CN=Schema,CN=Configuration,DC=ps,DC=local
dSCorePropagationData 1601. 01. 01. 0:00:00
Látható, hogy jónéhány esetben csak annyit látunk, hogy System.__ComObject. És nem csak kiolvasni nem tudjuk, hanem beállítani sem ezeket az értékeket:
PS C:\Users\Administrator> $o.maxStorage = 1234
PS C:\Users\Administrator> $o.maxStorage
1234
PS C:\Users\Administrator> $o.put()
Exception calling "put" with "0" argument(s): "Number of parameters specified does not match the expected number."
At line:1 char:1
+ $o.put()
+ ~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : CatchFromBaseAdapterMethodInvokeTI
Ez a maxStorage attribútum Large Integer típusú, ahogy ezt a Schema Management eszközben is látjuk:
136 . ábra A maxStorage attribútum a Schema Manager-ben
Próbáltam felderíteni, hogy pontosan itt mit is kellene betölteni az attribútumba, olyasmit találtam, hogy magas bájt és alacsony bájt tömbjét, de nem nagyon jutottam dűlőre. Viszont az LDAP osztállyal viszonylag egyszerűen tudjuk ezt is kezelni.
Elsőként építsük fel a kapcsolatot:
try{
[void] [System.DirectoryServices.Protocols.LdapConnection]
}
catch{
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
}
$c = New-Object -TypeName System.DirectoryServices.Protocols.LdapConnection -ArgumentList "ps.local:389"
$c.SessionOptions.SecureSocketLayer = $false
$c.SessionOptions.ProtocolVersion = 3
$c.AuthType = [System.DirectoryServices.Protocols.AuthType]::Kerberos
try{
$c.Bind()
}
catch{
Write-Host "Kapcsolódási hiba!" -ForegroundColor Red
Write-Host $_
return
}
Az első sorokban megnézem, hogy be van-e töltve a System.DirectoryServices.Protocols.LDAPConnection típus. Ha nincs, akkor a catch ágban betöltöm. Ezután a $c változóba létrehozom az LDAP kapcsolati objektumot, ennek paramétereként a tartományom DNS nevét és a 389-es portot adom át. Ezután még finomhangolom a kapcsolatot, nem kérek titkosítást, 3-as verziójú LDAP-ot használok és Kerberosszal autentikálok. Ezután kapcsolódok a Bind() metódussal. Hiba esetén kiírom, hogy hiba van és a hiba szövegét.
Nézzük, hogyan lehet egy objektumot lekérdezni:
$sr = new-object -TypeName System.DirectoryServices.Protocols.SearchRequest
$sr.DistinguishedName = "DC=ps,DC=local"
$sr.Filter = "(&(objectclass=user)(samaccountname=vj))"
$re = $c.SendRequest($sr)
Az $sr változóba létrehozok egy System.DirectoryServices.Protocols.SearchRequest objektumot, ennek többfajta tulajdonságát ki lehet tölteni. Én most csak az egyszerűség kedvéért a keresés helyét és az LDAP lekérdezést adtam meg, majd a korábban létrehozott $c kapcsolati objektum SendRequest() metódusának adom át, ami ezt a lekérdezést átküldi a kapcsolaton keresztül a tartományvezérlőnek és a választ az $re változóba kapom meg:
PS C:\> $re
MatchedDN :
Controls : {}
ResultCode : Success
ErrorMessage :
Referral : {}
References : {}
Entries : {CN=Vegetári János,OU=Test,DC=ps,DC=local}
RequestId :
A ResultCode mutatja, hogy sikeres volt-e a lekérdezés, ha igen, akkor az Entries-be kapom vissza a tényleges választ. Ez mindenképpen gyűjtemény lesz, még akkor is, ha csak 1 találatot kaptam vissza:
PS C:\> $re.Entries
DistinguishedName Attributes
----------------- ----------
CN=Vegetári János,OU=Test,DC=ps,DC=local {givenname, codepage, objectcatego...
Ennek Attributes tulajdonságában vannak az AD objektum attribútumai:
PS C:\> $re.Entries[0].Attributes
Name Value
---- -----
givenname {74 195 161 110 111 115}
codepage {48}
objectcategory {67 78 61 80 101 114 115 111 110 44 67 78 61...
dscorepropagationdata {49 54 48 49 48 49 48 49 48 48 48 48 48 48 4...
usnchanged {49 54 52 50 51}
instancetype {52}
logoncount {48}
name {86 101 103 101 116 195 161 114 105 32 74 19...
badpasswordtime {48}
pwdlastset {49 51 49 56 52 54 50 56 55 55 54 48 56 55 5...
objectclass {116 111 112, 112 101 114 115 111 110, 111 1...
badpwdcount {48}
samaccounttype {56 48 53 51 48 54 51 54 56}
usncreated {49 54 52 49 49}
sn {86 101 103 101 116 195 161 114 105}
objectguid {237 100 82 153 105 171 32 73 143 139 82 82 ...
whencreated {50 48 49 56 49 48 50 49 50 48 53 50 53 54 4...
maxstorage {49 50 51 52 53 54 55 56}
useraccountcontrol {53 49 50}
cn {86 101 103 101 116 195 161 114 105 32 74 19...
countrycode {48}
primarygroupid {53 49 51}
whenchanged {50 48 49 56 49 48 50 49 50 49 49 50 53 54 4...
lastlogon {48}
distinguishedname {67 78 61 86 101 103 101 116 195 161 114 105...
samaccountname {118 106}
objectsid {1 5 0 0 0 0 0 5 21 0 0 0 237 21 168 170 134...
lastlogoff {48}
displayname {86 101 103 101 116 195 161 114 105 32 74 19...
accountexpires {57 50 50 51 51 55 50 48 51 54 56 53 52 55 5...
userprincipalname {118 106 64 112 115 46 108 111 99 97 108}
Elsőre nem tűnik túl szimpatikusnak, amit látunk. A legtöbb érték valójában egy szöveg karakterkódjainak tömbje, például a DistinguishedName értékét így lehet visszafejteni:
PS C:\> [char[]] $re.Entries[0].Attributes.distinguishedname[0] -join ""
CN=Vegetári János,OU=Test,DC=ps,DC=local
Itt megint kellett a [0] indexet használnom, hiszen itt is minden attribútum tömbként van kezelve, jobban mondva tömbök tömbjeként. A példánkban szereplő maxStorage attribútumot is ugyanígy tudjuk dekódolni, de ha számként kell kezeljük, akkor még egy típuskonverziót is hozzáadhatunk:
PS C:\> [int64] ([char[]] $re.Entries[0].Attributes.maxstorage[0] -join "")
12345678
Ha át akarjuk írni ennek az attribútumnak az értékét, akkor a következőket kell tenni:
$r = new-object -TypeName System.DirectoryServices.Protocols.ModifyRequest
$r.DistinguishedName = "CN=Vegetári János,OU=Test,DC=ps,DC=local"
$a = New-Object -TypeName System.DirectoryServices.Protocols.DirectoryAttributeModification
$a.Name = "MaxStorage"
$a.Operation = [System.DirectoryServices.Protocols.DirectoryAttributeOperation]::Replace
[void] $a.Add(11223344)
[void] $r.Modifications.Add($a)
$re = $c.SendRequest($r)
if ($re.ResultCode -ne [System.directoryServices.Protocols.ResultCode]::Success){
write-host "Hiba!" -ForegroundColor Red
write-host "Hibakód: $($re.ResultCode)"
write-host "Üzenet : $($re.ErrorMessage)"
}
else{
$re.ResultCode
}
Elsőként most egy System.DirectoryServices.Protocols.ModifyRequest objektumot hoztam létre az $r változóba. Ennek tulajdonságaként a módosítandó objektum DistinguishedName nevét adom meg. Majd egy újabb objektum, egy System.DirectoryServices.Protocols.DirectoryAttributeModification jön, amiben összerakom, hogy mit is akarok módosítani. Ennek Name tulajdonságába kerül az attribútum neve, az Operation tulajdonságba kerül a változtatás típusa, ami itt Replace, majd meghívom az Add() metódust, amivel az attribútum új értékét töltöm bele. Mindezt berakom a ModifyRequest objektum Modifications tulajdonságába, majd a kapcsolaton keresztül átküldöm a tartományvezérlőhöz. Ezután már csak a választ kell értékelni, ami jó esetben Success a válasz ResultCode tulajdonságában.
Látható, hogy a $r.Modifications-be akár több különböző változtatást is berakhatunk és mindezt egy menetben küldhetjük át feldolgozásra. Az attribútumokkal természetesen nem csak felülírást (Replace) végezhetünk, hanem törlést és hozzáadást is:
PS C:\> [System.DirectoryServices.Protocols.DirectoryAttributeOperation] | gm -
Static -MemberType Property
TypeName: System.DirectoryServices.Protocols.DirectoryAttributeOperation
Name MemberType Definition
---- ---------- ----------
Add Property static System.DirectoryServices.Protocols.DirectoryAttri...
Delete Property static System.DirectoryServices.Protocols.DirectoryAttri...
Replace Property static System.DirectoryServices.Protocols.DirectoryAttri...
Mindezt tehát egy kis ízelítőül szántam arra, hogy az ADSI-n kívül milyen egyéb módszer áll rendelkezésünkre AD objektumok lekérdezésére, módosítására.