Use PowerShell to Search Active Directory Users’ Missing Values and Add Same

Use PowerShell to Search Active Directory Users’ Missing Values and Add Same

  • Comments 6
  • Likes

 

Summary: Learn how to use Windows PowerShell to search Microsoft Active Directory for users that have missing values, and automatically add default values.

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! We have several network administrators where I work. Unfortunately, all of them have complete rights to Active Directory. This means that any one of the administrators can create a user. We have scripts that we are supposed to use when creating a user, but from time to time, one of the administrators will use Active Directory Users and Computers to manually create the user. When this happens, invariably something gets messed up.

We just had an audit, and we got hit on the inspection due to the fact that our users are created in an inconsistent manner. We have 30 days to resolve our deficiencies before “stuff” hits the fan. What I need is a Windows PowerShell script that will search a particular Active Directory organizational unit for users that are missing a value for a specific attribute. After a user has been identified, I need the script to add a value for that attribute. Do you have a script that can perform this task for us?

-- CW

 

Hey, Scripting Guy! Answer

Hello CW,

Microsoft Scripting Guy Ed Wilson here. I keep waiting for things to slow down. It seems like it does not matter how many meetings one goes to, or how many emails one answers. There are always more coming down the pike. It is like “honey do” lists at home or lists of scripts I want to write. I have a whiteboard in my office that contains a list of scripts I want to write someday. It seemed like a good idea at the time. The scripts are all things that will take a bit of research to write. The problem is that script ideas come to the board faster than I can erase completed scripts to make room for more scripts. Scribbles cover the board; I may add a second board.

CW, you are in luck because one of my whiteboard hieroglyphics represents the exact script scenario you detailed in your email to scripter@microsoft.com. Actually, we are both in luck because I get to erase said hieroglyph from the board. The complete FindAndAddEmptyADValues.ps1 Windows PowerShell script is shown here.

FindAndAddEmptyADValues.ps1

Function Get-UsersFromOU
{
 
Param(
 
$SearchRoot,
 
$filter = "ObjectCategory=Person"
 
)
 
$ds = [adsiSearcher]$filter
 
$ds.SearchRoot = [adsi]"LDAP://$SearchRoot"
 
$ds.findAll() 
} #end get-usersFromOu
Function Set-AttributeValue
{
 
Param(
 
$SR,
 
$SearchAttribute,
 
$NewValue
 
)
 
$i = $null
 
Foreach($user in $sr) {
 
IF([string]::isNullOrEmpty($user.properties.item($SearchAttribute)))
  
{
    
$de = $user.GetDirectoryEntry()
     "modifying $($de.name)"
    
$de.$SearchAttribute = $newValue
    
$de.setinfo()
    
$i++
  
} #end if
 
} #end foreach-object
  "modified $i users"
} # end Set-AttributeValue

# *** Entry Point to script ***

$sr = Get-UsersFromOU -SearchRoot "ou=testou,dc=nwtraders,dc=com"
Set-AttributeValue -SR $sr -SearchAttribute "description" -NewValue "By Script"

The FindAndAddEmptyADValues.ps1 begins with the Get-UsersFromOU function. The function accepts two parameters: the first parameter specifies the organizational unit (OU) to use for the search root, and the second parameter specifies the search filter. The search filter is set to filter out objects that are equal to the objectcategory of person. A filter such as this one will exclude computer objects. The parameter portion of the Get-UsersFromOU function is shown here:

Function Get-UsersFromOU
{
 
Param(
 
$SearchRoot,
 
$filter = "ObjectCategory=Person"
 
)

The [adsiSearcher] type accelerator is used to create an instance of the System.DirectoryServices.DirectorySearcher .NET Framework class. The search filter is supplied to the [adsiSearcher] type accelerator for its constructor. Other ways of creating an instance of the DirectorySearcher class are documented on MSDN. The DirectorySearcher class is created here and stored in the $ds variable:

$ds = [adsiSearcher]$filter

The searchRoot property of the DirectorySearcher needs to be an instance of a System.DirectoryServices.DirectoryEntry .NET Framework class. To easily create an instance of the DirectoryEntry class, use the [adsi] type accelerator as shown here:

$ds.SearchRoot = [adsi]"LDAP://$SearchRoot"

To find all of the objects that meet the search filter and are located in the search root, use the findall() method shown here:

$ds.findAll()

The complete Get-UsersFromOU function is shown here.

Function Get-UsersFromOU
{
 
Param(
 
$SearchRoot,
 
$filter = "ObjectCategory=Person"
 
)
 
$ds = [adsiSearcher]$filter
 
$ds.SearchRoot = [adsi]"LDAP://$SearchRoot"
 
$ds.findAll() 
} #end get-UsersFromOU

The Set-AttributeValue function accepts three input parameters. The first is an instance of the System.DirectoryServices.SearchResultCollection class that is returned from the Get-UsersFromOU function. The $searchAttribute parameter is the attribute that is to be checked for a value, and a value is supplied if it is empty or null. The $newValue parameter is the string value to add to the empty or null attribute value. This portion of the Set-AttributeValue function is shown here:

Function Set-AttributeValue

{

 
Param(

 
$SR,

 
$SearchAttribute,

 
$NewValue

 
)

The $i variable is used to count the number of users that are modified. It has its initial value set to $null. Because the $sr variable contains a SearchResultCollection class, I need to walk through the collection to retrieve an individual instance of the SearchResult class. The $user variable will contain a single instance of the SearchResult class on each pass through the collection. The isNullOrEmpty static method is used from the system.string .NET Framework class. This portion of the function is shown here:

$i = $null

 
Foreach($user in $sr) {

 
IF([string]::isNullOrEmpty($user.properties.item($SearchAttribute)))

The SearchResult class has a GetDirectoryEntry method that will create a DirectoryEntry class that matches the SearchResult. The attribute receives a new value, and the setinfo() method is called to write the changes back to Active Directory. The value of the $i counter variable is then incremented, and a summary string is displayed in the Windows PowerShell console. This portion of the function is shown here:

{

    
$de = $user.GetDirectoryEntry()

     "modifying $($de.name)"

    
$de.$SearchAttribute = $newValue

    
$de.setinfo()

    
$i++

  
} #end if

 
} #end foreach-object

  "modified $i users"

} # end Set-AttributeValue

The complete Set-AttributeValue function is shown here.

Function Set-AttributeValue

{

 
Param(

 
$SR,

 
$SearchAttribute,

 
$NewValue

 
)

 
$i = $null

 
Foreach($user in $sr) {

 
IF([string]::isNullOrEmpty($user.properties.item($SearchAttribute)))

  
{

    
$de = $user.GetDirectoryEntry()

     "modifying $($de.name)"

    
$de.$SearchAttribute = $newValue

    
$de.setinfo()

    
$i++

  
} #end if

 
} #end foreach-object

  "modified $i users"

} # end Set-AttributeValue

The entry point to the FindAndAddEmptyAdValues.ps1 script calls the Get-UsersFromOU function and stores the returned SearchResultCollection in the $sr variable. It then calls the Set-AttributeValue function and passes the SearchResultCollection, an attribute to search, and a new value to supply if the attribute value is empty or null. This portion of the script is shown here:

# *** Entry Point to script ***

 

$sr = Get-UsersFromOU -SearchRoot "ou=testou,dc=nwtraders,dc=com"

Set-AttributeValue -SR $sr -SearchAttribute "description" -NewValue "By Script"

When I ran the script on my computer, the output shown in the following image appeared in my Windows PowerShell ISE.

Image of output from running script

 

CW, that is all there is to using Windows PowerShell to search AD DS for users with empty attribute values and then add values to those attributes. Active Directory Week will continue tomorrow when we will talk about using the Microsoft Active Directory cmdlets to search AD DS for users with empty attribute values.

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • You have just made my first PowerShell experiance a dream. If only FaxNumber and FacsimileTelephoneNumber were the same it would have been silky smooth.

    Thanks again.

  • That is something I was looking for...:)

    Thanks.

  • @Grzegorz I am glad it is what you were looking for.

    @Callum I do not understand the part of "If only FaxNumber and FacsimileTelephoneNumber were the same it would have been silky smooth" what exactly are you saying?

  • The only issue I have: what should be a SearchResultCollection is just a collection of SearchResult objects. OK, more information is needed... when calling the Dispose() method of the SearchResultCollection method invocation fails because the object is not actually a SearchResultCollection (not even type casting has resolved this issue.)

    Why call Dispose? "To prevent a memory leak, you must call the Dispose method when the SearchResultCollection object is no longer needed"

  • @Adam - PowerSHell GC does that for you.  Just rv the variable or leave it in the pipeline.

    $searcher.FindAll() | %{...process $_ }

    You cannot directly apply C# code info to PowerShell in most cases.

    The Dispose works with no issues so I am not sure what else you are having issues with.  Here is code from WS2008R2 DC.

    PS C:\scripts> $searcher=[adsisearcher]'objectcategory=computer'

    PS C:\scripts> $results=$searcher.FindAll()

    PS C:\scripts> $results

    Path                                                        Properties

    ----                                                        ----------

    LDAP://CN=SBS01,OU=Domain Controllers,                      {ridsetreferences, iscriticalsystemobject, logoncount,

    LDAP://CN=WS701,OU=SBSComputers,OU=Computers,OU=MyBusine... {iscriticalsystemobject, logoncount, codepage, objectca

    LDAP://CN=WS702,OU=SBSComputers,OU=Computers,OU=MyBusine... {iscriticalsystemobject, logoncount, codepage, objectca

    LDAP://CN=WS703,OU=SBSComputers,OU=Computers,OU=MyBusine... {iscriticalsystemobject, logoncount, codepage, objectca

    PS C:\scripts> $results.Dispose()

    PS C:\scripts>

  • thanks