Filtering multiple users with get-aduser - powershell

I'm new to powershell and have to fetch users from AD based on a list with names. Is there any way to filter from AD using something similar to an in-statement in SQL? (select * from users where name in ('Joe','Bill)?
As for now I fetch users in a foreach loop and add them to an arraylist, but I don't know if this is good practice:
function GetUsers()
{
$dummydata = #('Bill','Joe','Sam')
$users = New-Object System.Collections.ArrayList($null)
foreach($user in $dummydata)
{
$aduser = get-aduser -f {GivenName -eq $user} -Properties * | select *
$users.add($aduser) | Out-Null
}
Return ,$users
}

You'd probably want to put this into a function:
$dummydata = #('Bill','Joe','Sam')
$filter =
[scriptblock]::create(($dummydata| foreach {"(GivenName -eq '$_')"}) -join ' -or ')
Get-ADUser -f $filter

mjolinor's answer is elegant and works, but the use of script blocks is problematic for two reasons:
It is unnecessary, because the script block will invariably be converted back to a string when it is passed to Get-ADUser -Filter.
More importantly, it perpetuates the widespread misconception that Get-ADUser -Filter accepts PowerShell script blocks that support PowerShell syntax, which is not true; it is a misconception that leads to frustration sooner or later; in short: construct your -Filter arguments as strings to begin with, and know that these filter strings, while resembling PowerShell syntax, use AD-provider-specific syntax, which is not only much more limited, but behaves subtly differently even with operators of the same name as in PowerShell - see this answer for the full story.
Therefore, use string manipulation to construct your filter:
Get-AdUser -Filter ('Bill', 'Joe', 'Sam' -replace
'^.*', 'GivenName -eq "$&"' -join ' -or ')
For information on the regex-based -replace operator, see this answer.
The -Filter argument evaluates to the following string literal, which is what the AD provider ultimately sees:
GivenName -eq "Bill" -or GivenName -eq "Joe" -or GivenName -eq "Sam"

Related

Powershell - How to remove trailing spaces from a list of groups a user is in

I've copied/created a script to get all the members of a group that a user is part of, including nested groups. However, my output isn't quite the way I want it.
It goes in one of two ways. Either it outputs as one big string, which looks nice, but has trailing spaces on each line so I cannot simply copy and paste it into AD. Or if I change the Out-String to use -stream, it comes out as a garbled mess, but may allow me to trim the spaces.
I currently have the output going into a TextBox in a simple GUI.
Function Get-ADUserNestedGroups {
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
#Get the AD object, and get group membership.
$ADObject = Get-ADObject -Filter "DistinguishedName -eq '$DistinguishedName'" -Properties memberOf, DistinguishedName;
#If object exists.
If($ADObject)
{
#Enummurate through each of the groups.
Foreach($GroupDistinguishedName in $ADObject.memberOf)
{
#Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject -Filter "DistinguishedName -eq '$GroupDistinguishedName'" -Properties memberOf, DistinguishedName;
#Check if the group is already in the array.
If(($Groups | Where-Object {$_.DistinguishedName -eq $GroupDistinguishedName}).Count -eq 0)
{
#Add group to array.
$Groups += $CurrentGroup;
#Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups;
}
}
}
#Return groups.
Return $Groups;
}
Function Display-UserGroups {
#Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName;
$ResultsTextBox.Text = $Groups | Select-Object Name| Sort-Object name | Out-String
The output with the first way looks like:
Group Name1(Eight Spaces Here)
Group Name2(Eight Spaces Here)
The output with the second way looks like:
Group Name1GroupName2GroupName3
Thanks for your help!
You need to trim your output, which can easily be done with String.Trim method. However, it can only be applied against strings. $Groups will be an array of ADObject types. You will need to return the Name values of those objects and apply the Trim() method to the values.
($Groups | Select -Expand Name | Sort).Trim() -join "`r`n"
You can use $Groups += $CurrentGroup.trimEnd() to add the value with the trailing spaces trimmed.
A few other methods you might want to research
.trim() # Trim leading and trailing
.trimEnd() # Trim trailing
.trimStart() # Trim leading
The padding is being caused by PowerShell's formatting system. Select-Object is returning single property (Name) objects. PowerShell outputs that as a table, which may have some padding. Out-String is keeping that padding which is why it was reflecting in your final output...
The name property of the group is already a string. There's no need to use Out-String if you unroll the Name property from your $Groups variable/collection.
With some other adjustments for readability & testing. Below will emit a single string with each group on a new line. That should be paste-able into AD, by which I think you meant Active Directory User & Computers.
Function Get-ADUserNestedGroups
{
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
# Get the AD object, and get group membership.
$ADObject = Get-ADObject $DistinguishedName -Properties memberOf, DistinguishedName
# If object exists.
If( $ADObject )
{
# Enummurate through each of the groups.
Foreach( $GroupDistinguishedName in $ADObject.memberOf )
{
# Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject $GroupDistinguishedName -Properties memberOf, DistinguishedName
# Check if the group is already in the array.
If( $Groups.DistinguishedName -notcontains $GroupDistinguishedName )
{
# Add group to array.
$Groups += $CurrentGroup
# Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups
}
}
}
# Return groups.
Return $Groups
}
$userSAM = 'UserName'
# Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName
($Groups.Name | Sort-Object) -join [System.Environment]::NewLine
Among the secondary changes you'll see I removed the -Filter parameter in a few places. The DistinguishedName can be used as the -Identity parameter so no need to filter.
Also, where you were checking if the $Groups collection already had the current group I used the -notContains operator instead. That should be faster then repeatedly iterating the collection with |Where{...} The code is also shorter and more readable.
Note: if we reverse the operands we could use -notin which may look
nicer still
An aside; You should avoid appending arrays with +=. Doing so can causes performance problems because it creates a new array any copies the contents over. The best way to deal with this is to allow PowerShell to accumulate the results for you. If you must do the append directly, look into using Array Lists. There's a lot information on this, just google it.

How do I reference an object's property in a Powershell Active Directory cmdlet?

Related to this fantastic answer about how to properly construct a -Filter parameter argument, I'm working on a script that makes heavy use of the Active Directory cmdlets in PowerShell, and am having difficulty constructing filter arguments containing the property of an object variable.
The script basically manages distribution groups by reading in spreadsheet and processing each row as appropriate - creating Active Directory contacts if they don't exist, adding or removing them to the appropriate group and so on.
The spreadsheet is stored in an object array, with each row being treated as an object, with the columns defining the object properties.
For example, the below code takes each row of the imported spreadsheet and attempts to find a matching contact object in Active Directory.
Foreach ($contact in $NOClist)
{
$contactobject = Get-ADObject -filter ("name -eq `"$contact.name`" -and Objectclass -eq `"contact`"") -SearchBase "$NOCContactPath" -Server $ADServer;
#... do some more stuff.
}
The problem is that $contact.name is being evaluated literally, so it ends up searching for a contact in Active Directory with a name property that's literally $contact.name. I've tried the other variations in the previously referenced answer, (""$contact.name"", '$contact.name' and "' + $contact.name + '"), but they all either evaluate to the literal $contact.name or throw a syntax error on the . character.
The hacky workaround I've come up with is to assign the object property to a variable and use that instead, such as the below, but that just feels terrible.
Foreach ($contact in $NOClist)
{
$contactname = $contact.name;
$contactobject = Get-ADObject -filter ("name -eq `"$contactname`" -and Objectclass -eq `"contact`"") -SearchBase "$NOCContactPath" -Server $ADServer;
#... do some more stuff.
}
Is there a way to reference an object property inside the filter argument, or is this workaround of assigning it to a variable and then using the variable really the best approach?
PowerShell does only simple variable expansion in strings, no complex stuff like index or dot-property access. There are several ways to deal with this limitation, e.g.
concatenation:
-Filter ("name -eq '" + $contact.name + "' -and Objectclass -eq 'contact'")
subexpressions:
-Filter "name -eq '$($contact.name)' -and Objectclass -eq 'contact'"
the format operator:
-Filter ("name -eq '{0}' -and Objectclass -eq 'contact'" -f $contact.name)
Note that for the first and third approach you need to put the operation in a grouping expression (i.e. in parentheses), so that the result of that operation is passed to the parameter.
First, are you sure that $contact.name is valid within that that loop? For debug purposes, throw a write-host in there to make sure it's what you think it should be.
But the major thing is that you're using single-quotes around the variable, which blocks variable expansion. It looks like you're trying to escape the double-quotes, not sure if that's possible but if it is, that's not the right way to do it. You should be shooting for this from the link:
Get-ADUser -Filter ('sAMAccountName -eq "' + $SamAc + '"')
Example for me:
PS V:> $contactname = "finn2617"
PS V:> Get-ADuser -filter ('samaccountname -eq "' + $contactname + '"' ) | select name, samaccountname
name samaccountname
---- --------------
Finnigan, Matthew FINN2617
So, for you:
$contactobject = Get-ADObject -filter ('name -eq `"' + $contact.name + " -and Objectclass -eq "contact" ' ) -SearchBase "$NOCContactPath" -Server $ADServer;

Powershell comparison with attributes

I'm beginner in powershell and I need your help.
I need to compare the department attribute from the AD containing some text amd replacing by another value.
But it doesn't work. Do I made a mistake below? Cheers
//Find the user and save the user in the variable
$member = get-Aduser -f {GivenName -eq 'Jack'}
//check if the Departement field match with "Dep20 "
if($member.department -eq "Dep20")
{
//Set "Dep21" in department field
$member.Department = 'Dep21';
set-AdUser -f {GivenName -eq $member.givenName} -departement $member.Department;
}
Some issues with your initial script
First
Get-AdUser won't give you the property Department by default.
You could have confirmed this by actually looking at the output of your Get-AdUser statement. You do need to add it to the list of properties explicitely.
get-Aduser -f {GivenName -eq 'Jack'} -Properties Department
Also, you did make a mistake in the Set-AdUser cmdlet. The parameter name you have written, at the time of my answer, is -departement. Instead, you need to set -department.
Finally, Get-AdUser could return multiple users (or none).
Therefore, you need to account for that by checking how many $member were returned or to do a foreach to process none (if 0) or all of them the same.
At least, that part is subjective to what you need but here would be my approach.
$member = get-Aduser -Filter 'GivenName -like "Jack*"' -Properties Department
$member | foreach {
if ($member.Department -eq 'Dep20')
{
$_.Department = 'Dep21'
set-AdUser $_ -Department $_.Department;
}
}
Edit:
I modified my answer to switch the Filter parameter from a scriptblock (as your question) for a string filter as per mklement0 comment.
Because the Filter parameter is actually a string, giving it a script block will create problems on multiple occasions and you are better restrict yourself to the string type for this parameter.
See this for a more detailed explanation on the matter.

PowerShell comparison of partial string confusion

I have a set of partial job titles in an array that I'm trying to compare to someone's full title in Active Directory (AD). -like and -match, as well as all the others, aren't working.
[string[]]$Titles =#('genetic','medic','nurs','optome','perfusion','pharm','phys')
($titles -like $user.Title) - nope
($user.title -contains $titles) - nope
($Titles.Contains($user.title)) - nope
I need a user title, "Physician", to match up with "phys". Why is this not working?
To do what you (seem to) want, iterate ("loop") over each title in $Titles and compare the $User.Title property to each individual partial title, and then see if any of them returned $true:
foreach($User in Get-ADUser -Filter * -Properties Title){
$TitleFound = #($Titles |ForEach-Object {$User.Title -like "$_*"} ) -contains $true
if($TitleFound){
Do-Stuff
}
}
That being said, you may want to use the partial strings to build an LDAP Filter string, that way the Domain Controller can take care of the filtering instead of returning all the users.
I LDAP search filter syntax, your query would look like this:
(|(title=genetic*)(title=medic*)(title=nurs*)(title=optome*)(title=perfusion*)(title=pharm*)(title=phys*))
You could generate that from your string array, with ForEach-Object:
# The | prefix means "or", as in "if any of these statements are true", return the object
$FilterTemplate = '(|{0})'
# This will be the comparison statements inside the filter
$FilterCriteria = $Titles |Foreach-Object { '(title={0}*)' -f $_ }
# Now put it all together
$LDAPFilter = $FilterTemplate -f -join($FilterCriteria)
# And retrieve the users:
$MedicalStaff = Get-ADUser -LDAPFilter $LDAPFilter -Properties Title

Powershell - Get-aduser return no value using a $var

i'm using this command to query AD for usernames::
get-aduser -filter 'SamAccountName -like "trembto*"
The AD return me some result that comply with that search.
when I try to apply this line with a $var inside, I get no result:
$userloop = "trembto"
get-aduser -filter 'SamAccountName -like "$($userloop)*"'
I should get the same result but it always returning me nothing, no error message
I tried may alternative for the var part but in vain.
Thank for helping me
Variable expansion will not happen when single quotes are used to create a string. You must use double quotes to create a string for variable expansion to occur. In your case, you need to use double quotes to create the filter string, and use single quotes around the expanded variable.
Change to this:
$userloop = "trembto"
get-aduser -filter "SamAccountName -like '$($userloop)*'"
You can see this behavior by inspecting the string you use for your filter parameter.
Test:
$userLoop = "trembto"
$filter = 'SamAccountName -like "$($userLoop)*"'
Output of $filter:
SamAccountName -like "$($userLoop)*"
Changed to:
$userLoop = "trembto"
$filter = "SamAccountName -like '$($userLoop)*'"
Outputs:
SamAccountName -like 'trembto*'