Using PowerShell how can I search and filter an array of exported Active Directory users like I can with Get-ADUser? - powershell

I have a PowerShell script that compares the contents of a CSV file with Active Directory. The CSV file contains a list of demographic information of people already in AD. One of the columns is "emplid". The values in this field correspond to the values of the "employeeID" attribute of user objects in AD. So, I currently use this "emplid" property to cross reference AD and find the corresponding user accounts. To do this I use a line similar to this:
$UserAccounts = $ListOfEmloyeeIDs | ForEach-Object {Get-ADUser -Properties * -Filter {employeeID -Eq $_}}
I then use this to add those user accounts to a security group:
$UserAccounts.SamAccountName | ForEach-Object {Add-ADGroupMember -Identity SpecialSecurityGroup -Members $_}
The problem is with the first line. There are thousands of user accounts and the script can take hours to run. This has also led to complaints from the AD admins. What I would like to do is load all active AD users into a variable (which takes less than 2 minutes to run) using:
$ADPeopleActive = Get-ADUser -SearchBase "OU=People,DC=MyAD,DC=com" -Properties EmployeeID -Filter {Enabled -Eq $True}
Then I would like to do my cross reference against this array and build a list of SamAccountNames to feed to something like my second line to populate my security group.
My problem is I can't figure out a way to do this cross reference against an array that I've built the same way I can cross reference with AD using Get-ADuser. Can anyone help?

Something like
$UserAccounts = $ADPeopleActive| Where-Object { $ListOfEmloyeeIDs -contains $_.EmployeeID }
?

Related

Finding out if the same property occurs on multiple AD users

I'm pretty new on Powershell and this is by far the trickiest task I have gotten so far. I want to write a script that shows me if the same personal identity number occurs on multiple AD users.
I have managed to get a list of all AD users and their ID numbers using the Powershell Active Directory module and the following:
Get-ADUser -Filter * -SearchBase "OU=X,DC=X,DC=X,DC=X" -Properties PersonalIdentityNumber | Select-Object Name,PersonalIdentityNumber | Where-Object {$_.PersonalIdentityNumber} | Sort-Object -Property PersonalIdentityNumber
Although, I am not sure where to go from there. I suspect that I will have to use a for or foreach loop in some way, but I have tested a bit and not made any concluions. It will most likely be too heavy to compare every user against all other users, but I think that every user can be compared to the 20 users before or after, since matching ID numbers will probably be on users with the same name.
Any ideas on how to accomplish this?
Use the Group-Object cmdlet to group the users based on the value of the PersonalIdentityNumber property:
$usersWithPIN = Get-ADUser -Filter * -SearchBase "OU=X,DC=X,DC=X,DC=X" -Properties PersonalIdentityNumber | Select-Object Name,PersonalIdentityNumber | Where-Object {$_.PersonalIdentityNumber}
$usersWithSamePINGroups = $usersWithPIN |Group-Object PersonalIdentityNumber |Where-Object Count -gt 1
$usersWithSamePINGroups will now contain zero or more Group objects with a Count property (the number of users sharing a given PIN), and a Group property containing the user objects in question

Get samaccountname from display name - but with extras

First time poster but site has helped me so much in the past.
We are an MSP and regularly get requests from clients to pull various details off a list of users they send us. Unfortunately though their lists rarely (if ever) contain any unique identifiers for AD such as samAccountName or even e-mail.
So typically I only get their first and last names, job titles etc. and use a slight variation on the below to try and get the required samAccountNames to work in batch modify scripts.
Get Samaccountname from display name into .csv
The problem comes (and caused a big headache recently) when I try to put that output back into a table to line up with the displaynames. As if the script can't find the displayname it just moves onto the next one in the list and puts the samAccountName directly below the last one it found. making it out of line with the displayname column I've put it beside.
My question is is there something I can add to the below script that when an error occurs it simply inputs null or similar into the samAccountName output csv so I could spot that easily in an excel sheet.
Similarly some users have multiple accounts like an admin and non-admin account with the same display name but different samAccountName so it pulls both of them, which is less of a problem but also if there was any way to have the script let me know when that happens? That would be super useful for future.
Import-Module activedirectory
$displayname = #()
$names = get-content "c:\temp\users.csv"
foreach ($name in $names) {
$displaynamedetails = Get-ADUser -filter { DisplayName -eq $name } -server "domain.local"| Select name,samAccountName
$displayname += $displaynamedetails
}
$displayname | Export-Csv "C:\temp\Samaccountname.csv"
So the problem lies in that you rely on Get-ADUser to provide you with user objects and when it doesn't you have gaps in your output. You instead need to create an object for every name/line in your "csv" regardless of whether Get-ADUser finds anything.
Get-Content 'c:\temp\users.csv' |
ForEach-Object {
$name = $_
$adUser = Get-ADUser -Filter "DisplayName -eq '$name'" -Server 'domain.local'
# Create object for every user in users.csv even if Get-ADUser returns nothing
[PSCustomObject]#{
DisplayName = $name # this will be populated with name from the csv file
SamAccountName = $adUser.SamAccountName # this will be empty if $adUser is empty
}
} | Export-Csv 'C:\temp\Samaccountname.csv'

Windows PowerShell script for verifying several Active Directory groups exist

I need to run a PowerShell script to verify that a huge list of Active Directory groups exist based on an .xlsx file. I would also like to see the owners of the AD groups if possible.
I can run separate scripts, if needed. Please help.
Reading and writing to an excel file from PowerShell is its own adventure, but one that is heavily documented if you google it.
For the query you are asking, its important to know if you have the group's Display Name or the group's SamAccountName. If you have both, you could put an -OR in the filter.
Here is a quick example of how it could work based on a text list.
$list = 'Cross Functional Team
Security Group
Bobs Team' -split([Environment]::NewLine)
$SelectSplat = #{
Property = #{n='Name';E={$_}},
#{N='Found';e={[Bool](Get-adgroup -Filter {DisplayName -eq $_})}},
#{N='ManagedBy';e={Get-adgroup -Filter {DisplayName -eq $_} -Properties ManagedBy | %{(Get-ADUser -Identity $_.ManagedBy).Name}}}
}
$list | Select-Object #SelectSplat

How to move multiple users to multiple OUs importing users from CSV and filtering by department

New to powershell and scripting in general. Trying to improve automation in our onboarding process, we have to move multiple user accounts to multiple OUs every couple of months.
Import-Module ActiveDirectory
$dept1 = "OU=Path1,DC=SOMEWHERE,DC=OUTTHERE"
$dept2 = "OU=Path2,DC=SOMEWHERE,DC=OUTTHERE"
Import-Csv "C:\Scripts\Incoming.csv" | ForEach-Object {
$samAccountName = $_."samAccountName"
Get-ADUser -Identity $samAccountName -Filter {Department -eq "Dept1"} | Move-ADObject -TargetPath $dept1
Get-ADUser -Identity $samAccountName -Filter {Department -eq "Dept2"} | Move-ADObject -TargetPath $dept2
}
This actually moves ALL users with the department marked into the path I have set.. but I only want it to look at those users in the csv and then filter their departments from AD - not the CSV. I feel like I'm missing a step but I've searched everywhere and tried the get-help. I feel like I need to get the identity, then search/filter against something else but I'm not quite sure how. Would appreciate any help.
edit
Okay, if I run the following:
Get-ADUser -Filter {Department -eq "Dept1"} -Properties Department
It returns everyone that fits that property but how do I compare those to the $samAccountName and ONLY try to move those accounts or run the commands against the accounts on the list? When I ran the second half of the command it tried to move them all and failed.
Move-ADObject $samAccountName -Target $dept1
I feel dumb.
It's ok to feel dumb. You're not and everyone feels that way at times when trying to learn a new thing. You're also here asking for help, so you're ahead of the game compared to a lot of others.
#Lee_Daily's comment is correct that Get-ADUser doesn't support using both -Identity and -Filter in the same command. They're part of different parameter sets. You can tell from the syntax output of Get-Help Get-ADUser or the online docs. Both show 3 different sets of parameters and Identity and Filter are not in the same one. What's odd is that your original script should have thrown an error because you tried to use both in the same command. No need to worry about that now though.
Here's a typical way one might approach this task. First, you query the user's details including the department you want to make a decision on. Then, you write your condition and perform the appropriate action. Doing it this way means you're only querying AD once for each user in your CSV rather than twice like your original script which is good for script performance and load on your AD. The inside of your ForEach-Object loop might look something like this. Note the addition of -Properties department in Get-ADUser. We need to ask for it explicitly because department isn't returned in the default result set.
# all of this goes inside your existing ForEach-Object loop
$u = Get-ADUser -Identity $_.samAccountName -Properties department
if ($u.Department -eq 'Dept1') {
$u | Move-ADObject -TargetPath $dept1
} elseif ($u.Department -eq 'Dept2') {
$u | Move-ADObject -TargetPath $dept2
}
Now let's talk about some alternative ways you might approach this.
The first way sort of flips things around so you end up only calling Get-ADUser once total, but end up doing a lot more filtering/processing on the client side. It's not my favorite, but it sets things up to understand my preferred solution. In particular, the Get-ADUser call uses the -LDAPFilter parameter. LDAP filter syntax is a little strange if you've never seen it before and this particular example could use the more common -Filter syntax just as easily. But in the next example it would be much more difficult and learning LDAP filter syntax enables you to query AD from anything rather than just PowerShell.
# instead of immediately looping on the CSV we imported, save it to a variable
$incoming = Import-Csv "C:\Scripts\Incoming.csv"
# then we make a single AD query for all users in either Dept1 or Dept2
$users = Get-ADUser -LDAPFilter '(|(department=Dept1)(department=Dept2))' -Properties department
# now we filter the set of users from AD by department and whether they're in the CSV and do the moves as appropriate
$users | Where-Object { $_.department -eq 'Dept1' -and
$_.samAccountName -in $incoming.samAccountName } | Move-ADObject -TargetPath $dept1
$users | Where-Object { $_.department -eq 'Dept2' -and
$_.samAccountName -in $incoming.samAccountName } | Move-ADObject -TargetPath $dept2
The benefit of this method is the single AD round trip for users rather than one for each in the CSV. But there are a lot more local loops checking the samAccountNames in the results with the samAccountNames from the CSV which can get expensive cpu-wise if your CSV and/or AD is huge.
The final example tweaks the previous example by expanding our LDAP filter and making AD do more of the work. AD is ridiculously good at returning LDAP query results. It's been fine-tuned over decades to do exactly that. So we should take advantange of it whenever possible.
Essentially what we're going to do is create a giant 'OR' filter out of the samAccountNames from the CSV so that when we get our results, the only check we have to do is the check for department. The way I verbalize this query in my head is that we're asking AD to "Return all users where SamAccountName is A or B or C or D, etc and Department is Dept1 or Dept2. The general form of the filter will look like this (spaces included for readability).
(& <SAM FILTER> <DEPT FILTER> )
# Where <DEPT FILTER> is copied from the previous example and
# <SAM FILTER> is similar but for each entry in the CSV like this
(|(samAccountName=a)(samAccountName=b)(samAccountName=c)...)
So let's see it in action.
# save our CSV to a variable like before
$incoming = Import-Csv "C:\Scripts\Incoming.csv"
# build the SAM FILTER
$samInner = $incoming.samAccountName -join ')(samAccountName='
$samFilter = "(|(samAccountName=$samInner))"
# build the DEPT FILTER
$deptFilter = '(|(department=Dept1)(department=Dept2))'
# combine the two with an LDAP "AND"
$ldapFilter = "(&$($samFilter)$($deptFilter))"
# now make our single AD query using the final filter
$users = Get-ADUser -LDAPFilter $ldapFilter -Properties department
# and finally filter and move the users based on department
$users | Where-Object { $_.department -eq 'Dept1' } | Move-ADObject -TargetPath $dept1
$users | Where-Object { $_.department -eq 'Dept2' } | Move-ADObject -TargetPath $dept2
There are more efficient ways to build the LDAP filter string, but I wanted to keep things simple for readability. It's also a lot easier to read with better PowerShell syntax highlighting than StackOverflow's. But hopefully you get the gist.
The only limitation with using this method is when your incoming CSV file is huge. There's a maximum size that your LDAP filter can be. Though I'm not sure what it is and I've never personally reached it with roughly ~4000 users in the filter. But even if you have to split up your incoming CSV file into batches of a few thousand users, it's still likely to be more efficient than the other examples.

Powershell Script to search specific OU in AD and find disabled users that is member of a group

I'm trying to write a script to find disabled users that is member of one or more groups in a specific OU in AD. It will then remove all the groups for all the disabled users. I found this script which removes all groups from users in a csv file, but as i'm looking to run this as a scheduled task I prefer not to process users that already had their groups removed without having to move them to a different OU.
Import-Csv $csvFile | ForEach-Object {
# Disable the account
Disable-ADAccount -Identity $_.samAccountName
# Retrieve the user object and MemberOf property
$user = Get-ADUser -Identity $_.samAccountName -Properties MemberOf
# Remove all group memberships (will leave Domain Users as this is NOT in the MemberOf property returned by Get-ADUser)
foreach ($group in ($user | Select-Object -ExpandProperty MemberOf))
{
Remove-ADGroupMember -Identity $group -Members $user -Confirm:$false
}
}
Any idea on how to filter out the users with more then one group?
I'm using this script to export disabled users that has not logged on for 60 days:
Get-QADUser -searchRoot $OuDomain -searchScope OneLevel -InactiveFor 61 -NotLoggedOnFor 61 -disabled -sizelimit 0
Thx
You seem to have filter by ou part down which is good. You have some thoughts in the beginning of you post but the only actual question is how to filter out the users with more then one group. Not sure if that is a typo or not but I read that as checking the count of groups a user has. A more realistic interpretation of that is filter users that could have at least one of a list of groups. I'm going to cover both.
The Count
I'm sure this is not what you want but just want to cover the base. The following would also work in a Where-Object clause
If((get-aduser $user -Properties MemberOf).MemberOf.Count -gt 0){Process...}
Multiple Groups
I'm sure this was your intention. Locate users that could contain one of serveral groups. This is best handled with regex.
$groupsFilter = "citrix_GateKeeper","barracuda_spam_alerts"
$groupsFilter = "($($groupsFilter -join '|'))"
# $groupsFilter in this example is: (citrix_GateKeeper|barracuda_spam_alerts)
If(((Get-ADUser $user -Properties MemberOf).MemberOf) -match $groupsFilter){Process....}
Create a regex match string based on a string array of multiple groups. If $user is a member of either of those groups then true would be returned.
If nothing here is of any use to you then I would suggest making your question clearer. Hopefully this helps.