This exemplar piece comes from some work I did a while back looking into different strategies I could use at a clients for a logon script engine that would require minimum (none was the preference if I could manage it) maintenance and upkeep as requirements to map drives and printers changed. Previously I have worked with a number of solutions using 'if member' variations, configuration files etc. but this client had a couple of great advantage - a mostly green field domain and a desire to have and utilise naming standards.
One of my personal dislikes is code where changing the behaviour of the code requires changing the code itself! Just stop and think about that for a minute; lots of reasons exist for that to be perfectly valid but not in the vast majority of cases. Configuration files, the registry etc. all fulfil a very useful role; they allow you to change the colour of the desktop, screen dimensions etc. all without a change to a single line of code. Hard coded behaviour is just storing up problems for the future.
I explore the options, problems and a possible solution for the problems I see with many logon scripts over at tokenGroupsLogon and tokenGroups if you are interested in more in this area. If you want to skip ahead to either some sample output or script details please follow the appropriate link.
1Option Explicit
2
3Const ADS_GROUP_TYPE_SYSTEM = &h1
4Const ADS_GROUP_TYPE_GLOBAL_GROUP = &h2
5Const ADS_GROUP_TYPE_LOCAL_GROUP = &h4
6Const ADS_GROUP_TYPE_UNIVERSAL_GROUP = &h8
7Const ADS_GROUP_TYPE_SECURITY_ENABLED = &h80000000
8
9Dim oADSystemInfo, oUser, aGroups, i, sOutput
10
11Set oADSystemInfo = CreateObject("ADSystemInfo")
12Set oUser = GetObject("LDAP://"
& oADSystemInfo.UserName)
13
14oUser.GetInfoEx Array("tokenGroups") ,
0
15aGroups = oUser.Get("tokenGroups")
16
17If TypeName(aGroups) = "Byte()"
Then
18'single SID
19sOutput = FindGroup(aGroups) & VbCrLf
20Elseif UBound(aGroups) > -1
Then
21'two or more SID's
22For i = 0 To UBound(aGroups)
23sOutput = sOutput & FindGroup(aGroups(i)) & VbCrLf
24Next
25Else
26'no SID's
27End If
28
29WScript.Echo sOutput
30
31Function FindGroup(aBytes)
32Dim oGroup, sHex, iGroupType
33
34sHex = BytesToHexString(aBytes)
35
36On Error Resume Next
37
38Set oGroup = GetObject("LDAP://<SID="
& sHex & ">")
39
40If Err.Number <> 0 Then
41'Error
42FindGroup = Left(sHex & Space(56),56)
& vbTab & vbTab & "Error Binding (SIDHistory?)"
43Else
44'OK
45FindGroup = Left(sHex & Space(56),56)
& vbTab
46Select Case oGroup.Get("groupType") And
15
47Case ADS_GROUP_TYPE_GLOBAL_GROUP
48FindGroup = FindGroup & "G"
49Case ADS_GROUP_TYPE_GLOBAL_GROUP + ADS_GROUP_TYPE_SYSTEM
50FindGroup = FindGroup & "G/S"
51Case ADS_GROUP_TYPE_LOCAL_GROUP
52FindGroup = FindGroup & "L"
53Case ADS_GROUP_TYPE_LOCAL_GROUP + ADS_GROUP_TYPE_SYSTEM
54FindGroup = FindGroup & "L/S"
55Case ADS_GROUP_TYPE_UNIVERSAL_GROUP
56FindGroup = FindGroup & "U"
57Case ADS_GROUP_TYPE_UNIVERSAL_GROUP + ADS_GROUP_TYPE_SYSTEM
58FindGroup = FindGroup & "U/S"
59Case Else
60FindGroup = FindGroup & "!"
61End Select
62FindGroup = FindGroup & vbTab & oGroup.samAccountName
63End If
64
65Err.Clear
66On Error GoTo 0
67End Function
68
69Function BytesToHexString(aBytes)
70Dim i
71
72BytesToHexString = ""
73
74For i = 1 To LenB(aBytes)
75BytesToHexString = BytesToHexString & Right("0"
& Hex(AscB(MidB(aBytes, i, 1))), 2)
76Next
77End Function
010500000000000515000000A51E5703625B2817CC01B84969B90000
Error Binding (SIDHistory?)
010500000000000515000000A51E5703625B2817CC01B84950060000
Error Binding (SIDHistory?)
010500000000000515000000A51E5703625B2817CC01B8492AAF0000
Error Binding (SIDHistory?)
01020000000000052000000021020000
L/S Users
01050000000000051500000031957A8D5D46F81B91F7240DF3290100 L
Fusion_Public_Readers
01050000000000051500000031957A8D5D46F81B91F7240D9E390000 U
DL-BSkyB
01050000000000051500000031957A8D5D46F81B91F7240DA12B0000 U
DL-GIS-ServerTeam
01050000000000051500000031957A8D5D46F81B91F7240D9D680100 G
ApwdAL3
01050000000000051500000031957A8D5D46F81B91F7240D57210100 G
F2-Roaming(R)
01050000000000051500000031957A8D5D46F81B91F7240D01020000 G
Domain Users
01050000000000051500000031957A8D5D46F81B91F7240D79C80000 G
esAllAs
01050000000000051500000031957A8D5D46F81B91F7240D4B9D0000 G
esTeaGIS.EuropeAsiaAs
01050000000000051500000031957A8D5D46F81B91F7240D543B0000 G
Associates
01050000000000051500000031957A8D5D46F81B91F7240D2F9B0000 G
esComPSEAs
01050000000000051500000031957A8D5D46F81B91F7240DA4680100 G
BpwdAL1
01050000000000051500000031957A8D5D46F81B91F7240DF8FC0000 G
esDepQ9D000000MAs
01050000000000051500000031957A8D5D46F81B91F7240D839B0000 G
esCouGBRAs
01050000000000051500000031957A8D5D46F81B91F7240DDA9B0000 G
esHorGlobalInfrastructureServicesAs
01050000000000051500000031957A8D5D46F81B91F7240D83160100 G
esTeaIS.EuropeAsiaAs
01050000000000051500000031957A8D5D46F81B91F7240D7A160100 G
esHorInfrastructureSolutionsAsThe first column is the hex string representation of the SID's returned by the query for tokenGroups.
The length of a SID depends on the kind of SID it is with as you can see in the fourth row a well known SID for the built-in system generated Users group which Domain Users is nested into.
Also worth noticing that the first portion of the top three SID's is different
from the remainder, excepting the fourth one, and can not be resolved by the
attempt to bind using the LDAP://<SID=xxx> method. In most cases
these are caused by a migration process that utilised SID history and has not
been cleaned up since and in this case are added to the tokenGroups attribute
because when three of the other groups are.
The middle column designates the groups scope will be one of G (global), L (local), U (universal) or ! (unknown) and may also be post fixed by S (system created). The Users group would be shown shown in Active Directory Users and Computers as Built-in Local but I personally prefer not to further muddy domain local vs. local with built-in local as well.