-eq and -ne give me the same results - powershell

Why do the following two command, with the only difference the -eq and -ne operator give me my list of DCs?
Get-ADComputer -Filter {(operatingsystem -like "*server*") -AND
(PrimaryGroup -eq "CN=Domain Controllers,CN=Users,DC=domain,DC=com") }
-Property Name,PrimaryGroup
I'd expect this one to have everything, but domain controllers.
Get-ADComputer -Filter {(operatingsystem -like "*server*") -AND
(PrimaryGroup -ne "CN=Domain Controllers,CN=Users,DC=domain,DC=com") }
-Property Name,PrimaryGroup
If I run the equivalent against PrimaryGroupID instead of PrimaryGroup, it works as expected.

I actually get an error when trying both commands. Did a little digging and the filter was causing the problem. Had a quick look in ADSIEdit at a server object. It doesn't appear to have a attribute called "PrimaryGroup".
This was in a 2008 R2 AD running in 2008 R2 forest and domain functional levels.
As an aside, if you want a list of DC in a domain get-ADDomainController will do the job.
regards
Arcass

If you want to put one statement on separate lines, you need to put the backtick (`) at the end of the line to tell PowerShell that the statement continues on the next line. However, even then, you cannot split the filter on two different lines. So it should look something like this:
Get-ADComputer -Filter {(operatingsystem -like "*server*") -AND (PrimaryGroup -eq "CN=Domain Controllers,CN=Users,DC=example,DC=com") } `
-Property Name,PrimaryGroup
As you've found, AD doesn't actually have an attribute called PrimaryGroup. That is a property that PowerShell exposes to you, which interprets the value in the primaryGroupId attribute for you.
The primaryGroupId attribute of any object has the Relative Identifier (RID) of the group. The RID is the last section of number in the SID, but the group also stores this value in its primaryGroupToken atrribute. So you can get this value like this:
$primaryGroupToken = (Get-ADGroup "Domain Controllers" -Properties primaryGroupToken).primaryGroupToken
PowerShell has to convert what you pass into the -Filter parameter into a proper LDAP query, so when you use PrimaryGroup in the filter, PowerShell is doing that for you.
However, doing that lookup is not really necessary in this case, because the Domain Controllers group is a built-in group and always has an RID of 516. So you can do what you're trying to do like this:
Get-ADComputer -Filter "operatingsystem -like '*server*' -AND PrimaryGroupId -eq 516" `
-Property Name,PrimaryGroup

Related

How to query the Active Directory using a list of users in a text file for a specific attribute with PowerShell

I'm somewhat basic to Powershell and use one-liner commands only to keep it short and basic.
I would like to do the following: I have a list of users in a text file in the form of UserPrincipalName. I'd like to query this list of users if their accounts are still active/enabled or not. To do so, I'm trying to run the following command, which just reveals nothing in the end (blank output):
gc .\users.txt | foreach {get-aduser -server "corp.xxx.com"
-f 'name -like "$_"' -properties *}| select displayname,enabled
As mentioned, the output is blank with no errors or whatsoever.
I read that aduser doesn't work with pipelines, but I need to find a solution.
Kindly request your support :)
Thanks
Your use of single quotes in your filter is not allowing the expansion of the variable. Double-quotes should be wrapping the filter expression so as to allow the interpolation of the automatic variable $_:
Get-ADUser -Filter "name -like '$_'" ...
Single-quoted strings:
A string enclosed in single quotation marks is a verbatim string. The string is passed to the command exactly as you type it. No substitution is performed.
Also note, you mention in your question that the file has the user's UserPrincipalName attribute, yet you're querying the Name attribute, if that's the case, the filter should be:
Get-ADUser -Filter "UserPrincipalName -eq '$_'" ...
Note the use of -eq instead of -like, for exact matches you should always use this operator, see about_ActiveDirectory_Filter for usage details and examples of each operator.
If you're only interested in DisplayName and Enabled for your output, there is no reason in querying all the user's attributes, -Properties * should be just -Properties DisplayName since Enabled is already part of the default attributes returned by Get-ADUser.
Finally, the -Identity parameter can be bound from pipeline, and this parameter accepts a UserPrincipalName as argument, hence ForEach-Object is not needed in this case:
Get-Content .\users.txt |
Get-ADUser -server "corp.xxx.com" -Properties DisplayName |
Select-Object DisplayName, Enabled

Filtering Get-ADGroups with question mark wildcard doesn't seem to work

I'm trying to get a list of AD groups that have a name that starts with "Users-####-" (# is a number 0-9).
I've tried using Get-ADGroup -Filter {name -like "Users-[0-9][0-9][0-9][0-9]-*"} and Get-ADGroup -Filter {name -like "Users-????-*"}, but got no results.
I can of course use Get-ADGroup -Filter {name -like "Users-*"}, but this will also include all the groups that have something else than four characters after Users-.
I then decided to try using Where-Object and the this code returned the expected groups
Get-ADGroup -Filter * | Where-Object {$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"}
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
Anybody have an idea what I'm doing wrong or is this just a bug in how ADGroup filtering works?
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
That's a reasonable assumption, but the -Filter parameter exposed by some cmdlets in the ActiveDirectory module is a deceptive construct - it's designed to look like PowerShell's native operator syntax, but "underneath the hood" the cmdlet translates the filter expression to a valid LDAP query filter:
name -like "Users-*"
# is translated to
(name=Users-*)
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
# is translated to
(Name=Users-[0-9][0-9][0-9][0-9]-*)
Since LDAP doesn't recognize the wildcard range construct [0-9], it ends up querying the directory store for objects where the name literally starts with Users-[0-9][0-9][0-9][0-9]- - same goes for ?.
Since * is the only wildcard accepted by LDAP, the closest you can get is:
Get-ADGroup -Filter {name -like "Users-*-*"}
And then filter the results further on the client with Where-Object (in which case we're back to PowerShell performing the comparison and we can use all the wildcards again):
Get-ADGroup -Filter {name -like "Users-*-*"} | Where-Object Name -like 'Users-[0-9][0-9][0-9][0-9]-*'
As stated in about_ActiveDirectory_Filter:
Note: PowerShell wildcards, other than "*", such as "?" are not
supported by the -Filter parameter syntax.
In this case, you can combine -LDAPFilter with Where-Object to keep your query compatible and efficient:
Get-ADGroup -LDAPFilter "(name=Users-*-*)" | Where-Object {
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
}
You can use -Filter in this case as pre-filter, so at least you will get only groups with names starting with Users-.
Then in a further Where-Object clause you can specify further and in this case I would use regex -match there like:
Get-ADGroup -Filter "Name -like 'Users-*'" | Where-Object { $_.Name -match '^Users-\d{4}-.*' }
P.S. -Filter should be a string, not a scriptblock
The filters in the Powershell Active Directory module have odd behaviors.
Filter or Where Clause
There are two ways to restrict the output of an AD cmdlet like
Get-ADUser. First, you can use the -LDAPFilter or -Filter parameters
to filter the output. Second, you can pipe the results to the
Where-Object cmdlet. Where possible, the first method is more
efficient for two reasons.
Filtering is done on the domain controller instead of the local
client. The domain controller is more likely to be a server class
computer optimized for queries. Filtering results in a smaller
resultset sent over the network from the domain controller to the
client. In contrast, the Where-Object cmdlet only filters on the local
client after the resultset has been sent from the remote computer. For
example, you could retrieve all users with a department that starts
with "IT" using the Where-Object cmdlet as follows:
Get-ADUser -Filter * -Properties department | Where-Object {$_.department -Like "it*"} | Select sAMAccountName, department The
resultset from the Get-ADUser statement includes all users in the
domain. A more efficient method to get the same results would use a
filter, similar to below:
Get-ADUser -Filter {department -Like "it*"} -Properties department | Select sAMAccountName, department Now only the users needed are
included in the resultset from Get-ADUser. In a test domain with 2,150
users (7 of which have "IT" departments) the first command above took
4 times as long as the second (average of 10 trials each with 16
minutes between trials). The difference could be substantial in a
domain with ten's of thousands of users.
Also, note that the statements above use the -Properties parameter to
specify only the properties needed. The default properties exposed by
the cmdlet are always included, like sAMAccountName in this case. If
you request all properties, with -Properties *, the resultset will
include many properties for each user. The resultset will be much
smaller if you only specify the extended properties needed, like
department in this case. Repeating the last command above in the test
domain with 2,150 users, but requesting all properties (with
-Properties *) required 75% more time on average to complete. The default and extended properties exposed by the Get-ADUser cmdlet are
documented in Active Directory: Get-ADUser Default and Extended
Properties.
PowerShell Filter Syntax
The PowerShell Active Directory module cmdlets support an extended
form of the PowerShell Expression Language. PowerShell documentation
indicates that PowerShell syntax filters should be enclosed in braces.
But there are many examples where single quotes or double quotes are
used instead. As you might expect, this affects how the filter is
interpreted.
Using String Attributes The following table shows some example
PowerShell syntax filters using string properties, like Department.
Some filters result in error, others do not raise an error but never
produce results. The variable $Dept is defined as previously.
Filter Result
-Filter {department -eq "IT Department"} Works
-Filter {department -eq $Dept} Works
-Filter {department -eq "$Dept"} No Results
-Filter {department -eq '$Dept'} No Results
-Filter "department -eq $Dept" Error
-Filter 'department -eq $Dept' Works
-Filter {department -eq "it*"} No Results
-Filter {department -Like "it*"} Works
-Filter "department -Like ""it*""" Works
-Filter "department -Like 'it*'" Works
-Filter 'department -Like "it*"' Works
-Filter 'department -Like ''it*''' Works
-Filter {department -ge "IT"} Works
Some of these results may not be expected.
For example, you might expect enclosing a variable in a quoted string
to work. The best policy might be to always enclose PowerShell syntax
filters in braces, and to refrain from quoting variables.
The last example using the "-ge" operator is only useful in rare
situations. The filter will result in any departments that are
lexicographically greater than or equal to "IT". For example, it will
return "Test Department", because "T" is greater than "I".

Fax number update script, how to reverse?

I have created a script that would allow for the addition of the values "1-" to the beginning of our current AD fax numbers. I am running this in a test environment and the script runs exactly as I want it. I am looking for a failback and I can't seem to get that script to remove the "1-" it instead removes the 1 from the beginning and the final number from the end of the fax so the output looks like this: "-(###)-###-###" instead of looking like the correct number format "(###)-###-####"
Get-ADUser -Filter {facsimileTelephoneNumber -like "*"} -Properties facsimileTelephoneNumber| foreach {Set-ADUser -Identity $_ –replace #{facsimileTelephoneNumber="1-$($_.facsimileTelephoneNumber)"}}
Get-ADUser -Filter {facsimileTelephoneNumber -like "*"} -Properties facsimileTelephoneNumber| foreach {Set-ADUser -Identity $_ –replace #{facsimileTelephoneNumber="$(($_.facsimileTelephoneNumber).Substring(1,($_.facsimileTelephoneNumber.length) -2 ))"}}
Following my answer to your previous question where the 1- is added to the fax number, this is how you can reverse that.
I see in your question you are trying to do it all as one-liners, thereby skipping all possibility to check your code step-by-step. Using the first oneliner in your question also prepends 1- to simply every faxnumber found, regardless if it is needed or not.
Especially when just starting PowerShell, writing things out is a good thing.
Having said that, here's the code to remove the leading 1- from faxnumbers
# Remove leading '1-' from ADUsers faxnumbers
Import-Module ActiveDirectory
# get all users in the specified OU that have a fax number starting with '1-'
Get-ADUser -LdapFilter '(facsimileTelephoneNumber=1-*)' -SearchBase 'OU=UserAccounts,DC=YourDomain,DC=com' -Properties 'Fax' | ForEach-Object {
# using Substring() to remove the first two characters. See: https://ss64.com/ps/substring.html
$newFax = ($_.Fax).Substring(2)
Write-Host "Setting Faxnumber to '$newFax' for user $($_.Name)"
# remove the '-WhatIf' if you are sure the number may be changed
$_ | Set-ADUser -Fax $newFax -WhatIf
}
Hope this helps

How to pass a variable to -Filter

I have come across a very strange situation in PS.
In a script I have, there is a cmdlet (Get-Mailbox) which pulls back a few mailboxes and stores them in $mailboxes.
I then loop through this as follows to find a matching AD account.
foreach ($user in $mailboxes) {
Get-ADUser -Filter {UserPrincipalName -eq $user.UserPrincipalName}
}
When I run this it errors saying that it can't find the property UserPrincipalName on $user.
I have debugged the script and tested it thoroughly. At the point where it errors if I type $user.UserPrincipalName it outputs a list of UPNs and their date type is string so the property is there and has data.
I came to the conclusion that for some reason -Filter can't see the $user variable - as if it is isolated inside the {} brackets which I have heard can be the case with functions. However if I modify the code like so it works.
foreach ($user in $mailboxes) {
$name = $user.UserPrincipalName
Get-ADUser -Filter {UserPrincipalName -eq $name}
}
Although this fixes my problem I'd like to learn why the first example doesn't work. Can anyone explain?
Something worth noting is the get-mailbox actually connects to Exchange Online first and returns a data type of:
Deserialized.Microsoft.Exchange.Data.Directory.Management.Mailbox
but when the Get-ADUser command errors it says the the object is of type PSCustomobject. I think this maybe part of the problem.
Get-ADUser -Filter "userprincipalname -eq '$($user.userprincipalname)'"
I don't know why, but there's some more discussion here about which syntaxes do and don't work with Get-ADUser, and how the scriptblock syntax you are using works with a full user object but not with a PSCustomObject, here:
http://www.powershellish.com/blog/2015-11-17-ad-filter

How to get all the users with Admincount=1 that are not in built-in privileged groups?

I'm trying to get all members that have the Admincount=1 attribute, and are not part of any of the built-in privileged groups. I'm just not sure how to do the part with the built-in privileged groups.
Here is what I've tried:
Connect-QADService -Service *****.org | Out-Null
$Domains="*****.org","*****.org","*****.org","*****.org","*****.org"
foreach ($Domain in $Domains)
$Users=get-qaduser -sizelimit 0 -searchroot $Domain/ -LDAPFilter “(admincount=1)” | where-object {($_.AccountIsDisabled -eq $False) -and ($_.AllMemberOf -ne "*,CN=Builtin,*")}
Don't have the Quest cmdlets but should be easy to assume that AllMemberOf is an array. Couple points about using -eq or -ne in this case is that they dont use wildcards and you are comparing a string to an array. Point being that this is not the way you would make that comparison. Using the ActiveDirectory cmdlets, if that is an option for you, you could do this.
Get-ADuser -LDAPFilter "(admincount=1)" -Properties memberof | Where-Object{(($_.Memberof -join "") -notmatch "cn=builtin") -and $_.Enabled}
Since it looks like you are excluding users if they are in a builtin group we just join all the groups into one big string and test for a match.
Also I would heed Mjolinor advice. Never steered me wrong yet.
I noticed that AllMemberOf is supposed to be good for getting membership from other domains since Memberof does not have this information. I can't test this but updating your code would give you something like this:
$Users = Get-QADUser -sizelimit 0 -searchroot $Domain/ -LDAPFilter “(admincount=1)” |
where-object {(!($_.AccountIsDisabled) -and (($_.AllMemberOf -join "") -notmatch "CN=Builtin")}
If you just change them all to 0, within an hour the AdminSDHolder process will change all of them that are a member of a protected group back to 1.
AdminSDHolder