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 “Hey, {username}, you have not filled out your information.” I know how to write a Windows PowerShell script to send an e-mail (I copied one from the TechNet Script Center Gallery). 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.
— KM
Hello KM,
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—40 miles round trip (64 kilometers) —and one trip for takeout barbeque to feed the neighbors’ kids. My knees are sore from climbing up and down the extension ladder to clean pine 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 tea from pine needles. 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.)
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 “honey do list,” but it usually involves dragging out a 40 feet (12 meters) extension ladder. I was therefore in my office early this morning, and eagerly opened the scripter@microsoft.com 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.
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.
FindUsersWithManagers.ps1]
$domain = “dc=nwtraders,dc=com”
$dse = [adsi]”LDAP://$domain”
$filter = ‘(&(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3)(manager=*))’
$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)
$manager = $null
$searcher.findall() |
ForEach-Object {
[adsi]$_.path
} |
Format-Table -AutoSize -Property name, manager
The first thing the FindUsersWithManagers.ps1 script does is assign the string “dc=nwtraders,dc=com” to the $domain variable. This string is the string that represents the NWTraders.Com domain. Each part of the domain name, nwtraders and com, is separated by “dc=”. You will need to modify this string to match your domain. Remember, each portion of the domain must be separated by “dc=”; therefore, if your domain is Northamerica.NWTraders.Com, you will need to assign the string “dc=northamerica,dc=nwtraders,dc=com” to the $domain variable. The first line of code for the FindUsersWithManagers.ps1 script is seen here:
$domain = “dc=nwtraders,dc=com”
The $domain variable is passed to the [adsi] type accelerator to create a DirectoryEntry object. (For more information about the DirectoryEntry object, see yesterday’s Hey, Scripting Guy! post.) The line of code that creates the DirectoryEntry object and stores it in the $dse variable is seen here.
$dse = [adsi]”LDAP://$domain”
The search filter is stored in the $filter variable. Hints about search filter syntax can be obtained by using the Active Directory Users and Computers search feature seen here:
The filter uses the LDAP dialect syntax. The search filter states that the objectCategory is equal to person, and the objectSid attribute is equal to anything. The manager attribute value can also be equal to anything. The & character at the beginning of the search filter means that everything will be AND’ed together. The hard part of the query string is the !SamAccountType portion seen here:
!samAccountType:1.2.840.113556.1.4.804:=3
The number 1.2.840.113556.1.4.804 means that you want to do a bitwise OR operation on the samAccountType Active Directory attribute. The 3 at the end of the string, is the number that will be bitwise OR’ed with the samAccountType value.
If the number 1.2.840.113556.1.4.803 had been used, it would mean that you wanted to do a bitwise AND operation. These two special numbers are used for LDAP queries and are documented in KB article 269181 in the TechNet Knowledge Base. The samAccountType 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 MSDN. By performing a bitwise OR operation in your search filter, the filter will return true if any bit from the samAccountType enumeration has a 3 in it. The samAccountType enumerations are seen in Table 1.
<
p style=”MARGIN: 3pt 0in” class=”TableNum-Title”>Table 1 samAccountType Enumerations
Enumeration |
Value |
SAM_DOMAIN_OBJECT |
0x0 |
SAM_GROUP_OBJECT |
0x10000000 |
SAM_NON_SECURITY_GROUP_OBJECT |
0x10000001 |
SAM_ALIAS_OBJECT |
0x20000000 |
SAM_NON_SECURITY_ALIAS_OBJECT |
0x20000001 |
SAM_USER_OBJECT |
0x30000000 |
SAM_NORMAL_USER_ACCOUNT |
0x30000000 |
SAM_MACHINE_ACCOUNT |
0x30000001 |
SAM_TRUST_ACCOUNT |
0x30000002 |
SAM_APP_BASIC_GROUP |
0x40000000 |
SAM_APP_QUERY_GROUP |
0x40000001 |
SAM_ACCOUNT_TYPE_MAX |
0x7fffffff |
The filter is seen here:
$filter = ‘(&(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3)(manager=*))’
The DirectorySearcher object is stored in the $searcher variable. This is seen here:
$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)
The value of $null is assigned to the $manager variable. This will keep your results from displaying bogus results if the $manager variable already exists. The findall method returns a searchResult object that contains the path to all of the objects that match the search filter. This section of the script is seen here:
$manager = $null
$searcher.findall() |
The ForEach-Object cmdlet receives the search results from the findall method, and uses the path property of the searchResult object along with the [adsi] type accelerator to create a DirectoryEntry object. The DirectoryEntry objects are piped to the Format-Table cmdlet to display the user name and the associated manager. This section of the script is seen here:
ForEach-Object {
[adsi]$_.path
} |
Format-Table -AutoSize -Property name, manager
When the FindUsersWithManagers.ps1 script is run, the following is displayed on the Windows PowerShell console:
PS C:> C:Usersadministrator.NWTRADERSDocumentsFindUsersWithManagers.ps1
name manager
—- ——-
{testuser1} {CN=bob,OU=test,DC=NWTraders,DC=Com}
{testuser2} {CN=user Manager,OU=testou,DC=NWTraders,DC=Com}
PS C:>
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.
FindUsersWithOutManagers.ps1
$domain = “dc=nwtraders,dc=com”
$dse = [adsi]”LDAP://$domain”
$filter = ‘(&(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3))’
$searcher = New-Object DirectoryServices.DirectorySearcher ($dse, $filter)
$manager = $null
$searcher.findall() |
ForEach-Object {
[adsi]$_.path |
Where-Object { [string]::IsNullOrEmpty($_.properties.Item(“Manager”)) }
} |
Format-Table -AutoSize -Property name
The FindUsersWithOutManagers.ps1 script is very similar to the FindUsersWithManagers.ps1 script. The big difference is the addition of the Where-Object filter inside the ForEach-Object cmdlet. The Where-Object is used to filter out users that have a null or empty value for the Manager attribute. A user with a filled-out manager attribute is seen here:
The Where-Object cmdlet uses the static IsNullOrEmpty method from the string class. If the value that is being tested is null or empty, the IsNullOrEmpty static method from the string class will return true. If the value being tested is not empty or null, the IsNullOrEmpty method will return false. This is illustrated here:
PS C:> $test = $null
PS C:> [string]::isNullOrEmpty($test)
True
PS C:> $test = “value”
PS C:> [string]::isNullOrEmpty($test)
False
PS C:> $test = “”
PS C:> [string]::isNullOrEmpty($test)
True
PS C:>
The complete Where-Object statement is seen here:
Where-Object { [string]::IsNullOrEmpty($_.properties.Item(“Manager”)) }
When the FindUsersWithOutManagers.ps1 script is run, the following results are returned:
0 comments