Helyi fiókok kezelése ADSI-val

Nem csak az AD objektumokat, hanem helyi felhasználókat és csoportokat is tudjuk kezelni az ADSI felületen, a WinNT szolgáltatón keresztül. Erre már láttunk példát a 2.21.4 AD objektumok tulajdonságainak kiolvasása, módosítása részben. Ezen keresztül nem csak a felhasználókat, hanem nagyon sok más információt kiolvashatunk az adott géppel kapcsolatban, így külön rá kell szűrni, ha csak bizonyos típusba tartozókat akarjuk megjeleníteni. Nézzük, hogy általában hogyan lehet felsorolni a helyi fiókokat:

[8] PS C:\> ([ADSI]"WinNT://soost-pc,computer").children | Select-Object -Prope

rty @{n="Name";e={$_.name.tostring()}}, schemaclassname

 

Name                                    SchemaClassName

----                                    ---------------

Administrator                           User

Guest                                   User

HomeGroupUser$                          User

soost                                   User

UpdatusUser                             User

Administrators                          Group

Backup Operators                        Group

Replicator                              Group

Users                                   Group

Dummy                                   Group

HomeUsers                               Group

Canon Inkjet PIXMA iP4000               PrintQueue

AdobeARMservice                         Service

AeLookupSvc                             Service

ALG                                     Service

Láthatjuk, hogy van itt még PrintQueue, Service is a listában. Erre alapozottan a következő függvény megtalálja nekünk a helyi rendszergazda fiókot:

function get-localadmin ($server = $env:COMPUTERNAME)

{

    $pl = [ADSI]"WinNT://$server,computer"

    $pl.children | where-object {$_.schemaclassname -eq "user"} |

    %{

        $username = $_.name.tostring()

        $User = New-Object System.Security.Principal.NTAccount($username)

        $SID = $User.Translate([System.Security.Principal.SecurityIdentifier])

          New-Object -typename PSObject -property @{

                NameOfLocalAdmin = $username

                SID = $SID.value

          }

    } | ?{$_.sid -match "-500$"}

}

Ez ugye azért érdekes, mert a rendszergazdát át is lehet nevezni, meg nem angol Windowsban nem Administrator a neve, hanem a helyi nyelvnek megfelelő rendszergazda. Így igazán támpontot az 500-ra végződő SID jelenti számunkra. Ennek a függvénynek a kimenete:

[12] PS C:\> get-localadmin

 

NameOfLocalAdmin                        SID

----------------                        ---

Administrator                           S-1-5-21-1879730660-3170798090-2708...

Nézzük, hogyan lehetne kideríteni egy helyi csoport tagjait! Először próbáljuk megragadni az Administrators csoportot az eddig látottak alapján:

PS C:\> $computername = $env:COMPUTERNAME

PS C:\> $group = "Administrators"

PS C:\> $LocalGroup = [ADSI]("WinNT://$computername/$group,group")

PS C:\> $LocalGroup | fl *

 

 

groupType          : {4}

Name               : {Administrators}

Description        : {Administrators have complete and unrestricted access to

                     the computer/domain}

objectSid          : {1 2 0 0 0 0 0 5 32 0 0 0 32 2 0 0}

AuthenticationType : Secure

Children           : {}

Guid               : {D9C1AAD0-1E71-11CF-B1F3-02608C9E7553}

ObjectSecurity     :

NativeGuid         : {D9C1AAD0-1E71-11CF-B1F3-02608C9E7553}

NativeObject       : System.__ComObject

Parent             : WinNT://WORKGROUP/STLENO

Password           :

Path               : WinNT://STLENO/Administrators,group

Properties         : {groupType, Name, Description, objectSid}

SchemaClassName    : Group

SchemaEntry        : System.DirectoryServices.DirectoryEntry

UsePropertyCache   : True

Username           :

Options            :

Site               :

Container          :

Látható, hogy a helyi csoport sok tulajdonsága között nincsenek a csoport tagjai, azokat külön le kell kérni. Nézzük, vajon hogyan lehet ezt megtenni:

PS C:\> $LocalGroup | gm

 

 

   TypeName: System.DirectoryServices.DirectoryEntry

 

Name                        MemberType Definition

----                        ---------- ----------

ConvertDNWithBinaryToString CodeMethod static string ConvertDNWithBinaryToS...

ConvertLargeIntegerToInt64  CodeMethod static long ConvertLargeIntegerToInt...

Description                 Property   System.DirectoryServices.PropertyVal...

groupType                   Property   System.DirectoryServices.PropertyVal...

Name                        Property   System.DirectoryServices.PropertyVal...

objectSid                   Property   System.DirectoryServices.PropertyVal...

Sajnos nem sok erre alkalmas metódust láthatunk. De ne felejtkezzünk meg arról, hogy a PowerShell képes elrejteni az igazságot! Nézzük, hogy mik az „igazi” tagjellemzői ennek a helyi csoportobjektumnak a Base nézeten keresztül:

PS C:\> $LocalGroup | gm -View Base

 

 

   TypeName: System.DirectoryServices.DirectoryEntry

 

Name                      MemberType Definition

----                      ---------- ----------

Disposed                  Event      System.EventHandler Disposed(System.Ob...

Close                     Method     void Close()

CommitChanges             Method     void CommitChanges()

CopyTo                    Method     adsi CopyTo(adsi newParent), adsi Copy...

CreateObjRef              Method     System.Runtime.Remoting.ObjRef CreateO...

DeleteTree                Method     void DeleteTree()

Dispose                   Method     void Dispose(), void IDisposable.Dispo...

Equals                    Method     bool Equals(System.Object obj)

GetHashCode               Method     int GetHashCode()

GetLifetimeService        Method     System.Object GetLifetimeService()

GetType                   Method     type GetType()

InitializeLifetimeService Method     System.Object InitializeLifetimeService()

Invoke                    Method     System.Object Invoke(string methodName...

InvokeGet                 Method     System.Object InvokeGet(string propert...

InvokeSet                 Method     void InvokeSet(string propertyName, Pa...

MoveTo                    Method     void MoveTo(adsi newParent), void Move...

RefreshCache              Method     void RefreshCache(), void RefreshCache...

Rename                    Method     void Rename(string newName)

ToString                  Method     string ToString()

AuthenticationType        Property   System.DirectoryServices.Authenticatio...

Children                  Property   System.DirectoryServices.DirectoryEntr...

Container                 Property   System.ComponentModel.IContainer Conta...

Guid                      Property   guid Guid {get;}

Name                      Property   string Name {get;}

NativeGuid                Property   string NativeGuid {get;}

NativeObject              Property   System.Object NativeObject {get;}

ObjectSecurity            Property   System.DirectoryServices.ActiveDirecto...

Options                   Property   System.DirectoryServices.DirectoryEntr...

Parent                    Property   adsi Parent {get;}

Password                  Property   string Password {set;}

Path                      Property   string Path {get;set;}

Properties                Property   System.DirectoryServices.PropertyColle...

SchemaClassName           Property   string SchemaClassName {get;}

SchemaEntry               Property   adsi SchemaEntry {get;}

Site                      Property   System.ComponentModel.ISite Site {get;...

UsePropertyCache          Property   bool UsePropertyCache {get;set;}

Username                  Property   string Username {get;set;}

Itt már látuk egy szép nagy listát. Ugyan nincs közöttük semmilyen „member”-re utaló metódus, de van egy általános Invoke és egy InvokeGet is. Nekünk az előbbi kell:

PS C:\> $GMembers = $LocalGroup.Invoke("Members")

PS C:\> $GMembers

System.__ComObject

System.__ComObject

Valamit kaptunk, de nem sok minden derül ki az eredményből. Próbélkozzunk a jól bevált fl *-al:

PS C:\> $GMembers | fl *

System.__ComObject

System.__ComObject

Sajnos most ez sem vált be. Ha elég sokat túrunk az interneten, akkor fellelhetjük a megoldást: végig kell menni a tagokat reprezentáló COM objektumokon, át kell őket alakítani ADSI objektumokká, majd immárnon az InvokeGet metódust kell meghívni, átadván azt a paraméternevet, amit a csoporttagról meg akarunk tudni:

PS C:\> foreach($gmember in $GMembers){([ADSI] $gmember).InvokeGet("Name")}

Administrator

Tibi

Sajnos a tagok neve nem tartalmazza, hogy például ez a Tibi itt most honnan származik? Helyi Tibi vagy valamelyik tartomány Tibije? Ami még kellemetlenebb, hogy nem tudok olyan tulajdonségról, amit az InvokeGet-tel lekérdezve megkapnánk a tag teljes nevét. A megoldás az, hogy lekérdezzük a tag SID-jét és ez alapján visszafordítjuk a teljes nevet, ami már a taromány nevét is tartalmazza. A teljes megoldást a következő szkript tartalmazza:

$computername = $env:COMPUTERNAME

$group = "Administrators"

$LocalGroup = [ADSI]("WinNT://$computername/$group,group")

 

$GMembers = $LocalGroup.Invoke("Members")

 

$GMemberProps = @{Server="$computername"; LocalGroup=$group;SamAccountName="";Name="";ObjectClass="";ADSPath="";Domain="";SID=""}

 

foreach ($gmember in $gmembers){

    $memberobject = new-object psobject -Property $GMemberProps

    $name = ([ADSI] $gmember).InvokeGet("Name")

    $sid = ([ADSI] $gmember).InvokeGet("objectsid")

    $UserSid = New-Object System.Security.Principal.SecurityIdentifier -ArgumentList $sid, 0

 

    try{

        $strSID = $UserSid.Translate([System.Security.Principal.NTAccount])

        $samaccountname = $strSID.Value

    }

    catch{

        $samaccountname = "!!!Unresolvable!!!"

    }

 

    $class = ([ADSI] $gmember).InvokeGet("Class")

    $ads = ([ADSI] $gmember).InvokeGet("adspath")

    $memberobject.name= "$name"

    $memberobject.ObjectClass= "$class"

    $memberobject.adspath="$ads"

    $memberobject.sid=$usersid.value

    $memberobject.SamAccountName = $samaccountname

 

    $memberobject.domain = $samaccountname.Split("\")[0]

 

    $memberobject

}

 Gondolhatja a tisztelt olvasó, hogy ha a csoporttagság lekérdezése ilyen bonyolult, akkor milyen lehet a egy tag hozzáadása a csoporthoz? Igen, az egy picit még bonyolultabb, főleg mert a speciális fiókok, mint például a NT AUTHORITY\Authenticated Users külön elbírálás alá esik. Ezért inkább nem is mennék el ebbe az irányba, hanem a következő fejezetben nézzük mit hozott nekünk a PowerShell 5.1!



Word To HTML Converter