Tag Archives: Active Directory

Active Directory Friday: Determine the forest functional level

Knowing the Forest Functional Level can be important when implementing new products or when considering to upgrade your functional level. This information can be view in the ‘Active Directory Domains and Trusts’ console, but for the purpose of this article we will take a look how this information can be retrieved programmatically, or to be more specific: How to retrieve this using PowerShell.

In the following example we use the Get-ADForest cmdlet to Retrieve information about the current forest. In particular the property we are interested in is the ForestMode property:

1
Get-ADForest | Select-Object ForestMode

Alternatively the [adsi] type accelerator can be used, this has the advantage that it works on any computer that has PowerShell installed as it does not rely on having the Active Directory module installed, the following code will retrieve the Forest Functional level:

1
([adsi]"LDAP://CN=Partitions,$(([adsi](“LDAP://RootDSE”)).configurationNamingContext)").'msDS-Behavior-Version'

The problem with this is that the value of the Forest Functional Level is stored as an integer. Luckily for us this integer can be found on MSDN. So by combining the previous command with a switch statement we can get the expected output:

1
2
3
4
5
6
7
8
9
switch (([adsi]"LDAP://CN=Partitions,$(([adsi](“LDAP://RootDSE”)).configurationNamingContext)").'msDS-Behavior-Version') {
    0 {'DS_BEHAVIOR_WIN2000'}
    1 {'DS_BEHAVIOR_WIN2003_WITH_MIXED_DOMAINS'}
    2 {'DS_BEHAVIOR_WIN2003'}
    3 {'DS_BEHAVIOR_WIN2008'}
    4 {'DS_BEHAVIOR_WIN2008R2'}
    5 {'DS_BEHAVIOR_WIN2012'}
    6 {'DS_BEHAVIOR_WIN2012R2'}
}

For more information about the Forest Functional Level I have included a TechNet article that goes in depth about the implications of the various forest and domain functional levels. For more information about the msDS-Behavior-Version attribute I have included the link to the MSDN entry.

Forest Functional Level
Understanding Active Directory Domain Services (AD DS) Functional Levels
msDS-Behavior-Version: Forest Functional Level
Share

Active Directory Friday: Find empty Organizational Unit

As an Active Directory Administrator there are some moments, few and far in between where you might have a moment to yourself. In this article I will give you a short line of code so you can use this moment to find out if you have any empty Organizational Units in your domain. The definition of empty is an OU that does not contain any child objects. By this definition an OU containing another OU would not be considered empty. Because there is no LDAP filter for this we will take a look at how to do this using the Cmdlets and the [adsisearcher] type accelerator.

In the following example I will use Get-ADOrganizationalUnit in combination with an if-statement and Get-ADObject to gather empty OUs:

1
2
3
4
5
Get-ADOrganizationalUnit -Filter * | ForEach-Object {
	   if (-not (Get-ADObject -SearchBase $_ -SearchScope OneLevel -Filter * )) {
      		$_
   	}
}

So lets have a look at what this code does, the first portion is straight forward, gather all OUs using the Get-ADOrganizationalUnit cmdlet and pipe it into the ForEach-Object cmdlet. The if-statement is the interesting part here, I am using the Get-ADObject cmdlet to establish if this OU contains any child object, by setting the SearchBase to that OU and setting the SearchScope to OneLevel. Setting the SearchScope to OneLevel will only return direct child objects of the parent, the OU, without returning the OU itself. Because of this Get-ADObject will not return any objects if the OU is empty.

For more information about the SearchScope parameter and the possible arguments have a look at the following link: Specifying the Search Scope

Because you might not have the ActiveDirectory module loaded in your current PowerShell session it can be useful to know the [adsisearcher] alternative:

1
2
([adsisearcher]'(objectcategory=organizationalunit)').FindAll() | Where-Object {
   -not (-join $_.GetDirectoryEntry().psbase.children) }

This is a slightly different approach to illustrate a different method of gathering empty OUs, here we check the Children property part of the base object that is retrieved. The -join operator is used to ensure the -not does not evaluate the empty System.DirectoryServices.DirectoryEntries object as true.

Using the logic in this post it is also possible to filter for other specific objects contained in the OUs. For example display OUs that only have user objects, display OUs with both user and computer objects and so on.

For more information on this subject please refer to the following links:

Additional resources
Specifying the Search Scope
Get-ADObject
Get-ADOrganizationalUnit

Active Directory Friday: Use the ANR filter for LDAP Queries

ANR or Ambiguous Name Resolution is used to query for objects in Active Directory if the exact identity of an object is not known. A query containing Ambigious Name Resolution will query for all the attributes for example, Given Name, Sur Name, Display Name and samaccountname. For Windows Server 2008 and later versions this is the full list of ANR Attributes included in the search results:

For a full list of all the attributes that are queried please refer to the following TechNet article: ANR Attributes.

  • Display-Name
  • Given-Name
  • Physical-Delivery-Office-Name
  • Proxy-Addresses
  • RDN
  • SAM-Account-Name
  • Surname
  • Legacy-Exchange-DN
  • ms-DS-Additional-Sam-Account-Name
  • ms-DS-Phonetic-Company-Name
  • ms-DS-Phonetic-Department
  • ms-DS-Phonetic-Display-Name
  • ms-DS-Phonetic-First-Name
  • ms-DS-Phonetic-Last-Name

For a full list of all the attributes that are queried please refer to the following TechNet article: ANR Attributes.

An ANR query is useful in a number of scenarios, for example when relying on user input in your script. In this case querying against a samaccountname might fail if the spelling does not match the samaccountname. Similarly an export from a different department or database might be close to what is stored in Active Directory but not an exact match, again this is somewhere where an ANR query might be useful. Something that should be kept in mind is that this is a relatively expensive query and therefore should be avoided when it is not required. In this article we will discuss how to create an ANR filter and what happens exactly in such a query.

In the next example we will be using Get-ADUser cmdlet, which is part of the ActiveDirectory module, in combination with the LDAPFilter parameter in order to execute our query:

1
Get-ADUser -LDAPFilter '(anr=Jaap Brasser)'

This will query against all the attributes in the list as ‘Jaap Brasser*’ and two additionally queries: ‘GivenName=Jaap*’ and ‘SurName=Brasser*’ as well as ‘GivenName=Brasser*’ and ‘SurName=Jaap*’. As a result more than one result might be returned, as different attributes of a user account might overlap or are not unique to a single user account. This is the downside of this method of querying.

In the following example I will use the [adsisearcher] type accelerator to execute the same query:

1
([adsisearcher]'(anr=Jaap Brasser)').FindAll()

Alternatively the DirectorySearcher object can be manually created to execute a query:

$ADSearcher = New-Object DirectoryServices.DirectorySearcher -Property @{
 Filter = '(anr=Jaap Brasser)'
 PageSize = 100
}
$ADSearcher.FindAll()

For more information on this Ambiguous Name Resolution (ANR) have a look at the following resources:

Ambiguous Name Resolution
MSDN Ambiguous Name Resolution
ANR Attributes
KB Ambiguous Name Resolution for LDAP in Windows 2000
Share

Active Directory Friday: Find users with password never expires

Having password set to never expires might be something that is not allowed by your IT policy, or perhaps you would like to get some insight about how widespread this setting is in your domain. In order to find accounts the Search-ADAccount cmdlet can be used. In order to find all user accounts that do have the password never expires option enabled the following code can be used:

1
Search-ADAccount -UsersOnly -PasswordNeverExpires

Alternatively the Get-ADObject cmdlet can also be used in combination with an LDAP filter to filter out the user accounts and the password never expires option. To filter out user accounts we should filter the following: ‘(objectCategory=person)(objectClass=user)‘. To search for password never expires the following filter is used: ‘(userAccountControl:1.2.840.113556.1.4.803:=65536)‘. Combined that gives us the following code:

1
Get-ADObject -LDAPFilter "(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))"

It is of course also possible to do this using the DirectoryServices.DirectorySearcher. This time we use a slightly different LDAP filter, instead of filtering on ‘(objectCategory=person)(objectClass=user)‘ we filter on ‘(sAMAccountType=805306368)‘ which gives the same output but is a more efficient query. Also we set pagesize to 100 so we ensure that all results are displayed:

1
2
3
4
5
$ADSearcher = New-Object DirectoryServices.DirectorySearcher -Property @{
  Filter = '(&(sAMAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=65536))'
  PageSize = 100
}
$ADSearcher.FindAll()

And that is all that is required in order to find AD users with the password never expires option set, with or without the ActiveDirectory module.

Share

Active Directory Friday: Change a user’s password

It is one of the most common tasks Active Directory administrators face, changing a user’s password or unlocking their account. Today we will discuss how this can be done in Powershell using either the Active Directory module or [adsi] type accelerator for this purpose.

Setting or resetting a password is rather straight forward using the Active Directory cmdlets, simply use Get-ADUser to get the AD user object and pipe it into Set-ADAccountPassword:

1
Get-ADUser jaapbrasser | Set-ADAccountPassword -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "secretpassword01" -Force)

To unlock an account the Unlock-ADAccount cmdlet can be used:

1
Get-ADUser jaapbrasser | Unlock-ADAccount

To both unlock and change the password of a user using the ADSI type accelerator the following code can be used:

1
2
3
4
$User = [adsi]([adsisearcher]'samaccountname=jaapbrasser').findone().path
$User.SetPassword("secretpassword01")
$User.lockoutTime = 0
$User.SetInfo()
Share

Active Directory Friday: Create new OU

PowerShell can be used to create any number of objects in Active Directory. Today I will demonstrate how to create an organizational unit using both the ActiveDirectory module as well as the [adsi] alternative.

Creating an OU using the New-ADOrganizationalUnit is quite straight forward:

1
New-ADOrganizationalUnit -Name Departments -Path "ou=Resources,DC=jaapbrasser,DC=com'

Using the [adsi] accelerator to create an OU requires some additional steps. First the parent object has to be selected, in this example the Resources OU in the jaapbrasser.com domain. The next step is create a new object, an organizationalunit in this case, finally the changes are committed to Active Directory by using the SetInfo() method.

1
2
3
$TargetOU = [adsi]'LDAP://ou=Resources,DC=jaapbrasser,DC=com'
$NewOU = $TargetOU.Create('organizationalUnit','ou=Departments')
$NewOU.SetInfo()

That is all there is to it, creating an Organizational Unit in Active Directory is quite easy, with or without the ActiveDirectory module.

Share

Update to my TechNet scripts

I upload some of my scripts into the TechNet Script Gallery and there are some lively discussions going on in the questions and answers sections of the scripts. I received feedback and based on that I have improved and updated some of my scripts.

The first script I would like to highlight is the Get-RemoteProgram script that has been built as a replacement for the Win32_Product WMI class. The Win32_Product class is useful but there are some downsides to this, have a look at this article for more information. This prompted me to write an alternative function for this purpose. This function generates a list by querying the registry and returning the installed programs of a local or remote computer. In the latest version of this script. There was an issue with some scenarios when running this script on a 32 bit machine which have been resolved in the latest version.

.EXAMPLE 
Get-RemoteProgram -ComputerName Server01 -Property DisplayVersion,VersionMajor 
 
Description: 
Will gather the list of programs from Server01 and attempts to retrieve the displayversion and versionmajor subkeys from the registry for each installed program 
 
.EXAMPLE 
'server01','server02' | Get-RemoteProgram -Property Uninstallstring 
 
Description 
Will retrieve the installed programs on server01/02 that are passed on to the function through the pipeline and also retrieves the uninstall string for each program

The second script that received an update is the Get-ScheduledTask the status of the tasks was listed in integers instead of ‘Running’,’Disabled’ or ‘Ready’. This has been updated and there were also scenarios in which the script would list the same task twice which has also been patched.

.EXAMPLE 
.\Get-ScheduledTask.ps1 -Computername mycomputer1

Description 
----------- 
This command query mycomputer1 and display a formatted list of all scheduled tasks on  that computer

Another script that I have updated is the Get-OrphanHomeFolder, this script takes a folder path as input and checks if the the folder has a corresponding samaccountname in Active Directory and if that is the case if the account is enabled or disabled. A request came in to also display the enabled users in order to get a full overview. Because of this I added the DisplayAll parameter to the script.

.PARAMETER DisplayAll 
This switch parameters will force the script to also display enabled active directory  accounts, can be used in combination with -FolderSize parameter. 

.EXAMPLE    
.\Get-OrphanHomeFolder.ps1 -HomeFolderPath \\Server01\Home -FolderSize -DisplayAll 
 
Description: 
Will list all the folders in the \\Server01\Home path. For each of these folders it    will query AD using the foldername, regardless of the AD results folder size will be returned 

The last script I updated is the Compare-ADuserAddGroupGUI this script compares the group membership between two users. The goal of this script is to grant the destination user all the missing memberships when compared to the source user. Any missing memberships will be added by this script and and any additional memberships will be removed. The ComputerName parameter has been added to this script in order to specify which domain controller will be queried for the comparison.

.EXAMPLE 
.\Compare-ADuserAddGroupGUI.ps1 testuserabc123 testuserabc456

Description 
----------- 
This command will add&remove from groups testuserabc456 to match groups that testuserabc123 is a member of the user is prompted by user interface to confirm these changes.

.EXAMPLE 
 .\Compare-ADuserAddGroupGUI.ps1

Description 
----------- 
Will use GUI to prompt for confirmation 

For a complete list of all my script feel free to browse to my submissions in the TechNet Script Gallery.

TechNet Script Gallery
My entries in TechNet Script Gallery
Get-RemoteProgram Get-ScheduledTask
Get-OrphanHomeFolder Compare-ADuserAddGroupGUI
Share