{"id":63433,"date":"2007-12-08T01:09:00","date_gmt":"2007-12-08T01:09:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2007\/12\/08\/hey-scripting-guy-how-can-i-map-drives-based-on-membership-in-an-active-directory-group-when-the-user-belongs-to-multiple-groups\/"},"modified":"2007-12-08T01:09:00","modified_gmt":"2007-12-08T01:09:00","slug":"hey-scripting-guy-how-can-i-map-drives-based-on-membership-in-an-active-directory-group-when-the-user-belongs-to-multiple-groups","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-map-drives-based-on-membership-in-an-active-directory-group-when-the-user-belongs-to-multiple-groups\/","title":{"rendered":"Hey, Scripting Guy! How Can I Map Drives Based on Membership in an Active Directory Group When the User Belongs to Multiple Groups?"},"content":{"rendered":"<p><H2><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\"> <\/H2>\n<P>Hey, Scripting Guy! In <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/dec07\/hey1206.mspx\"><B>yesterday\u2019s column<\/B><\/A> you showed a sample script that mapped a drive based on membership in an Active Directory group; along the way, you brought up the possibility of a user belonging to more than one group. \u201cSuppose you have two groups,\u201d you said. \u201cWhat happens if a user is a member of both groups?\u201d You then said, and I quote, \u201cBut that\u2019s something we\u2019ll have to tackle on another day. Tomorrow? We\u2019ll see.\u201d Well, it\u2019s tomorrow: are you going to tackle that issue or not?<BR><BR>&#8212; SE<\/P><IMG border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\"><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\"><A href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><IMG class=\"farGraphic\" title=\"Script Center\" border=\"0\" alt=\"Script Center\" align=\"right\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" height=\"288\"><\/A> \n<P>Hey, SE. By the way, in case anyone is wondering, we\u2019re pretty sure that SE is really the Scripting Editor. How do we know that? Well, for one thing, the Scripting Guy who writes this column is a keen student of human psychology; after carefully analyzing the vocabulary and syntax found in this email he believes there\u2019s at least a 97% chance that the message was written by the Scripting Editor.<\/P>\n<P>And, besides, the Scripting Editor is currently sitting across the desk from him, brandishing her red pencil and demanding to know why the Scripting Guy who writes this column would make yet another promise to do something: \u201cWhy do you keep doing <I>that<\/I>?\u201d <\/P>\n<TABLE id=\"EGD\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P><B>Note<\/B>. As it turns out, the Scripting Editor doesn\u2019t like it when we say, in print, that we\u2019ll do something. Why not? Who knows; apparently that\u2019s just the way she is. After all, look at our track record: other than the Active Directory browser (which, in our defense, we never said that we <I>would<\/I> do), we can\u2019t think of a single thing that we said we\u2019d do (or might do) and then never followed through on.<\/P>\n<P>OK, maybe part 3 of the HTA Tutorial series. And the next installment in the Active Directory series. And \u2013 well, that\u2019s all we can think of at the moment. If you can think of any others, please do <I>not<\/I> send us an email and let us know. We don\u2019t need to add any fuel to the Scripting Editor\u2019s fire.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>Anyway, thanks to SE (aka the Scripting Editor ), today\u2019s column is a continuation, of sorts, to yesterday\u2019s <I>Hey, Scripting Guy!<\/I> As SE noted, yesterday we showed you a script that maps a drive based on the user\u2019s membership in a particular Active Directory group. After explaining the basic script we extended the code a little, showing how you could map drive X to one share if the user was a member of group A, or map drive X to a different share if the user was a member of group B. And then we raised the question that also raised the Scripting Editor\u2019s hackles: what do you do if a user happens to be a member of <I>both<\/I> group A and group B?<\/P>\n<P>Well, <I>as promised<\/I>, here\u2019s one solution to that problem:<\/P><PRE class=\"codeSample\">On Error Resume Next<\/p>\n<p>Set objDictionary = CreateObject(&#8220;Scripting.Dictionary&#8221;)\nSet objNetwork = CreateObject(&#8220;Wscript.Network&#8221;)<\/p>\n<p>Set objADSysInfo = CreateObject(&#8220;ADSystemInfo&#8221;)\nstrUser = objADSysInfo.UserName<\/p>\n<p>Set objUser = GetObject(&#8220;LDAP:\/\/&#8221; &amp; strUser)<\/p>\n<p>For Each strGroup in objUser.memberOf\n    Set objGroup = GetObject(&#8220;LDAP:\/\/&#8221; &amp; strGroup)\n    strGroupName = objGroup.CN\n    objDictionary.Add strGroupName, strGroupName   \nNext<\/p>\n<p>If objDictionary.Exists(&#8220;Finance Managers&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\managers&#8221;\n    Wscript.Quit\nEnd If<\/p>\n<p>If objDictionary.Exists(&#8220;Finance Users&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\users&#8221;\n    Wscript.Quit\nEnd If<\/p>\n<p>If objDictionary.Exists(&#8220;Finance Planners&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\planners&#8221;\n    Wscript.Quit\nEnd If<\/p>\n<p>If objDictionary.Exists(&#8220;Finance Consultants&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\consultants&#8221;\n    Wscript.Quit\nEnd If\n<\/PRE>\n<P>And some people (we\u2019re looking at you, \u201cSE\u201d) think that the Scripting Guys never keep their promises. Well, what do you think of us <I>now<\/I>, \u201cSE\u201d?<\/P>\n<P>Really? Yikes! <\/P>\n<TABLE id=\"ENE\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\"><B>Note<\/B>. Although we shouldn\u2019t be surprised; after all, editors have to know a <I>lot<\/I> of words. We just wouldn\u2019t have guessed that SE knew words like <I>those<\/I>.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>But that\u2019s OK; sticks and stones, right? Before we dive into the details of the code, let\u2019s quickly discuss the scenario we\u2019re working under. For our purposes, we\u2019re assuming we have four groups of interest in Active Directory:<\/P>\n<TABLE border=\"0\" cellSpacing=\"0\" cellPadding=\"0\">\n<TBODY>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Finance Managers<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Finance Users<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Finance Planners<\/P><\/TD><\/TR>\n<TR>\n<TD class=\"listBullet\" vAlign=\"top\">\u2022<\/TD>\n<TD class=\"listItem\">\n<P>Finance Consultants<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<P>The idea here is that we\u2019re going to use a logon script to map drive X to a specified share. Which share? Well, that depends on which group the user belongs to; for example, members of the Finance Managers group will have drive X mapped to a different share than members of the Finance Users group.<\/P>\n<P>So what\u2019s the big deal? The big deal is that a user can be a member of more than one of these groups; that is, any given user could be in the Finance Managers group as well as the Finance Users, or even the Finance Planners, group. <\/P>\n<P>On top of that, some groups take priority over other groups; in fact, the groups are listed in order of precedence. What does that mean? Well, suppose a user <I>is<\/I> a member of three groups: Finance Managers, Finance Users, and Finance Planners. How do we determine which share drive X gets mapped to? That\u2019s where the order of precedence comes into play. Because Finance Managers appears at the top of the list we\u2019ll use that group as the user\u2019s primary group (well, their primary group for drive-mapping purposes, anyway). What if a user is a member of Finance Planners and Finance Consultants? You got it: Finance Planners takes precedence, so we\u2019ll use Finance Planners as the primary group.<\/P>\n<P>Etc., etc.<\/P>\n<P>Got that? Good; then let\u2019s get started. <\/P>\n<P>The script kicks off by creating instances of the <B>Scripting.Dictionary<\/B> and <B>Wscript.Network<\/B> objects. We\u2019ll use the Network object to map the drive. And the Dictionary object? We\u2019ll talk about that in just a moment.<\/P>\n<P>After creating these two objects, we then create a third object: <B>ADSystemInfo<\/B>, an object that enables us to grab the user\u2019s distinguished name (DN). Once we have the DN we then bind to the user account in Active Directory and retrieve a collection of all the groups that the user belongs to. (See <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/dec07\/hey1206.mspx\"><B>yesterday\u2019s column<\/B><\/A> for complete details.) After that we use the following line of code to set up a For Each loop that loops us through all the groups that the user belongs to:<\/P><PRE class=\"codeSample\">For Each strGroup in objUser.memberOf\n<\/PRE>\n<P>Inside this loop we use the DN for the first group to bind to the group account in Active Directory. (Again, see yesterday\u2019s column for more information.) We then use this line of code to store the group CN in a variable named strGroupName:<\/P><PRE class=\"codeSample\">strGroupName = objGroup.CN\n<\/PRE>\n<P>Once we have the group CN we then add that group to our Dictionary object, using the group CN as both the Dictionary key and the Dictionary item:<\/P><PRE class=\"codeSample\">objDictionary.Add strGroupName, strGroupName\n<\/PRE>\n<P>What\u2019s the point of all that? Try to be patient for another minute; we\u2019re almost ready to talk about the details of the Dictionary object.<\/P>\n<P>Before we do that, however, let\u2019s finish talking about the For Each loop. After we\u2019ve added the first group to the Dictionary we loop around and repeat the process with the next group in the collection. When we\u2019re all done, we\u2019ll have a Dictionary object that contains a list of all the groups that the user belongs to.<\/P>\n<P>So why <I>do<\/I> we need to have all the groups in a Dictionary object? That\u2019s easy: we can\u2019t really start mapping drives until we have a list of all the groups that the user belongs to. Why not? Well, suppose we\u2019re in the For Each loop and we discover that the user is a member of the Finance Consultants group. If we wanted to, we could go ahead and map drive X based on the user\u2019s membership in Finance Consultants. However, that\u2019s going to cause a problem if it turns out that the user is also a member of the Finance Users group. <\/P>\n<P>To help avoid that problem, we decided to grab all the group names and put them in a Dictionary object. After the groups are safely tucked away like that we can then use the Dictionary object\u2019s <B>Exists<\/B> method to determine whether the user is a member of a particular group. Equally important, we can check for group membership using the order of precedence.<\/P>\n<P>In other words, that\u2019s what this block of code is for:<\/P><PRE class=\"codeSample\">If objDictionary.Exists(&#8220;Finance Managers&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\managers&#8221;\n    Wscript.Quit\nEnd If\n<\/PRE>\n<P>What we\u2019re doing here is checking to see if the string value <I>Finance Managers<\/I> appears in our Dictionary. If it doesn\u2019t, that means that the user is not a member of Finance Managers. In that case we then skip down to the next block of code, which checks to see if the user is a member of Finance Users. (Note that we\u2019re following the order of precedence: we\u2019re checking for membership in Finance Managers before we check for membership in any other groups.)<\/P>\n<P>Let\u2019s assume that the value <I>Finance Managers<\/I> actually <I>does<\/I> appear in the Dictionary. That can mean only one thing: the user is a member of the Finance Managers group. In turn, we then use this line of code to map drive X to the share \\\\atl-fs-001\\public\\managers:<\/P><PRE class=\"codeSample\">objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\managers&#8221;\n<\/PRE>\n<P>From there we call the <B>Wscript.Quit<\/B> method to terminate the script. Why? Because the Finance Managers group takes precedence over any other group. In other words, once we\u2019ve determined that the user is a member of Finance Managers there\u2019s no reason to keep going; after we map a drive to \\\\atl-fs-001\\public\\managers we\u2019re done. Notice that we also use Wscript.Quit in our next block of code:<\/P><PRE class=\"codeSample\">If objDictionary.Exists(&#8220;Finance Users&#8221;) Then\n    objNetwork.MapNetworkDrive &#8220;X:&#8221;, &#8220;\\\\atl-fs-001\\public\\users&#8221;\n    Wscript.Quit\nEnd If\n<\/PRE>\n<P>Why? You got it: If the user is a member of Finance Users then we don\u2019t care whether they also belong to Finance Planners or Finance Consultants. That\u2019s because membership in Finance Users takes precedence over membership in those other two groups. Consequently, we map the drive and then terminate the script.<\/P>\n<P>Etc., etc.<\/P>\n<P>What if you needed to check for membership in 10 different groups? Just keep adding on these If Then blocks, making sure that you check for groups in order of precedence.<\/P>\n<P>We hope that answers your \u2026 question \u2026 SE. And we hope you\u2019ll begin to cut the Scripting Guy who writes this column a little slack, at least when it comes to making promises. After all, you know what Nikita Khrushchev once said about promises:<\/P>\n<TABLE id=\"EQH\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">If we were to promise people nothing better than only revolution, they would scratch their heads and say: \u201cIs it not better to have good goulash?\u201d<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>We couldn\u2019t have said it better ourselves.<\/P>\n<P>Speaking of goulash, did someone say that it\u2019s lunchtime? See you all tomorrow.<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! In yesterday\u2019s column you showed a sample script that mapped a drive based on membership in an Active Directory group; along the way, you brought up the possibility of a user belonging to more than one group. \u201cSuppose you have two groups,\u201d you said. \u201cWhat happens if a user is a member [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[7,44,3,185,12,20,5],"class_list":["post-63433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-groups","tag-scripting-guy","tag-shared-folders-and-mapped-drives","tag-storage","tag-user-accounts","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! In yesterday\u2019s column you showed a sample script that mapped a drive based on membership in an Active Directory group; along the way, you brought up the possibility of a user belonging to more than one group. \u201cSuppose you have two groups,\u201d you said. \u201cWhat happens if a user is a member [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/63433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=63433"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/63433\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=63433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=63433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=63433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}