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áron 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 tartomá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!