Modifying Powershell LDAPFilter to add enabled=true - powershell

I've built a filter to look for a number of AD fields and properties that works well until I try to add a section looking for 'enabled -eq $true.'
Here is the filter that works successfully:
$filter = "(&(msExchMailboxguid=*)"+"(facilityID=12345)"+"(|(jobCodeID=0001)"+"(jobCodeID=0002)"+"(jobCodeID=0003)(jobCodeID=0004)"+"(jobCodeID=0005)"+"(jobCodeID=0006)))"
Get-ADUser -SearchBase "dc=acme,dc=corp" -LDAPFilter $filter
This works, and produces the correct AD user objects (four total).
But if I try looking for enabled accounts only, like so:
$filter = "(&(msExchMailboxguid=*)"+"(facilityID=12345)"+"(enabled=$true)"+"(|(jobCodeID=0001)"+"(jobCodeID=0002)"+"(jobCodeID=0003)(jobCodeID=0004)"+"(jobCodeID=0005)"+"(jobCodeID=0006)))"
It either fails with "the search filter can not be recognized," or it returns nothing at all depending on whether there are 3 or 4 closed parentheses. I've tried a bunch of variations like (enabled=true), (enabled -eq true) but none of them work.

The issue is that you are using an LDAP filter which is different than a native PowerShell filter and so has a different syntax. Even though most LDAP fields match pretty closely to their normal names, the Enabled field is not stored as a "normal" property (e.g. boolean true/false). Instead, it is held in a part of a bitmasked property userAccountControl. That means you have to use the "intuitive" filter:
(!(userAccountControl:1.2.840.113556.1.4.803:=2))
To filter out only the enabled accounts.
So that makes your filter for your example to become:
$filter = "(&(msExchMailboxguid=*)"+"(facilityID=12345)"+"(!(userAccountControl:1.2.840.113556.1.4.803:=2))"+"(|(jobCodeID=0001)"+"(jobCodeID=0002)"+"(jobCodeID=0003)(jobCodeID=0004)"+"(jobCodeID=0005)"+"(jobCodeID=0006)))"

Related

ADSI Search for DistinguishedName of the primary group based on primarygroupid

Because we don't have the active directory module available on all our systems we're using ADSI instead. The following code retrieves a user object from AD by using the AdsiSearcher:
$ADUser = ([AdsiSearcher]"(samaccountname=$SamAccountName)").FindOne()
This results in finding the property primarygroupid which represents the domain primary group for user, usually number 513. When we have this number we would like to find the distinguishedName of the group. However, the code below does that just fine I was wondering if there is a better filter that can be used instead of filtering after the FindAll() method?
$searcher = [adsisearcher]'objectclass=group'
$searcher.PropertiesToLoad.Add('primarygrouptoken')
$searcher.PropertiesToLoad.Add('distinguishedName')
$searcher.FindAll() |
Where-Object { $_.Properties.primarygrouptoken -eq 513}
Something like this would be great but it's not possible:
([adsisearcher]”(&(objectCategory=group)(primaryGroupid=513))”).FindOne()
The primaryGroupToken is a constructed attribute, meaning that it's not actually materialized in the database, and can't be filtered using LDAP.
In order to build an equivalent filter we'll need to look at how it is constructed - and the primary group token in Active Directory is always the same as the group's RID part (the relative identifier) of the objectSid attribute.
So, if we want to search by it, we can simply filter by objectSid instead:
# Obtain domain SID
$dncDN = ([adsi]"LDAP://RootDSE").defaultNamingContext
$dnc = [adsi]"LDAP://$dncDN"
$domainSID = [System.Security.Principal.SecurityIdentifier]::new($dnc.objectSid.Value, 0)
# Set the group ID we're looking for
$RID = 513
# Search for group by objectSid value:
([adsisearcher]"(&(objectCategory=group)(objectSid=${domainSID}-${RID}))").FindOne()

Powershell: Checking for duplicate email in AD

Background:
I'm trying to make a script that will see if a new users email ($email) is the same as one already existing (which would cause an error). I have a very remedial understanding of objects so this is what I have so far (yes it is ugly):
$email = "smithj#company.com"
$mailcheck = Get-ADUser -filter * -Properties * | ForEach-Object {$_.mail}
$mailcheck | ForEach-Object {if ($email -eq $_.mail){"$email = $($_.mail) - Matching email"}else{"$email = $($_.mail) - No duplicate email"}}
Problem 1:
The script doesn't match emails. When I have a matching email in AD it doesn't recognize it.
Problem 2: When executing just the 2nd line, indexing doesn't work properly. While it looks like a consecutive list of emails, if a user doesn't have an email at all (blank) really it could be something like this:
smithj#company.com
johnsonj#company.com
robertsr#company.com
doej#company.com
So $mailcheck[0] returns smithj#company.com while $mailcheck[1] returns blank despite the list actually looking like this:
smithj#company.com
johnsonj#company.com
robertsr#company.com
doej#company.com
Conclusion: I really just need problem 1 solved but problem 2 peaked my curiosity. Thanks.
The way you are doing it above is really inefficient. -Properties * will return every property on the user, some properties are expensive in terms of processing power to return. Only use the properties you need. The properties returned by default without specifying that parameters do not need to be specified with -Properties, only additional nondefault properties. -Filter * will also match on literally any value for any field, effectively returning every ADUser, further increasing the resources required for your script to execute as you will now have to process every user to find any accounts matching that email.
Now that that's out of the way, here is a more efficient method to implement what you're asking:
# Set the email address to search for
$emailAddress = 'box#domain.tld'
# Get all users where the email address matches what is set above
# Force it as an array so you can treat it like one even if only
# one or zero users are returned
$adUsers = #( Get-ADUser -Filter "EmailAddress -eq '${emailAddress}'" )
# Make sure no accounts were returned
# If there are, throw an error with the number of users and who they are
if( $adUsers ) {
throw "Found $($adUsers.Count) users matching EmailAddress ${emailAddress}: $($adUsers.SamAccountName -join ', ')"
}
By using the filter to only match the specific email address, Powershell does not need to collect every single AD user in the system, or iterate over all of them to find a specific email address. This will take a long time to check, especially in larger environments, whereas filtering the returned objects based on email address (or on any other property) results in a faster operation and less data to sift through.
You can then check whether $adUsers contains anything (an array count of anything but 0 evaluates to $True, you could also use if( $adUsers.Count -gt 0 ) as the condition), and if so, throw an error with more information as I do above.
Update for comment question:
To answer your other question in the comment, "I didn't know what object to compare $email to", EmailAddress and Mail both look to be valid properties, but I don't know the difference between them. In my environment, both Mail and EmailAddress are populated with my email address, but have always used EmailAddress and haven't run into issues using that. Maybe one is deprecated and the other is new or something, but I'm not really sure.
There is also yet another property called proxyAddresses as well, which preliminary research shows that both EmailAddress and Mail are related to it, but I don't know much about it. It's not populated on my ADUser objects, so I can't poke around with it.

LDAP userAccountControl atttribute

In PowerShell I use the following LDAP query to retrieve the active directory properties of a host name:
$Filter = "(&(ObjectCategory=Computer)(ObjectClass=Computer)(CN=$ComputerName))"
if ($Found = ([ADSISEARCHER]$Filter).FindOne()) {
$Details = $Found.GetDirectoryEntry()
}
Once I have these properties I would like to check if the computer account is disabled. The following LDAP query is allowing me to do that:
$Filter = "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=2)(CN=$ComputerName))"
([ADSISEARCHER]$Filter).FindOne()
What I would like to know is, how can I avoid using the second LDAP query and use the variable $Details from the first LDAP query to obtain $True or $False if the computer account is disabled?
I found some extra information but I can't seem to put the pieces together.
Thank you for your help.
The information you're looking for is encoded in the userAccountControl of the directory entry object. However, the property contains an array with a numeric value, so you need to check if the "disabled" flag (numeric value 2) in the first array element is set:
$disabled = [bool]($Details.userAccountControl[0] -band 2)

LDAP filter syntax to compare two object's properties

I want to know if this is possible to compare two object's properties with the LDAPfilter ?
Something like (This is not working - return nothing every time): -LDAPfilter {sAMAccountName=userPrincipalName}
I tried to find a clue in this Microsoft's documentation about the LDAPfilter, but I found nothing.
Some explanations about my goal :
I want to get every user with the User logon name different than the User logon name (Pre Windows 2000).
I am using the command "Get-ADUser"
The two properties I want to compare are : "User logon name" (userPrincipalName) and "User logon name (Pre Windows 2000)" (sAMAccountName)
I don't know if this is possible with the LDAP filter, if not, is there an other way to do it ?
No, it's not possible with an LDAP filter. You can only compare an attribute with a value, not two attributes with each other. You need something like this for the latter:
Get-ADUser -Filter * -Properties * |
? { $_.SamAccountName -eq $_.UserPrincipalName }
Note that these two properties are practically guaranteed to be different, because the UPN normally includes the FQDN of the domain whereas the sAMAccountName does not. Your comparison will effectively look somewhat like this:
'user' -eq 'user#domain.example.com'
so you may want to do something like this instead:
Get-ADUser -Filter * -Properties * |
? { "$($_.SamAccountName)#$env:USERDNSDOMAIN" -eq $_.UserPrincipalName }

How to filter Win32_UserAccount results by OU

In PowerShell, I already know how to use DirectoryEntry and DirectorySearcher to get a list of users in a certain OU. The results of this method are mostly what I am looking for in AD, but it seems easier to get the same information by using a WMI query Win32_UserAccount. I like the properties of this class better and the SID is already in the correct string format (in the first method it needs to be converted from a hex byte array to string).
The problem I have with using Win32_UserAccount is that I cannot find a way to filter it by an OU. I can successfully filter by domain and name, and have tried several guesses with WQL, but can't seem to find any syntax for an OU filter. Most of my attempts result in "Invalid query." The following is an example of a query that works:
$user = gwmi Win32_UserAccount -filter "name='somebody' AND domain='mydomain'"
If there is no way to filter this by OU then I will go back to using the DirectoryEntry/DirectorySearcher.
Given that there are no LDAP related properties for the Win32_Account class I think you're out of luck unfortunately.
You could of course use this to get the SID in the format you want in addition to the directory searching to get the LDAP related data.
Are you familiar with the free AD cmdlets from Quest?
http://www.quest.com/powershell/activeroles-server.aspx
You can filter users based on OU and get the SID in various formats:
PS> Get-QADUser SizeLimit 0 -SearchRoot <OU_DistinguishedName>' | fl *sid*
objectSid : 0105000000000005150000006753F33372134F3FF673476FF4023001
Sid : S-1-5-21-54781788-1045369324-1866953526-501
(...)