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
Related
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.
We have users with minor but annoying differences in naming standards that are loosely followed making scripting a pain. The company has been around a while and depending on who was working in IT when the employee was hired the account could follow any naming convention if followed at all.
In one forest we have accounts that start with a-, the manager attribute of that account is the DN of the account owners other/main account without many other common attributes populated. I then need to look up the managers account and grab their SamAccountName. Then I need to add s- to the SamAccountName and do search for that to see if it exists.
Then I need to write the original SamAccountName, the second SamAccountName and the s-SamAccountName and something like a check box if its a valid account name all to a CSV.
without rewriting the script and passing everything to/from a var and then processing that I dont see a way to do it. This script looks up roughly 800 users and processes that three time, so it already takes a while to run without slowing it down with a bunch of var transfers.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select -ExpandProperty manager | Get-ADUser -Server XXX.int:3268 |
Select -ExpandProperty samaccountname
$test
You can do something like this to get you started.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select-Object SamAccountName,
#{n='Manager';e={Get-ADUser $_.Manager -Server XXX.int:3268 | Select -Expand SamAccountName}},
#{n='s-Manager';e={Get-ADUser "s-$($_.Manager)" -Server XXX.int:3268 | Select -Expand SamAccountName}}
$test
I am not clear if you need to add s- to the manager's or the original account's SamAccountName. The script above adds s- to the manager's SamAccountName.
In scripts where multiple AD lookups are required, sometimes it is best to do one larger lookup and then filter that result for what you need.
Explanation:
The main technique of the script makes use of delayed-script binding with the Select-Object command. Each user object found by the original Get-ADUser command is piped into Select-Object and is accessible by the pipeline variable $_. The hash table select can be updated fairly easily to meet your needs.
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 }
?
I am looking for a solution to add users to groups in active directory after I have created their users accounts. Currently my powershell script has a few things lacking but I am going to tackle them one at a time.
In this cycle I trying to learn the best way to add groups to newly created user accounts. Is it best to copy from a template account (which I am having problems doing as I keep getting a blank account... Or should I manage all new user information directly in the script. Which is best practice?
In my research I see how this can be done with adsi.
I was hoping not to use this method unless I have to. what I was hoping for was something like this. with Get-ADUser, Set-ADUser, Set-ADObject, Get-ADObject, or similar commands.
$user=get-aduser 'abc user'
$userModify=Set-aduser $user
$groups=get-aduser $tmplateUser | select -ExpandProperty memberof
# or groups could come from an array, I have not decided which is best.
foreach ($Group In $groups)
{
$usermodify.memberof.add -identity $Group -member $user
}
Does anyone have any suggestions or examples?
if you can use the 'ActiveDirectory' module then you can try:
Import-Module ActiveDirectory
This will show you the cmdlets available for managing groupmembership.
Get-Command -Verb add -Noun *group*
This will show you examples of the cmdlet.
Help cmdletname -examples
There are many ways to create users, most use information stored in a csv file as input to say a cmdlet like New-ADUser.
The foreach construct will depend upon which cmdlet you choose to use.
$groups = Get-ADUser $tmplateUser -Properties memberof |
Select-Object -ExpandProperty memberof
foreach ($group in $groups)
{
Add-ADGroupMember -Identity $group -Members $newuser
}
I would like to write a PS script that exports a .csv for all users that are specified in a separate .txt file. So, for instance, I could create a text file that has
Timmy Turner
Silly Sally
Then, when the script is ran, it searches AD for those two users and exports a CSV with their first name, last name, and email address.
I originally got hung up a bit on how the "Get-ADUser" filter worked, but I produced something semi-usable. However, what I've come up with just asks who you are searching for and then uses that. I think it would be much easier to just have it reference a pre-made text file, especially when searching for a large number of users. Or, there may be an even easier way to do this that I am not thinking of. Here is what I currently have:
$SamAc = Read-Host 'What is the first and last name of the person you would like to search for?'
$filter = "sAmAccountname -eq ""$SamAc"""
Get-ADUser -Filter $filter -Properties FirstName, LastName, EmailAddress | select FirstName, LastName, EmailAddress | Export-CSV "C:\Scripts\PS_ADQuery\Email_Addresses.csv"
I feel like the "Get-Content" cmdlet is close to what I am looking for, but I can't seem to get it to function correctly. I may be going in the totally wrong direction, though.
I found the answer. It turns out I even had the AD properties totally wrong. Take my comments with a grain of salt since I may not fully understand the processes behind each line of code, but this does exactly what I was looking to do.
#creates a $users variable for each username listed in the users.txt file. the ForEach
#command allows you to loop through each item in the users.txt array. the scriptblock
#nested into the ForEach command queries each username for specific properties.
$users = ForEach ($user in $(Get-Content C:\Scripts\PS_ADQuery\users.txt)) {
Get-AdUser $user -Properties GivenName,sn,mail
}
#takes the $users variable defined by the ForEach command and exports listed properties
#to a csv format.
$users |
Select-Object GivenName,sn,mail |
Export-CSV -Path C:\Scripts\PS_ADQuery\output.csv -NoTypeInformation