Monthly Archive


PowerShell and Active Directory

Windows Compatibility Pack

As reported last month the Windows Compatibility Pack for .NET core is available. This adds back some of the functionality missing from .NET core. This functionality is ONLY of relevance on Windows machines.


A PowerShell module based on the Compatibility Pack is in the works – this will add a number of cmdlets including the WMI cmdlets back into PowerShell v6 on Windows. There’s no ETA on the module at this time.


There is a module on the PowerShell gallery that will add the .NET components of the Compatibility Pack into your PowerShell v6 session.

PS>  Find-Module -Name PSCoreWindowsCompat | ft -a

Version Name                Repository Description 
 ------- ----                ---------- ----------- PSCoreWindowsCompat PSGallery  Provides the Microsoft.Windows.Compatibility Pack to PowerShell Core.


If you want to inspect the module

PS>  Save-Module -Name PSCoreWindowsCompat -Repository PSGallery -Path C:\Source\ -Force


To install the module:

PS>  Install-Module -Name PSCoreWindowsCompat -Repository PSGallery -Verbose -Force 
 VERBOSE: Repository details, Name = 'PSGallery', Location = ''; IsTrusted = 'False'; IsRegistered = 'True'. 
 VERBOSE: Using the provider 'PowerShellGet' for searching packages. 
 VERBOSE: Using the specified source names : 'PSGallery'. 
 VERBOSE: Getting the provider object for the PackageManagement Provider 'NuGet'. 
 VERBOSE: The specified Location is '' and PackageManagementProvider is 'NuGet'. 
 VERBOSE: Searching repository ''PSCoreWindowsCompat'' for ''. 
 VERBOSE: Total package yield:'1' for the specified package 'PSCoreWindowsCompat'. 
 VERBOSE: Performing the operation "Install-Module" on target "Version '' of module 'PSCoreWindowsCompat'". 
 VERBOSE: The installation scope is specified to be 'AllUsers'. 
 VERBOSE: The specified module will be installed in 'C:\Program Files\PowerShell\Modules'. 
 VERBOSE: The specified Location is 'NuGet' and PackageManagementProvider is 'NuGet'. 
 VERBOSE: Downloading module 'PSCoreWindowsCompat' with version '' from the repository ''. 
 VERBOSE: Searching repository ''PSCoreWindowsCompat'' for ''. 
 VERBOSE: InstallPackage' - name='PSCoreWindowsCompat', version='',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061' 
 VERBOSE: DownloadPackage' - name='PSCoreWindowsCompat', version='',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061\PSCoreWindowsCompat\PSCoreWindowsCompat.nupkg', uri='' 
 VERBOSE: Downloading ''. 
 VERBOSE: Completed downloading ''. 
 VERBOSE: Completed downloading 'PSCoreWindowsCompat'. 
 VERBOSE: InstallPackageLocal' - name='PSCoreWindowsCompat', version='',destination='C:\Users\Richard.MANTICORE\AppData\Local\Temp\51711061' 
 VERBOSE: Catalog file '' is not found in the contents of the module 'PSCoreWindowsCompat' being installed. 
VERBOSE: Module 'PSCoreWindowsCompat' was installed successfully to path 'C:\Program Files\PowerShell\Modules\PSCoreWindowsCompat\'.


Notice the installation path is OUTSIDE of the current version of PowerShell v6 so should remain available through any upgrades.

PS>  Import-Module -Name PSCoreWindowsCompat


Now you’ve got it how do you use it? The module DOESN’T have any functions – it just loads the .NET namespaces.


Its back to PowerShell v1 days – everything is a script instead of a cmdlet. For instance the Compatibility pack contains the System.DirectoryServices so you can script against AD.


Let’s say you want to see all the users in the domain:

$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() 
 $root = $dom.GetDirectoryEntry()

$search = [System.DirectoryServices.DirectorySearcher]$root 
 $search.Filter = "(&(objectclass=user)(objectcategory=user))" 
 $search.SizeLimit = 3000 
 $result = $search.FindAll()

foreach ($user in $result){ 


Use System.DirectoryServices.ActiveDirectory.Domain to get the current domain. Create a System.DirectoryServices.DirectorySearcher object and set the filter to users. Find all of the users and display their distinguishedname


Its a lot more complicated than using the cmdlet:

PS>  Get-ADUser -Filter * | select DistinguishedName


but it gets the job done.


If you need to administer AD and you need the cross platform capabilities of PowerShell maybe you should use the  PSCoreWindowsCompat module (with aplogies to A-Team fans everywhere)

Get an AD user’s manager

Interesting question on the forum about finding the manager for a given user in AD – assuming the Manager field is populated of course. If you’ve not worked with the AD cmdlets this is a good introduction to some of their quirks. This is how you get an AD user’s manager.


You need the manager property on the AD user account but that’s not one of the default properties that’s returned so you need to use the –Propertie parameter to ensure you get your data. Assuming you have a csv file with userids that looks like this

id userid 
-- ------ 
1  DonBrown 
2  DonFox 
3  JamesBrown 
4  JamesBlack


You can use this code

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $user.Manager 


This returns the distinguished name of the manager

id userid     Manager                                           
-- ------     -------                                           
1  DonBrown   CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
2  DonFox     CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
3  JamesBrown CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org 
4  JamesBlack CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org


if you prefer to have their name then you need an extra step

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $manager = Get-ADUser -Identity $user.Manager 
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name 


Which gives output like this

id userid     Manager    
-- ------     -------    
1  DonBrown   HARRIS Fred 
2  DonFox     HARRIS Fred 
3  JamesBrown HARRIS Fred 
4  JamesBlack HARRIS Fred


If you need to output the data to a csv file then just add Export-Csv

Import-Csv -Path .\names.csv | 
foreach { 
   $user = Get-ADUser -Identity $_.userid -Properties Manager 
   $manager = Get-ADUser -Identity $user.Manager 
   $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name 
 } | Export-Csv -Path names2.csv –NoTypeInformation

Comparing AD group membership on EmployeeId

Back in this post - I showed how to compare the membership of two groups using Compare-Object. The comparison was based on the samAccountName. A question raised the issue of comparing AD group membership on EmployeeId

In the case in particular users have multiple accounts BUT the EmployeeId is correct on all and will therefore show matching users. Assuming the EmployeeId is correct on all accounts it still leaves a problem.

When you run Get-ADGroupMember you get a very limited number of properties returned:

PS>  Get-ADGroupMember -Identity Testgroup1

distinguishedName : CN=JONES James,OU=UserAccounts,DC=Manticore,DC=org 
 name              : JONES James 
objectClass       : user 
objectGUID        : 027cb406-a3b0-4f45-9bbd-db47ccfb9212 
 SamAccountName    : JamesJones 
 SID               : S-1-5-21-759617655-3516038109-1479587680-1225

First thing I needed to do was set up some users with an EmployeeId

$ei = 1 
 Get-ADUser -Filter {Name -like "*Jones*"} -Properties EmployeeId | 
foreach { 
  $id =  23945 + $ei 
  $psitem | Set-ADUser -EmployeeID $id

  $ei = $ei + (Get-Random -Minimum 3 -Maximum 12) 

Get a set of users – including the EmployeeId – and forech of them set the id. The id is randomly generated based on a starting value and increment.

Now that the users have an Employeeid you can use that for comparison purposes

$group1 = Get-ADGroupMember -Identity Testgroup1 | 
foreach { 
  Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
  select -ExpandProperty EmployeeId 

$group2 = Get-ADGroupMember -Identity Testgroup2 | 
foreach { 
  Get-ADUser -Identity $psitem.distinguishedName -Properties EmployeeId | 
  select -ExpandProperty EmployeeId 

 Compare-Object -ReferenceObject $group1 -DifferenceObject $group2 -IncludeEqual |             
 where SideIndicator -eq "==" |            
foreach {            
  $id = ($_.InputObject)        
  Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId            

Get the membership of the first group and for each member use Get-ADUser to return the EmployeeId. Repeat for the second group.

Use  Compare-Object to compare the two sets of group members – you’re looking for matches indicated by “==”

Foreach match get the AD user account filtering on the EmployeeID.

The PROBLEM with this approach is that you’ll get all user accounts returned that have the particular EmployeeId.   You can replace the line

Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId


Get-ADUser -Filter {EmployeeId -eq $id} -Properties EmployeeId, MemberOf | where {$_.MemberOf -like "*Testgroup1*" -AND $_.MemberOf -like  "*Testgroup2*"}

Which should resolve the problem

Get-ADUser filtering

Saw a question on the forums that revolved around Get-ADUser filtering.

Initial code was like this

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like $_.Name}

which on the face of it seems reasonable. However, you get errors like this

Get-ADUser : Property: 'Name' not found in object of type:
At line:3 char:3
+   Get-ADUser -Filter {Name -like $_.Name}
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ADUser], ArgumentException
    + FullyQualifiedErrorId : ActiveDirectoryCmdlet:System.ArgumentException,Microsoft

Change –like to –eq and you’ll still get the error.

This won’t work either:

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like $($_.Name)}

You get messages about path errors.

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter {Name -like "$($_.Name)"}

will run but won’t return any data.

This will run and return the correct data.

Import-Csv .\users.txt |
foreach {
  $name = $_.Name
  Get-ADUser -Filter {Name -like $name}

Alternatively, you can use quotes so that the filter is a string

Import-Csv .\users.txt |
foreach {
  Get-ADUser -Filter "Name -like '$($_.Name)'"

Another option is to use the LDAP filter syntax

Import-Csv .\users.txt |
foreach {
  $name = $_.Name
  Get-ADUser -LDAPFilter "(Name=$name)"

Import-Csv .\users.txt |
foreach {
  Get-ADUser -LDAPFilter "(Name=$($_.Name))"

The help file about_activedirectory_filter is well worth reading. It doesn’t seem to be installed with the AD module on Windows Server 2016. You can read it on line at

You’ll also see links to

about_ActiveDirectory  - overview of module



Get-ADUser filtering isn’t the most obvious of topics to get your head round but these examples should help you make your filters work. If you’re building a complex filter build it up a step at a time so you can test each stage.

Are your domain controllers real?

A question on the forum asked about discovering if domain controllers are physical or virtual machines.

This will do the job

foreach ($domain in (Get-ADForest).domains) {
 Get-ADDomainController -filter * -server $domain |
 sort hostname  |
 foreach {
 Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $psitem.Hostname |
 select PSComputerName, Manufacturer, Model


Get the domains in your forest and then for each domain get the domain controllers. Get-ADDomainController outputs an object with a property of hostname – but you need a computername for Get-CimInstance. So, use a foreach-object and use the Hostname property as shown (you could create a property ComputerName on the pipeline object but its more work) and get the results. A virtual machine will show under the Model. You can sort or whatever once you have the results.

Name mismatch

Ever wondered why you can’t do this:

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
Get-CimInstance -ClassName Win32_OperatingSystem

The –ComputerName parameter on get-CimInstance accepts pipeline input BUT its by property name.

PS> Get-Help Get-CimInstance -Parameter ComputerName

-ComputerName [<String[]>]
Specifies computer on which you want to run the CIM operation. You can specify a fully qualified domain name
(FQDN), a NetBIOS name, or an IP address.

If you do not specify this parameter, the cmdlet performs the operation on the local computer using Component
Object Model (COM).

If you specify this parameter, the cmdlet creates a temporary session to the specified computer using the WsMan

If multiple operations are being performed on the same computer, using a CIM session gives better performance.

Required?                    false
Position?                    named
Default value                none
Accept pipeline input?       True (ByPropertyName)
Accept wildcard characters?  false

If you look at the output of Get-ADComputer it has a Name property.

PS>  Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org'

DistinguishedName : CN=W16PWA01,OU=Servers,DC=Manticore,DC=org
DNSHostName       :
Enabled           : True
Name              : W16PWA01
ObjectClass       : computer
ObjectGUID        : 8d137004-1ced-4ff1-bcf4-f0671652fc8c
SamAccountName    : W16PWA01$
SID               : S-1-5-21-759617655-3516038109-1479587680-1322
UserPrincipalName :

So you have a Name mismatch between the property and the parameter.

There are a number of ways to deal with this.

First use foreach

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
foreach {
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $psitem.Name |
select CSName, Caption

Use $psitem.Name (or $_.Name) as the input to –ComputerName. Simple coding and works very nicely.

If you have a lot of computers you may want to use a foreach loop instead

$computers = Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' | select -ExpandProperty Name
foreach ($computer in $computers) {
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer|
select CSName, Caption

Create an array of computer names and iterate through them.

Second use select

Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' |
select @{N='ComputerName';E={$_.Name}} |
Get-CimInstance -ClassName Win32_OperatingSystem |
select CSName, Caption

In this case use select-object to create a property with the name ComputerName (case DOESN’T matter) and pipe that into Get-CimInstance.

This option is a bit more advanced as you have understand how select-object works and how to create extra properties on the object you’re passing down the pipeline. It looks cooler and should get you a few extra “ace powerShell coder” points.

The third option takes advantage of the fact that _Computername accepts an array of computer names

Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName (Get-ADComputer -Filter * -SearchBase 'OU=Servers,DC=Manticore,DC=org' | select -ExpandProperty Name) |
select CSName, Caption

You run Get-ADComputer and use select –Expand to only return the VALUE of the computer name (a string). This gives you an array of computer names. Because its in () its treated as an input object to the parameter.

Very clever and gets you maximum points.

Modifying AD users in bulk

Modifying AD users in bulk involves either setting one or more properties to the same value for a set of users or reading in the values you need from a data source of some kind.

We prepared some test data in the last post so lets see how we use it.

$users = Import-Csv -Path .\users.csv
foreach ($user in $users){
Get-ADUser -Identity $user.Id |
Set-ADUser -Division $user.Division -EmployeeNumber $user.EmployeeNumber

The simplest way is to read in the data and store as a collection of objects. Use foreach to iterate through the set of user information. Get-ADUser gets the appropriate AD account which is piped to Set-ADUser. Set-ADUser is a great cmdlet because it has parameters for most of the user properties.

In this case though we know that some of the users don’t have employee numbers. This means a bit more work. Two approaches are possible – use splatting and the parameters used above or use the –Replace option

Lets look at splatting first

$users = Import-Csv -Path .\users.csv
foreach ($user in $users){
$params = @{
Division = $user.Division
EmployeeNumber = 0

if ($user.EmployeeNumber) {
$params.EmployeeNumber = $user.EmployeeNumber
else {

Get-ADUser -Identity $user.Id |
Set-ADUser @params

As before read the user information into the $users variable. Iterate over the users with foreach. Create a hashtable for the parameters and their values. Division is always present so that can be set directly. Employeenumber should be tested and if  present the place holder value should be overwritten with the correct value otherwise Employeenumber is removed from the hashtable.

The user account is found and Set-ADUser sets the correct values. Notice how the hashtable is specified to the cmdlet.

Splatting is a great way to dynamically set the parameters you’re using on a particular cmdlet.

Set-ADUser has an alternative – the –Replace parameter.

$users = Import-Csv -Path .\users.csv
foreach ($user in $users){
$params = @{
division = $user.Division
employeeNumber = 0

if ($user.EmployeeNumber) {
$params.EmployeeNumber = $user.EmployeeNumber
else {

Get-ADUser -Identity $user.Id |
Set-ADUser -Replace $params

This is very similar to the splatting example but instead of splatting the hashtable you use it as the value input to the Replace parameter. If you wrote  out the command it would look like this:

Set-ADUser –Replace @{division = ‘Division B’; employeeNumber  = 100}

With –Replace you’re using the LDAP names of the properties rather than the GUI or PowerShell name – there are differences for instance surname is sn in LDAP.

Modifying AD users in bulk is straightforward with PowerShell and its relatively easy to deal with missing values if you adopt one of the above ideas. Splatting is probably the easiest in this case.

Test data for bulk AD changes

I’ve had a number of questions about changing AD user data in bulk. If you need to do that you need some test data. The specific questions were around setting the Division property and the EmployeeNumber at the same time – but some accounts didn’t have an employee number.

First you need to generate some test data

$count =  1

Get-ADUser -Filter * -SearchBase 'OU=UserAccounts,DC=Manticore,DC=org' |
foreach {

$props = @{
Id = $psitem.SamAccountName
Division = ''
EmployeeNumber = $count
switch ($count % 3){
0 {$props.Division = 'Division A'}
1 {$props.Division = 'Division B'}
2 {$props.Division = 'Division C'}

if (-not ($count % 15)){$props.Remove('EmployeeNumber')}

New-Object -TypeName PSObject -Property $props

$count ++
} |
Export-Csv -Path users.csv –NoTypeInformation

You need a counter variable. Use Get-ADUser to get you test users and pipe to foreach. You can then create a hashtable for the properties – Id = samaccountname, Division is an empty string and EmployeeNumber is set equal to count.

A switch statement is used to modify the division. Modulo 3 on the $count variable can only give values of 0, 1 or 2.

Remove the EmployeeNumber for every 15th account

Create an object. Increment the counter.

Export the objects to a csv file.  You should end up with something like this:

Division   EmployeeNumber Id
--------   -------------- --
Division B 1              DonJones
Division C 2              DonSmith
Division A 3              DonBrown
Division B 4              DonBlack
Division C 5              DonWhite
Division A 6              DonGreen
Division B 7              DonWood
Division C 8              DonBell
Division A 9              DonHarris
Division B 10             DonFox
Division C 11             JamesJones
Division A 12             JamesSmith
Division B 13             JamesBrown
Division C 14             JamesBlack
Division A                JamesWhite
Division B 16             JamesGreen
Division C 17             JamesWood


Next time I’ll show you how to deal with the missing employee numbers when you modify the AD accounts.

Get-ADUser doesn’t display all properties

Microsoft’s Active Directory cmdlets have some issues. One of the ones that catches everyone when they start using them is that Get-ADUser doesn’t display all properties.

A default call to Get-ADUser displays a subset of the available properties of the user object:

DistinguishedName : CN=FOX Fred,OU=UserAccounts,DC=Manticore,DC=org
Enabled           : True
GivenName         :
Name              : FOX Fred
ObjectClass       : user
ObjectGUID        : db5a3975-980d-4749-b9c0-48aff9217b2a
SamAccountName    : foxfred
SID               : S-1-5-21-759617655-3516038109-1479587680-1314
Surname           :
UserPrincipalName :


Even if the properties are empty – such as Givenname and Surname – the property name is displayed. So, how do you get the properties that aren’t part of the default list?

There’s the brute force approach:

PS> Get-ADUser -Identity foxfred -Properties *
AccountExpirationDate                :
accountExpires                       : 9223372036854775807
AccountLockoutTime                   :
AccountNotDelegated                  : False
AllowReversiblePasswordEncryption    : False
AuthenticationPolicy                 : {}
AuthenticationPolicySilo             : {}
BadLogonCount                        : 0
badPasswordTime                      : 0
badPwdCount                          : 0
CannotChangePassword                 : False
CanonicalName                        : Fred
Certificates                         : {}
City                                 :
CN                                   : FOX Fred
codePage                             : 0
Company                              :
CompoundIdentitySupported            : {}
Country                              :
countryCode                          : 0
Created                              : 17/11/2016 14:07:13
createTimeStamp                      : 17/11/2016 14:07:13
Deleted                              :
Department                           :
Description                          :
DisplayName                          :
DistinguishedName                    : CN=FOX Fred,OU=UserAccounts,DC=Manticore,DC=org
Division                             :
DoesNotRequirePreAuth                : False
dSCorePropagationData                : {01/01/1601 00:00:00}
EmailAddress                         :
EmployeeID                           :
EmployeeNumber                       :
Enabled                              : True
Fax                                  :
GivenName                            :
HomeDirectory                        :
HomedirRequired                      : False
HomeDrive                            :
HomePage                             :
HomePhone                            :
Initials                             :
instanceType                         : 4
isDeleted                            :
KerberosEncryptionType               : {}
LastBadPasswordAttempt               :
LastKnownParent                      :
lastLogoff                           : 0
lastLogon                            : 0
LastLogonDate                        :
LockedOut                            : False
logonCount                           : 0
LogonWorkstations                    :
Manager                              :
MemberOf                             : {}
MNSLogonAccount                      : False
MobilePhone                          :
Modified                             : 18/11/2016 11:03:02
modifyTimeStamp                      : 18/11/2016 11:03:02
msDS-User-Account-Control-Computed   : 8388608
Name                                 : FOX Fred
nTSecurityDescriptor                 : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory                       : CN=Person,CN=Schema,CN=Configuration,DC=Manticore,DC=org
ObjectClass                          : user
ObjectGUID                           : db5a3975-980d-4749-b9c0-48aff9217b2a
objectSid                            : S-1-5-21-759617655-3516038109-1479587680-1314
Office                               :
OfficePhone                          :
Organization                         :
OtherName                            :
PasswordExpired                      : True
PasswordLastSet                      : 17/11/2016 14:07:13
PasswordNeverExpires                 : False
PasswordNotRequired                  : False
POBox                                :
PostalCode                           :
PrimaryGroup                         : CN=Domain Users,CN=Users,DC=Manticore,DC=org
primaryGroupID                       : 513
PrincipalsAllowedToDelegateToAccount : {}
ProfilePath                          :
ProtectedFromAccidentalDeletion      : False
pwdLastSet                           : 131238652330182673
SamAccountName                       : foxfred
sAMAccountType                       : 805306368
ScriptPath                           :
sDRightsEffective                    : 15
ServicePrincipalNames                : {}
SID                                  : S-1-5-21-759617655-3516038109-1479587680-1314
SIDHistory                           : {}
SmartcardLogonRequired               : False
State                                :
StreetAddress                        :
Surname                              :
Title                                :
TrustedForDelegation                 : False
TrustedToAuthForDelegation           : False
UseDESKeyOnly                        : False
userAccountControl                   : 512
userCertificate                      : {}
UserPrincipalName                    :
uSNChanged                           : 78123
uSNCreated                           : 62259
whenChanged                          : 18/11/2016 11:03:02
whenCreated                          : 17/11/2016 14:07:13


Using –properties * returns ALL of the properties of a user. That’s OK if you’re looking at one, or a few users, but becomes a very expensive operation if you’re looking at thousands of user objects.


A more elegant approach is to specify the properties you want:

PS> Get-ADUser -Identity foxfred -Properties EmailAddress, LockedOut, ProtectedFromAccidentalDeletion, whenCreated
DistinguishedName               : CN=FOX Fred,OU=UserAccounts,DC=Manticore,DC=org
EmailAddress                    :
Enabled                         : True
GivenName                       :
LockedOut                       : False
Name                            : FOX Fred
ObjectClass                     : user
ObjectGUID                      : db5a3975-980d-4749-b9c0-48aff9217b2a
ProtectedFromAccidentalDeletion : False
SamAccountName                  : foxfred
SID                             : S-1-5-21-759617655-3516038109-1479587680-1314
Surname                         :
UserPrincipalName               :
whenCreated                     : 17/11/2016 14:07:13


You get the properties you specified and the default properties.


So, while Get-ADUser doesn’t display all properties you can overcome this by using the –properties parameter with a * for all properties or a list of the properties you want in addition to the defaults.

Active Directory Schema Versions

With the release of Windows Server 2016 its time to update my schema versions script


$sch = [System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema()
$de = $sch.GetDirectoryEntry()
switch ($de.ObjectVersion) {
13{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2000"; break}
30{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2003"; break}
31{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2003 R2"; break}
44{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2008"; break}
47{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2008 R2"; break}
56{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2012"; break}
69{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2012 R2"; break}
87{"{0,25} " -f "Schema Version $($de.ObjectVersion) = Windows 2016"; break}
default{"{0,25} {1,2} " -f "Unknown Schema Version", $($de.ObjectVersion); break}


The script uses the GetCurrentSchema static method on System.DirectoryServices.ActiveDirectory.ActiveDirectorySchema. Derives a directory entry and uses the ObjectVersion to determine the corresponding Windows Server version.