PowerShell comparison of partial string confusion - powershell

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

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.

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 AD-Object DistinguishedName attributes

I am trying to check if an object returned by the Get-Adcomputer belongs to a certain group under the DistinguishedName property returned. I can't seem to find anything online and looking for some pointers. This attribute contains several different things and I am interested in the OU value in particular.
$computer = Get-AdComputer "$computerName"
$computer.DistinguishedName
This returns several attributes linked to OU and CN, but I am just interested if one of the OU attributes matches 'Server'.
here is something you could try if you're just looking for a true/false check
$computer = Get-AdComputer "$computerName"
$dn = $computer.DistinguishedName
$dn.Split(',') -match '^ou=.*server'
or you could use this to get the whole path
# split the distinguishedname into an array of CNs, OUs, etc.
$split = $dn.Split(',')
# setup match variable to signal that we found an object that matches what we are looking for.
$match = $false
# for each item in the array
($split | % {
# only check this if we have not yet found a match
# if this line is an OU whose name contains the word 'server'
if (!$match -and $_ -match '^ou=.*server') {
# set match to true because we found what we were looking for
$match = $true
}
# if we have found a match, output this and all the rest of the objects to the pipeline
if ($match) {
$_
}
# now we have an array starting at the OU we were looking for
# this join will put the pieces back together into a string so it looks right
}) -join ','
note: this will likely not work if any of your objects have a comma in the name.

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*'

Filtering multiple users with get-aduser

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"