<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Indented! &#187; groups</title>
	<atom:link href="http://www.indented.co.uk/index.php/tag/groups/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.indented.co.uk</link>
	<description></description>
	<lastBuildDate>Mon, 17 Oct 2011 19:03:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Active Directory, VbScript &amp; testing group membership</title>
		<link>http://www.indented.co.uk/index.php/2008/10/21/vbscript-active-directory-group-membership/</link>
		<comments>http://www.indented.co.uk/index.php/2008/10/21/vbscript-active-directory-group-membership/#comments</comments>
		<pubDate>Tue, 21 Oct 2008 13:49:40 +0000</pubDate>
		<dc:creator>Chris</dc:creator>
				<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[VbScript]]></category>
		<category><![CDATA[groups]]></category>
		<category><![CDATA[ismember]]></category>
		<category><![CDATA[member]]></category>
		<category><![CDATA[memberOf]]></category>
		<category><![CDATA[nested groups]]></category>
		<category><![CDATA[vbs]]></category>

		<guid isPermaLink="false">http://www.highorbit.co.uk/?p=251</guid>
		<description><![CDATA[When it comes to testing for group membership in Active Directory with VbScript there are a lot of different options. The following examples intend to demonstrate the basic to the complex. WinNT vs LDAP Not only does the structure of each group have to be considered but there are two separate providers to work with. [...]
No related posts.

Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.]]></description>
			<content:encoded><![CDATA[<p>When it comes to testing for group membership in Active Directory with VbScript there are a lot of different options. The following examples intend to demonstrate the basic to the complex.<br />
<span id="more-251"></span></p>
<h3>WinNT vs LDAP</h3>
<p>Not only does the structure of each group have to be considered but there are two separate providers to work with. To an extent these are inter-changeable, the examples below prefer to use LDAP. The advantages of LDAP become clear when performing more complex actions such as testing or returning nested membership.</p>
<p>The WinNT provider can still be useful as it is less complex, even if it does not grant access to a full set of attributes in AD.</p>
<h3>Primary Groups</h3>
<p>The Primary Group for an account is not listed in the memberOf attribute within AD and therefore not returned using LDAP. It is linked by the primaryGroupID attribute which matches the primaryGroupToken attribute on the group itself.</p>
<p>The WinNT provider on the other hand will list the Primary Group for an account using the MemberOf method.</p>
<p>None of the examples below explicitly check primary group membership.</p>
<h3>ADSystemInfo</h3>
<p>The ADSystemInfo interface is extremely useful when writing logon scripts, one of the most common reasons for checking group membership. It is documented on the MSDN area of Microsoft&#8217;s website as <a href="http://msdn.microsoft.com/en-us/library/aa705962(VS.85).aspx">IADsADSystemInfo</a>.</p>
<p>For the examples below the UserName or ComputerName properties are the most useful. The properties contain the distinguished names for the current user and current computer respectively.</p>
<h3>IsMember method</h3>
<p>Available in both the WinNT and LDAP provider the IsMember method can be called on a group object to test whether the ADSPath passed in belongs to that group.</p>
<h4>ADSPath using LDAP</h4>
<p>The ADSPath when using LDAP is written as follows and documented by Microsoft <a href="http://msdn.microsoft.com/en-us/library/aa746384(VS.85).aspx">here</a>.</p>
<pre class="brush: plain; title: ; notranslate">
ConnectionProvider://Server/DistinguishedName
LDAP://somedomain.example/CN=Chris Dent,OU=Somewhere,DC=somedomain,DC=example
</pre>
<h4>ADSPath using WinNT</h4>
<p>The ADSPath when using WinNT is written as follows and documented by Microsoft <a href="http://msdn.microsoft.com/en-us/library/aa746534(VS.85).aspx">here</a>.</p>
<pre class="brush: plain; title: ; notranslate">
ConnectionProvider://Server/Object Name
WinNT://somedomain.example/Chris
</pre>
<h4>Using LDAP and the IsMember method</h4>
<pre class="brush: vb; title: ; notranslate">
strGroupDN = &quot;CN=Domain Admins,CN=Users,DC=somedomain,DC=example&quot;
Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroupDN)

Set objADSysInfo = CreateObject(&quot;ADSystemInfo&quot;)
' strUserDN will look like CN=Chris Dent,OU=Somewhere,DC=somedomain,DC=example
strUserDN = objADSysInfo.UserName

If objGroup.IsMember(&quot;LDAP://&quot; &amp; strUserDN) Then
  ' The user is in the group so we can do things
End If
</pre>
<h4>Using WinNT and the IsMember method</h4>
<pre class="brush: vb; title: ; notranslate">
strGroupADSPath = &quot;WinNT://somedomain.example/Domain Admins&quot;
Set objGroup = GetObject(strGroupADSPath)

strUserADSPath = &quot;WinNT://somedomain.example/Chris&quot;

If objGroup.IsMember(strUserADSPath) Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Group concatenation</h3>
<p>Perhaps the simplest way of getting a list of groups from a user is to connect to the user and use the Join method to concatenate all of the groups into a single string.</p>
<p>This example makes use of GetEx with the LDAP provider, the method ensures that the list of groups is returned as an Array. If the user is only a member of a single group and Get is used the value returned would be a string which will break the script when Join is used.</p>
<p>The WinNT interface is a little more complex than the LDAP interface in this case.</p>
<p>The InStr function used to test for a group is okay, but can be a bit too accommodating. For instance, imagine these groups were returned with the LDAP interface:</p>
<pre class="brush: plain; title: ; notranslate">
CN=Domain Admins,CN=Users,DC=somedomain,DC=example
CN=Admins,OU=Users,OU=London,DC=somedomain,DC=example
</pre>
<p>In this case, if group membership of Admins were tested as follows:</p>
<pre class="brush: vb; title: ; notranslate">
If InStr(1, strGroups, &quot;Admins&quot;, VbTextCompare) &gt; 0 Then
</pre>
<p>It would match both Domain Admins and Admins. It is possible to work around this issue by including &#8220;CN=&#8221; and the trailing comma in the group name, effectively providing an explicit start and end to the group name.</p>
<p>The main advantages of this approach are speed and simplicity.</p>
<h4>Using LDAP to retrieve groups as a string</h4>
<pre class="brush: vb; title: ; notranslate">
Set objADSysInfo = CreateObject(&quot;ADSystemInfo&quot;)
Set objUser = GetObject(&quot;LDAP://&quot; &amp; objADSysInfo.Username)

strGroups = Join(objUser.GetEx(&quot;memberOf&quot;))

If InStr(1, strGroups, &quot;Domain Admins&quot;, VbTextCompare) &gt; 0 Then
  ' The user is in the group so we can do things
End If
</pre>
<h4>Using WinNT to retrieve groups as a string</h4>
<pre class="brush: vb; title: ; notranslate">
Set objShell = CreateObject(&quot;WScript.Shell&quot;)
strDomain = objShell.ExpandEnvironmentStrings(&quot;%USERDOMAIN%&quot;)
strUser = objShell.ExpandEnvironmentStrings(&quot;%USERNAME%&quot;)

Set objUser = GetObject(&quot;WinNT://&quot; &amp; strDomain &amp; _
  &quot;/&quot; &amp; strUser)

For Each objGroup in objUser.Groups
  strGroups = strGroups &amp; objGroup.Name
Next

If InStr(1, strGroups, &quot;Domain Admins&quot;, VbTextCompare) &gt; 0 Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Looping through MemberOf</h3>
<p>This function starts with a group name, then checks to see if the current user belongs to the group name. In the previous methods a full Distinguished Name must be specified, or care must be taken with the group name. This function does not require a lot of care, but it is a lot of work if a lot of testing is being done.</p>
<pre class="brush: vb; title: ; notranslate">
Function IsMember(strGroup)
  ' Returns true if the user is a member of strGroup

  Dim strGroupDN
  Dim objUser, objGroup
  Dim booIsMember

  Set objADSystemInfo = CreateObject(&quot;ADSystemInfo&quot;)
  Set objUser = GetObject(&quot;LDAP://&quot; &amp; objADSystemInfo.UserName)

  On Error Resume Next
  booIsMember = False
  ' GetEx is used here. This always returns an Array for the queried
  ' attribute, even if the array only has one element.
  For Each strGroupDN In objUser.GetEx(&quot;memberOf&quot;)
    Err.Clear
    Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroupDN)
    If Err.Number = 0 Then
      If LCase(objGroup.Get(&quot;name&quot;)) = LCase(strGroup) Then
        booIsMember = True
        Exit For
      End If
    End If
    Set objGroup = Nothing
  Next
  On Error Goto 0

  IsMember = booIsMember
End Function

' Example

If IsMember(&quot;Domain Admins&quot;) = True Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Looping through MemberOf with recursion</h3>
<p>This recursive function allows testing of membership in a nested chain.</p>
<p>A more concise version of this function is available from Microsoft in the <a href="http://technet.microsoft.com/en-us/magazine/cc161018.aspx">&#8220;Hey, Scripting Guy!&#8221;</a> posting.</p>
<p>This version is intended to stand on alone and drift down through nested groups for the current user with a minimal amount of input. It returns True or False depending on whether a group with a matching name was found in the chain.</p>
<p>Note that this can get caught in an infinite loop if it encounters circular group membership.</p>
<pre class="brush: vb; title: ; notranslate">
Function RecursiveIsMember(strGroup, arrGroups, x)
  ' Goes through Nested Groups until either booIsMember is True or there are
  ' no more groups to check

  Dim objADSystemInfo, objUser, objGroup
  Dim strGroupDN
  Dim arrTemp
  Dim booIsMember

  booIsMember = False

  On Error Resume Next
  If Not IsArray(arrGroups) Then
    Set objADSystemInfo = CreateObject(&quot;ADSystemInfo&quot;)
    Set objUser = GetObject(&quot;LDAP://&quot; &amp; objADSystemInfo.UserName)
    arrGroups = objUser.GetEx(&quot;memberOf&quot;)
  End If

  For Each strGroupDN in arrGroups
    Err.Clear
    Set objGroup = GetObject(&quot;LDAP://&quot; &amp; strGroupDN)
    ' WScript.Echo Space(x) &amp; objGroup.Get(&quot;name&quot;)
    If Err.Number = 0 Then
      If LCase(objGroup.Get(&quot;name&quot;)) = LCase(strGroup) Then

        booIsMember = True
        Exit For
      Else
        Err.Clear
        arrTemp = objGroup.GetEx(&quot;memberOf&quot;)
        If Err.Number = 0 Then
          y = x + 2
          booIsMember = RecursiveIsMember(strGroup, arrTemp, y)
          If booIsMember = True Then
            Exit For
          End If
        End If
      End If
    End If
    Set objGroup = Nothing
  Next
  On Error Goto 0
  RecursiveIsMember = booIsMember
End Function

' Calling the function:
' The group name, a blank value for the groups array and 0
' 0 is used to debug, debugging echoes the nested group structure as a tree
If RecursiveIsMember(&quot;Some Group&quot;, &quot;&quot;, 0) = True Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Using an LDAP Search to check &#8220;member&#8221;</h3>
<p>This uses an LDAP search to find all the groups a user belongs to.</p>
<pre class="brush: vb; title: ; notranslate">
Function IsMember(strGroup)
  Dim objADSysInfo, objConnection, objRootDSE, objRecordSet
  Dim strUserDN, strFilter
  Dim booIsMember

  booIsMember = False

  Set objADSysInfo = CreateObject(&quot;ADSystemInfo&quot;)
  strUserDN = objADSysInfo.UserName

  strFilter = &quot;(member=&quot; &amp; strUserDN &amp; &quot;)&quot;

  Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
  objConnection.Provider = &quot;ADsDSOObject&quot;
  objConnection.Open &quot;Active Directory Provider&quot;

  Set objRootDSE = GetObject(&quot;LDAP://RootDSE&quot;)
  Set objRecordSet = objConnection.Execute( _
    &quot;&lt;ldap://&quot; &amp; objRootDSE.Get(&quot;defaultNamingContext&quot;) &amp; &quot;&gt;;&quot; &amp; _
    strFilter &amp; &quot;;&quot; &amp; &quot;name;subtree&quot;)

  While Not objRecordSet.EOF
    If LCase(objRecordSet.Fields(&quot;name&quot;).Value) = LCase(strGroup) Then
      booIsMember = True
    End If
    objRecordSet.MoveNext
  WEnd

  IsMember = booIsMember
End Function

If IsMember(&quot;Domain Admins&quot;) = True Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Using LDAP_MATCHING_RULE_IN_CHAIN to check &#8220;member&#8221;</h3>
<p>With Windows 2003 SP1 Microsoft added an Object Identifier (OID) that allows searching through nested group structures with a single LDAP search. This is a powerful option and can significantly simplify tasks involving nested membership.</p>
<p>All it needs is a tiny modification to the filter used in the search example above.</p>
<pre class="brush: vb; title: ; notranslate">
Function IsMember(strGroup)
  Dim objADSysInfo, objConnection, objRootDSE, objRecordSet
  Dim strUserDN, strFilter
  Dim booIsMember

  booIsMember = False

  Set objADSysInfo = CreateObject(&quot;ADSystemInfo&quot;)
  strUserDN = objADSysInfo.UserName

  strFilter = &quot;(member:1.2.840.113556.1.4.1941:=&quot; &amp; strUserDN &amp; &quot;)&quot;

  Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
  objConnection.Provider = &quot;ADsDSOObject&quot;
  objConnection.Open &quot;Active Directory Provider&quot;

  Set objRootDSE = GetObject(&quot;LDAP://RootDSE&quot;)
  Set objRecordSet = objConnection.Execute( _
    &quot;&lt;ldap://&quot; &amp; objRootDSE.Get(&quot;defaultNamingContext&quot;) &amp; &quot;&gt;;&quot; &amp; _
    strFilter &amp; &quot;;&quot; &amp; &quot;name;subtree&quot;)

  While Not objRecordSet.EOF
    If LCase(objRecordSet.Fields(&quot;name&quot;).Value) = LCase(strGroup) Then
      booIsMember = True
    End If
    objRecordSet.MoveNext
  WEnd

  IsMember = booIsMember
End Function

If IsMember(&quot;Domain Admins&quot;) = True Then
  ' The user is in the group so we can do things
End If
</pre>
<h3>Returning all groups a user belongs to</h3>
<p>Checking groups one by one like the examples above is hard work. It would be much better if we got a list of them once then held onto that for as long as a script needs it. This modification of the function above does just that, it returns a dictionary object containing all of the users groups. It can return all nested groups as well by making a small change to the filter.</p>
<pre class="brush: vb; title: ; notranslate">
Function GetAllGroups
  Dim objADSysInfo, objConnection, objRootDSE, objRecordSet, objGroups
  Dim strUserDN, strFilter

  Set objADSysInfo = CreateObject(&quot;ADSystemInfo&quot;)
  strUserDN = objADSysInfo.UserName

  strFilter = &quot;(member=&quot; &amp; strUserDN &amp; &quot;)&quot;
  ' Alternative search filter to test nested groups
  ' strFilter = &quot;(member:1.2.840.113556.1.4.1941:=&quot; &amp; strUserDN &amp; &quot;)&quot;

  Set objConnection = CreateObject(&quot;ADODB.Connection&quot;)
  objConnection.Provider = &quot;ADsDSOObject&quot;
  objConnection.Open &quot;Active Directory Provider&quot;

  Set objRootDSE = GetObject(&quot;LDAP://RootDSE&quot;)
  Set objRecordSet = objConnection.Execute( _
    &quot;&lt;ldap://&quot; &amp; objRootDSE.Get(&quot;defaultNamingContext&quot;) &amp; &quot;&gt;;&quot; &amp; _
    strFilter &amp; &quot;;distinguishedName,name;subtree&quot;)

  Set objGroups = CreateObject(&quot;Scripting.Dictionary&quot;)
  objGroups.CompareMode = VbTextCompare

  While Not objRecordSet.EOF
    strGroup = objRecordSet.Fields(&quot;name&quot;).Value
    If Not objGroups.Exists(strGroup) Then
      objGroups.Add UCase(strGroup), &quot;&quot;
    End If
    objRecordSet.MoveNext
  WEnd

  Set GetAllGroups = objGroups
End Function
</pre>
<p>Finally, the function can be used as follows.</p>
<pre class="brush: vb; title: ; notranslate">
Set objUsersGroups = GetAllGroups

' Using .Exists (case-insensitive)
If objUsersGroups.Exists(&quot;some group&quot;) Then
  ' Do stuff
End If

' Using a loop and select case (case-sensitive)
For Each strGroup in objUsersGroups
  Select Case strGroup
    Case &quot;DOMAIN ADMINS&quot;
      ' Do stuff
    Case &quot;LONDON USERS&quot;
      ' Do stuff
  End Select
Next
</pre>
<p>No related posts.</p>
<p>Related posts brought to you by <a href='http://yarpp.org'>Yet Another Related Posts Plugin</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://www.indented.co.uk/index.php/2008/10/21/vbscript-active-directory-group-membership/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

