close

Windows

Cloud

Microsoft 365

PowerShell

Active Directory

Security

Windows Server

Video

If you are a large enterprise, don't miss our IT cost-cutting webinar!

Home

Active Directory

PowerShell

Active Directory User Accounts with PowerShell, ADSI, and LDAP

Jeff Hicks

|


We have been exploring some alternatives to the Active Directory (AD) PowerShell module. Most of the time, this module should meet your needs. It is always good to have options so I have been demonstrating how to use the ADSI type accelerator with the LDAP moniker. As long as you know the distinguished name to an AD object, you can reference it in PowerShell. When we ended last time, we were beginning to look at AD user objects. Let’s pick up that idea with a single-user account.
 

 

[ADSI]$al = "LDAP://CN=Al Fredo,OU=Sales and Marketing,OU=Departments,OU=Employees,DC=globomantics,DC=local"

User LDAP Properties (Image Credit: Jeff Hicks)

User LDAP Properties (Image Credit: Jeff Hicks)


To display user properties, I have been using Select-Object to expand each property value. There is an alternative that takes a list of property names and then creates an ordered hashtable. This gets the value from the user object. The [ordered] directive keeps all of the properties in the same order. This is not required but I like the look.

$props = "Name","sAMAccountname","Displayname","Description","Title","UserPrincipalName","WhenCreated","WhenChanged"
$hash = [ordered]@{}
foreach ($item in $props) {
  $hash.add($item,$al.$item.Value)
}
[pscustomobject]$hash

Once the hashtable is complete, it can be treated as a PowerShell custom object.

Selected properties (Image Credit: Jeff Hicks)

Selected Properties (Image Credit: Jeff Hicks)


You could use this technique for most properties. Let’s say you want to change a property. In most cases, all you need to do is assign a new value to the object.

$al.description = "sales laptop user"

An alternative is to use the Put() method.

$al.put("description","sales laptop user")

You have modified the locally-cached instance of the user account. To commit the change, invoke the Setinfo() method.

$al.setInfo()

You will not see this method with Get-Member but it is there. You can refresh the local version of the object.

$al.refreshcache()

And once replication finishes, you can see the change in Active Directory Users and Computers.

Viewing the change (Image Credit: Jeff Hicks)

Viewing the Change (Image Credit: Jeff Hicks)


Some information, like whether the account is enabled or not, is tucked away. This particular tidbit is the UserAccountControl value.
User account control (Image Credit: Jeff Hicks)

User Account Control (Image Credit: Jeff Hicks)



In order to determine if the account is enabled, you need to perform a bitwise operation using a hex flag value.

New-Variable UF_ACCOUNTDISABLE 0x2 -option Constant
($al.userAccountControl.value -band $UF_ACCOUNTDISABLE) -as [boolean]

Bitwise operation (Image Credit: Jeff Hicks)

Bitwise Operation (Image Credit: Jeff Hicks)


This is to be expected. Let’s disable the account.

$al.userAccountControl.value = $al.userAccountControl.value -bor $UF_ACCOUNTDISABLE
$al.setInfo()
$al.RefreshCache()
($al.userAccountControl.value -band $UF_ACCOUNTDISABLE) -as [boolean]
Testing the new value (Image Credit: Jeff Hicks)

Testing the New Value (Image Credit: Jeff Hicks)

$al.userAccountControl.value = $al.userAccountControl.value -bxor $UF_ACCOUNTDISABLE
$al.setInfo()
$al.RefreshCache()

Another property that you might want to get is the password last set. This is stored as a COM object. This is also a large-integer value.

Converting large AD integer (Image Credit: Jeff Hicks)

Converting Large AD Integer (Image Credit: Jeff Hicks)


This is actually a datetime value, which we can convert like this:

$val = $al.ConvertLargeIntegerToInt64($al.pwdLastSet.value)
$last = [datetime]::FromFileTime($val)

Calculating Password Last Set Date (Image Credit: Jeff Hicks)

Calculating Password Last Set Date (Image Credit: Jeff Hicks)


It does not take much more effort to figure out the age of the password.
Calculating Password Age (Image Credit: Jeff Hicks)

Calculating Password Age (Image Credit: Jeff Hicks)


The last bit of the user account that needs coaxing is the account properties. An example of this is figuring out whether the user’s password ever expires.
Account Properties (Image Credit: Jeff Hicks)

Account Properties (Image Credit: Jeff Hicks)


This is also stored in the UserAccountControl property. We will perform a bitwise operation to determine if the password can expire.

New-Variable ADS_UF_DONT_EXPIRE_PASSWD 0x10000 -Option Constant
($al.userAccountControl.value -band $ADS_UF_DONT_EXPIRE_PASSWD) -as [boolean]

Testing User Account Flag (Image Credit: Jeff Hicks)

Testing User Account Flag (Image Credit: Jeff Hicks)


The “user cannot change password” is actually an access control rule that uses an extended right.

$guid="ab721a53-1e2f-11d0-9819-00aa0040529b"
$al.ObjectSecurity.GetAccessRules($true,$true,[security.principal.NTAccount]).where({$_.objecttype -eq $guid -AND $_.IdentityReference -match "SELF"})

Change Password Rule (Image Credit: Jeff Hicks)

Change Password Rule (Image Credit: Jeff Hicks)


If the AccessControlType is set to Deny, then the user cannot change their password. This is the case with Al Fredo.
Let’s put all of this together into a single function. This will create a custom object with a user’s LDAP properties.

Function Get-LDAPUser {
[cmdletbinding()]
Param(
[Parameter(Position = 0, Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[string]$ADSPath
)
Begin {
  #define some variables
  New-Variable UF_ACCOUNTDISABLE 0x2 -option Constant
  New-Variable ADS_UF_DONT_EXPIRE_PASSWD 0x10000 -Option Constant
  $guid="ab721a53-1e2f-11d0-9819-00aa0040529b"
  #define a set of properties to retrieve
  $props = "DistinguishedName","Name","sAMAccountname","Displayname","UserPrincipalName",
  "Parent","Description","Title","Department","WhenCreated","WhenChanged"
}
Process {
  Write-Verbose "Getting user properties for $ADSPath"
  [ADSI]$user = $ADSPath
  if ($user.ADSPath) {
    $hash = [ordered]@{}
    foreach ($item in $props) {
      if ($user.$item -is [System.Collections.CollectionBase]) {
        $hash.add($item,$user.$item.Value)
      }
      else {
        $hash.add($item,$user.$item)
      }
    }
   $hash.Add("IsDisabled",($user.userAccountControl.value -band $UF_ACCOUNTDISABLE) -as [boolean])
   $pwdLast = [datetime]::FromFileTime($user.ConvertLargeIntegerToInt64($user.pwdlastset.value))
   $pwdAge = (Get-Date) - $pwdLast
   $hash.Add("PasswordLastSet",$pwdLast)
   $hash.Add("PassswordAge",$pwdAge)
   $hash.Add("PasswordNeverExpires",($user.userAccountControl.value -band $ADS_UF_DONT_EXPIRE_PASSWD) -as [boolean])
   $rule = $user.ObjectSecurity.GetAccessRules($true,$true,[security.principal.NTAccount]).where({$_.objecttype -eq $guid -AND $_.IdentityReference -match "SELF"})
   if ($rule.accesscontroltype -eq 'Deny') {
     $CannotChange = $True
   }
   else {
     $CannotChange = $False
   }
     $hash.Add("UserCannotChangePwd",$CannotChange)
   #write result to the pipeline
   [pscustomobject]$hash
  }
  else {
     Write-Warning "Failed to find $distinguishedname"
  }
}
End {
 #not used
}
}

I wrote the function so that you can pipe a user object to it.

Getting LDAP user properties (Image Credit: Jeff Hicks)

Getting LDAP User Properties (Image Credit: Jeff Hicks)


Or you can get all users from a particular OU.

[ADSI]$OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
$ou.children.where({$_.schemaclassname -match 'user'}) | Get-LDAPUser | Out-GridView -Title IT

User Report (Image Credit: Jeff Hicks)

User Report (Image Credit: Jeff Hicks)



I hope you will give some of this a try in a test environment. Next time, we will look at creating user accounts with ADSI.

More in PowerShell

PowerShell

How To Set Environment Variables With PowerShell

Dec 5, 2022 | Sukesh Mudrakola

PowerShell

How to Use a PowerShell Foreach Loop

Oct 24, 2022 | Ivan Mirchev

PowerShell

How to Use a PowerShell Array

Oct 19, 2022 | Michael Reinders

PowerShell

Filtering with PowerShell Where-Object: Easy Examples

Oct 10, 2022 | Michael Reinders

PowerShell

What is PowerShell and How to Get Started With It?

Sep 7, 2022 | Mike Kanakos

PowerShell

Use a PowerShell Substring to Search Inside a String

Aug 11, 2022 | Jeff Hicks

Most popular on petri

Article saved!

Access saved content from your profile page. View Saved