{"id":52303,"date":"2009-10-07T03:01:00","date_gmt":"2009-10-07T03:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/10\/07\/hey-scripting-guy-can-i-get-a-list-of-all-users-in-a-domain-without-their-manager-attribute-populated\/"},"modified":"2009-10-07T03:01:00","modified_gmt":"2009-10-07T03:01:00","slug":"hey-scripting-guy-can-i-get-a-list-of-all-users-in-a-domain-without-their-manager-attribute-populated","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-can-i-get-a-list-of-all-users-in-a-domain-without-their-manager-attribute-populated\/","title":{"rendered":"Hey, Scripting Guy! Can I Get a List of All Users in a Domain Without Their Manager Attribute Populated?"},"content":{"rendered":"<p><!-- AddThis Button BEGIN --><a class=\"addthis_button\" href=\"http:\/\/www.addthis.com\/bookmark.php?v=250&amp;pub=scriptingguys\"><img decoding=\"async\" alt=\"Bookmark and Share\" src=\"http:\/\/s7.addthis.com\/static\/btn\/v2\/lg-share-en.gif\" width=\"125\" height=\"16\"><\/a>  <\/p>\n<p><img decoding=\"async\" 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\"><\/p>\n<p class=\"MsoNormal\">Hey, Scripting Guy! I need to get a list of all the users that have set their manager in Active Directory. We have this Web application that allows users to populate information such as their phone number, their office location, and their manager. We actually paid a decent amount of money for this application. The problem is that the users are not going to the Web site to update their information. We have sent out numerous e-mails to all of the users in the company: a spam-like admonishment. I can now confirm that generic reprimands do not have much success in getting users to update their information. What I would like to do is to be able to send a targeted e-mail that says &#8220;Hey, {username}, you have not filled out your information.&rdquo; I know how to write a Windows PowerShell script to send an e-mail (I copied one from the <a href=\"http:\/\/bit.ly\/2V1G9D\"><font face=\"Segoe\">TechNet Script Center Gallery<\/font><\/a>). What I need to do is to retrieve a listing of all the users in the domain that do not have their manager attribute populated. <\/p>\n<p class=\"MsoNormal\">&#8212; KM<\/p>\n<p><img decoding=\"async\" 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\"><\/p>\n<p class=\"MsoNormal\">Hello KM, <\/p>\n<p class=\"MsoNormal\">Microsoft Scripting Guy Ed Wilson here, you know the problem with weekends? It is not that they are too short; it is that they are too long. I am sore from all the climbing, cleaning, and general pre-winter maintenance work that needs to be done around the house. This included three separate trips to the local home hardware supply store&mdash;40 miles round trip (64 kilometers) &mdash;and one trip for takeout <a href=\"http:\/\/en.wikipedia.org\/wiki\/Barbecue\"><font face=\"Segoe\">barbeque<\/font><\/a> to feed the neighbors&rsquo; kids. My knees are sore from climbing up and down the extension ladder to clean <a href=\"http:\/\/en.wikipedia.org\/wiki\/Pine\"><font face=\"Segoe\">pine<\/font><\/a> needles from the rain gutters, and my left thumb is cut from the rain gutter itself. Actually I may have just solved my rain gutter problem. On my Bing search for pine needles, I saw that you can make <a href=\"http:\/\/www.ehow.com\/how_2102192_pine-needle-tea.html\"><font face=\"Segoe\">tea from pine needles<\/font><\/a>. If I strip off all the pine needles and make tea, the needles will not clog my rain gutters. (Stay tuned, and I will let you know what pine needle tea tastes like.) <\/p>\n<p class=\"MsoNormal\">Needless to say, KM, I am happy to get back to work, so I can get away from all the work the Scripting Wife has lined up for me before winter arrives. If she wanted me to write scripts, I would love her &#8220;honey do list,&#8221; but it usually involves dragging out a 40 feet (12 meters) <a href=\"http:\/\/en.wikipedia.org\/wiki\/Extension_ladder\"><font face=\"Segoe\">extension ladder<\/font><\/a>. I was therefore in my office early this morning, and eagerly opened the <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\"><font face=\"Segoe\">scripter@microsoft.com<\/font><\/a> e-mail box when I came across your e-mail. Because your script does not involve climbing, I will gladly write a script to provide you with a list of users that have not populated their managers in Active Directory. <\/p>\n<p class=\"MsoNormal\">KM, I wrote the FindUsersWithManagers.ps1 for you to help you find users that already have managers assigned. The complete FindUsersWithManagers.ps1 script is seen here. <\/p>\n<p class=\"CodeBlockScreenedHead\"><strong>FindUsersWithManagers.ps1]<\/strong><\/p>\n<p class=\"CodeBlockScreened\"><span><font><font face=\"Lucida Sans Typewriter\">$domain = &#8220;dc=nwtraders,dc=com&#8221;<br>$dse = [adsi]&#8221;LDAP:\/\/$domain&#8221;<br>$filter = &#8216;(&amp;(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3)(manager=*))&#8217;<br>$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)<br>$manager = $null<br>$searcher.findall() | <br>ForEach-Object {<br>[adsi]$_.path<br>} |<br>Format-Table -AutoSize -Property name, manager<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The first thing the FindUsersWithManagers.ps1 script does is assign the string &#8220;dc=nwtraders,dc=com&#8221; to the <b>$domain<\/b> variable. This string is the string that represents the NWTraders.Com domain. Each part of the domain name, <b>nwtraders<\/b> and <b>com<\/b>, is separated by &#8220;dc=&#8221;. You will need to modify this string to match your domain. Remember, each portion of the domain must be separated by &#8220;dc=&#8221;; therefore, if your domain is Northamerica.NWTraders.Com, you will need to assign the string &#8220;dc=northamerica,dc=nwtraders,dc=com&#8221; to the <b>$domain<\/b> variable. The first line of code for the FindUsersWithManagers.ps1 script is seen here: <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$domain = &#8220;dc=nwtraders,dc=com&#8221;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The <b>$domain<\/b> variable is passed to the <b>[adsi]<\/b> type accelerator to create a <b>DirectoryEntry<\/b> object. (For more information about the <b>DirectoryEntry<\/b> object, see <a href=\"http:\/\/blogs.technet.com\/heyscriptingguy\/archive\/2009\/10\/06\/hey-scripting-guy-october-6-2009.aspx\">yesterday&rsquo;s Hey, Scripting Guy! post<\/a>.) <span>&nbsp;<\/span>The line of code that creates the <b>DirectoryEntry<\/b> object and stores it in the <b>$dse<\/b> variable is seen here. <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$dse = [adsi]&#8221;LDAP:\/\/$domain&#8221;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The search filter is stored in the <b>$filter<\/b> variable.<span>&nbsp; <\/span>Hints about search filter syntax can be obtained by using the Active Directory Users and Computers search feature seen here:<\/p>\n<p class=\"Fig-Graphic\"><img decoding=\"async\" title=\"Image of Active Directory Users and Computers search feature\" alt=\"Image of Active Directory Users and Computers search feature\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/october\/hey1007\/hsg-10-07-09-01.jpg\" width=\"384\" height=\"345\"><\/p>\n<p class=\"MsoNormal\">The filter uses the LDAP dialect syntax. The search filter states that the <b>objectCategory<\/b> is equal to person, and the <b>objectSid<\/b> attribute is equal to anything. The <b>manager<\/b> attribute value can also be equal to anything. The <b>&amp;<\/b> character at the beginning of the search filter means that everything will be AND&rsquo;ed together. The hard part of the query string is the <b>!SamAccountType<\/b> portion seen here:<\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">!samAccountType:1.2.840.113556.1.4.804:=3<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The number 1.2.840.113556.1.4.804 means that you want to do a bitwise <b>OR<\/b> operation on the <b>samAccountType<\/b> Active Directory attribute. The 3 at the end of the string, is the number that will be bitwise OR&#8217;ed with the <b>samAccountType<\/b> value. <\/p>\n<p class=\"MsoNormal\">If the number 1.2.840.113556.1.4.803 had been used, it would mean that you wanted to do a bitwise <b>AND<\/b> operation. These two special numbers are used for LDAP queries and are documented in <a href=\"http:\/\/support.microsoft.com\/kb\/269181\"><font face=\"Segoe\">KB article 269181<\/font><\/a> in the TechNet Knowledge Base. The <b>samAccountType<\/b> attribute is an enumeration that indicates the type of object that is stored in Active Directory (normal user object, basic group object, non-security group object, query group, or other type of object). The enumeration values are documented on <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms679637(VS.85).aspx\"><font face=\"Segoe\">MSDN<\/font><\/a>. By performing a bitwise <b>OR<\/b> operation in your search filter, the filter will return true if any bit from the <b>samAccountType<\/b> enumeration has a 3 in it. The <b>samAccountType<\/b> enumerations are seen in Table 1.<\/p>\n<p>&lt;<\/p>\n<p>p style=&#8221;MARGIN: 3pt 0in&#8221; class=&#8221;TableNum-Title&#8221;&gt;<strong>Table 1<span>&nbsp; <\/span>samAccountType Enumerations<\/strong><\/p>\n<table class=\"MsoNormalTable\" border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableHead\">Enumeration<\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableHead\">Value<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_DOMAIN_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x0 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_GROUP_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x10000000 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_NON_SECURITY_GROUP_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x10000001 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_ALIAS_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x20000000 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_NON_SECURITY_ALIAS_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x20000001 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_USER_OBJECT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x30000000 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_NORMAL_USER_ACCOUNT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x30000000 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_MACHINE_ACCOUNT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x30000001 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_TRUST_ACCOUNT <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x30000002 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_APP_BASIC_GROUP <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x40000000 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_APP_QUERY_GROUP <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x40000001 <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\" width=\"312\">\n<p class=\"TableText\">SAM_ACCOUNT_TYPE_MAX <\/p>\n<\/td>\n<td valign=\"top\" width=\"300\">\n<p class=\"TableText\">0x7fffffff<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p class=\"TableText\">&nbsp;<\/p>\n<p class=\"MsoNormal\">The filter is seen here:<\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$filter = &#8216;(&amp;(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3)(manager=*))&#8217;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The <b>DirectorySearcher<\/b> object is stored in the <b>$searcher<\/b> variable. This is seen here: <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The value of <b>$null<\/b> is assigned to the <b>$manager<\/b> variable. This will keep your results from displaying bogus results if the <b>$manager<\/b> variable already exists. The <b>findall<\/b> method returns a <b>searchResult<\/b> object that contains the path to all of the objects that match the search filter. This section of the script is seen here: <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$manager = $null<br>$searcher.findall() |<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The <b>ForEach-Object<\/b> cmdlet receives the search results from the <b>findall<\/b> method, and uses the <b>path<\/b> property of the <b>searchResult<\/b> object along with the <b>[adsi]<\/b> type accelerator to create a <b>DirectoryEntry<\/b> object. The <b>DirectoryEntry<\/b> objects are piped to the <b>Format-Table<\/b> cmdlet to display the user name and the associated manager. This section of the script is seen here: <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">ForEach-Object {<br>[adsi]$_.path<br>} |<br>Format-Table -AutoSize -Property name, manager<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">When the FindUsersWithManagers.ps1 script is run, the following is displayed on the Windows PowerShell console: <\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">PS C:&gt; C:Usersadministrator.NWTRADERSDocumentsFindUsersWithManagers.ps1<\/p>\n<p>name<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>manager<br>&#8212;-<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&#8212;&#8212;-<br>{testuser1} {CN=bob,OU=test,DC=NWTraders,DC=Com}<br>{testuser2} {CN=user Manager,OU=testou,DC=NWTraders,DC=Com}<\/p>\n<p><br>PS C:&gt;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">But, KM, you said you wanted a listing of users who had not filled out their manager attribute. To do this, you can use the FindUsersWithOutManagers.ps1 script that I also wrote for you.<\/p>\n<p class=\"CodeBlockScreenedHead\"><strong>FindUsersWithOutManagers.ps1<\/strong><\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">$domain = &#8220;dc=nwtraders,dc=com&#8221;<br>$dse = [adsi]&#8221;LDAP:\/\/$domain&#8221;<br>$filter = &#8216;(&amp;(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3))&#8217;<br>$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)<br>$manager = $null<br>$searcher.findall() | <br>ForEach-Object {<br><span>&nbsp;<\/span>[adsi]$_.path | <br><span>&nbsp;<\/span>Where-Object { [string]::IsNullOrEmpty($_.properties.Item(&#8220;Manager&#8221;)) }<br>} |<br>Format-Table -AutoSize -Property name<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The FindUsersWithOutManagers.ps1 script is very similar to the FindUsersWithManagers.ps1 script. The big difference is the addition of the <b>Where-Object<\/b> filter inside the <b>ForEach-Object<\/b> cmdlet. The <b>Where-Object<\/b> is used to filter out users that have a null or empty value for the <b>Manager<\/b> attribute. A user with a filled-out manager attribute is seen here:<\/p>\n<p class=\"Fig-Graphic\"><img decoding=\"async\" title=\"Image of a user with a filled-out manager attribute\" alt=\"Image of a user with a filled-out manager attribute\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/october\/hey1007\/hsg-10-07-09-02.jpg\" width=\"404\" height=\"448\"><\/p>\n<p class=\"MsoNormal\">The <b>Where-Object<\/b> cmdlet uses the static <b>IsNullOrEmpty<\/b> method from the <b>string<\/b> class. If the value that is being tested is null or empty, the <b>IsNullOrEmpty<\/b> static method from the <b>string<\/b> class will return true. If the value being tested is not empty or null, the <b>IsNullOrEmpty<\/b> method will return false. This is illustrated here:<\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">PS C:&gt; $test = $null<br>PS C:&gt; [string]::isNullOrEmpty($test)<br>True<br>PS C:&gt; $test = &#8220;value&#8221;<br>PS C:&gt; [string]::isNullOrEmpty($test)<br>False<br>PS C:&gt; $test = &#8220;&#8221;<br>PS C:&gt; [string]::isNullOrEmpty($test)<br>True<br>PS C:&gt;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">The complete <b>Where-Object<\/b> statement is seen here:<\/p>\n<p class=\"CodeBlock\"><span><font face=\"Lucida Sans Typewriter\">Where-Object { [string]::IsNullOrEmpty($_.properties.Item(&#8220;Manager&#8221;)) }<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\">When the FindUsersWithOutManagers.ps1 script is run, the following results are returned: <\/p>\n<p class=\"CodeBlock\"><span><\/span><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! I need to get a list of all the users that have set their manager in Active Directory. We have this Web application that allows users to populate information such as their phone number, their office location, and their manager. We actually paid a decent amount of money for this application. The [&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,3,8,45],"class_list":["post-52303","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-scripting-guy","tag-searching-active-directory","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! I need to get a list of all the users that have set their manager in Active Directory. We have this Web application that allows users to populate information such as their phone number, their office location, and their manager. We actually paid a decent amount of money for this application. The [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/52303","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=52303"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/52303\/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=52303"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=52303"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=52303"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}