Using Variable in Filter - powershell

I'm trying to query AD for a list of users from their Surname, which are help in a list.
I've tried most of the afternoon, but I just get a blank Excel sheet.
Also I want to know if there is more than one person with that username in AD, no idea how to even start with that one.
What I have so far:
Import-module ActiveDirectory
$names = get-content c:\tempfiles\Final.txt
$names | ForEach-Object {
$ADUserParams=#{
'Searchbase' = 'OU=Administrators,OU=Locations,DC=The,DC=group,DC=com'
'Searchscope'= 'Subtree'
}
get-aduser #ADUserParams -filter 'surname -like "$Names*"' | Select-Object Samaccountname, UserPrincipalName | export-csv C:\TempFiles\Usernames.csv
}
Do I even need a filter if it's a foreach-object? And is there a way to then check AD within that OU if there are more than one surname that are the same, and how would I count them? I can pull out a list of users surnames and then run the following, but it's then a manual task to locate the missing names. (If that makes sense)
What I have for that so far is:
get-content C:\TempFiles\Users.txt | sort -u > C:\TempFiles\users_cleaned.txt

This should do it (however is untested as I don't have access to an AD right now):
Import-module ActiveDirectory
$names = get-content c:\tempfiles\Final.txt
$ADUserParams=#{
'Searchbase' = 'OU=Administrators,OU=Locations,DC=The,DC=group,DC=com'
'Searchscope'= 'Subtree'
}
$names | ForEach-Object {
$CurrentUser = get-aduser #ADUserParams -filter "surname -like '$_*'" | Select-Object Samaccountname, UserPrincipalName
If ($CurrentUser) {
If ($CurrentUser.Count -gt 1){ $DuplicateSurname = $true }Else{ $DuplicateSurname=$false }
$CurrentUser | ForEach-Object {
$_ | Add-Member -MemberType NoteProperty -Name DuplicateSurname -Value $DuplicateSurname
Write-Output $_
}
} Else {
Write-Warning "$_* did not matched any users."
}
} | export-csv C:\TempFiles\Usernames.csv
Explanation:
Within a ForEach-Object loop the current item in the pipeline is represented by $_. You also need to use double quotes for the filter string, as variables (like $_) are expanded in double quoted strings, not single quoted strings.
You don't need to declare your $ADUserParams hashtable within the loop (that's wasteful) so I moved it outside.
The result of Get-ADUser will be returned to the pipeline, so finally I moved the | export-csv outside of the ForEach-Object so that the result of the processing is piped in to it. I think without this you'd only get the final result.
"Also I want to know if there is more than one person with that username in AD"
To handle this I have put a second ForEach-Object that loops through every user returned in to $CurrentUser and adds a "DuplicateSurname" property to the object (which should then be an additional column in your CSV) based on whether the count of $CurrentUser is more than 1 or not.
Finally we have to make sure that the contents of $_ are put back in to the pipeline which we do with Write-Object $_.

Related

Powershell AD: filter description -like $variable => contains $variable

My task include to filter all users names in group and subgroup in AD. Continue to filter the computers and show just those, which contains filtered names.The problem is, that description includes also other characters like space or "NEW".
My code:
foreach ($file in Get-ADGroupMember -Identity GroupName -Recursive) {Get-ADComputer -Filter 'Description -like $file.name' -Property Name,Description | Select -Property Name,Description}
It would be great to just add * or change -like to -include :D But...
My begginers question is: How to write the code to see all results, not just the ones which match exactly the $file.name?
Thank you for ur time!
Your initial problem was in the Filter you used. With the correct quoting and using the sub-expression operator $() that fixed it.
However, as promised in my comment, here's what I mean on how you can create a report of group members (both users, computers and if you like also subgroups).
Since all objects returned from the Get-ADGroupMember cmdlet have an .objectClass property, you can use that to determine what next Get-AD* cmdlet you can use.
Here, I'm capturing the collected objects output in the foreach() loop in a variable that you can show on screen, or save as Csv file you can open in Excel for instance.
$groupName = 'GroupName'
$result = foreach($adObject in (Get-ADGroupMember -Identity $groupName -Recursive)) {
# use the proper Get-AD* cmdlet depending on the type of object you have
switch ($adObject.objectClass) {
'user' {
$adObject | Get-ADUser -Properties Description | Select-Object Name, Description, #{Name = 'Type'; Expression = {'User'}}
}
'computer' {
$computer = $adObject | Get-ADComputer -Properties Description
# you want to output only the computers where the Description property holds the computer name
if ($computer.Description -like '*$($computer.Name)*') {
$computer | Select-Object Name, Description, #{Name = 'Type'; Expression = {'Computer'}}
}
}
# perhaps you don't want subgroups in your report, in that case just remove or comment out the next part
'group' {
$adObject | Get-ADGroup -Properties Description | Select-Object Name, Description, #{Name = 'Type'; Expression = {'Group'}}
}
}
}
# show the result on screen
$result | Format-Table -AutoSize
# save the result as Csv file
$outFile = Join-Path -Path 'X:\Somewhere' -ChildPath ('{0}_members.csv' -f $groupName)
$result | Export-Csv -Path $outFile -NoTypeInformation -UseCulture
The -UseCulture switch makes sure the Csv file uses the delimiter character your local Excel expects. Without that, a comma is used
Interesting reads:
about_Operators
Adam the Automator
Learn Powershell | Achieve More
and of course StackOverflow

Compare User AD and CSV file column Powershell

I'm not really good in Powershell, I try to write a script to compare a column "User" in a CSV with my all user AD.
I need to get all users in the CSV where not in our AD.
Here what I have wrote :
$csvfile = Import-CSV USERAccountstocompare.csv
$alladusers = Get-ADUser -Filter * | Select sAMAccountName
foreach($user in $alladusers){
$userAD = $alladusers.SamAccountName
foreach($usercsv in $csvfile){
if ($usercsv | where {$_.user -ne "$userAD"}){ write "$usercsv"}
else{}
}
}
When I put a write $usercsv before the if command; I get the good user
but after the if, it write all user with #{User= before, like "#{User=username}" so the comparison not working.
You don't need a foreach loop for this; just filter with Where-Object.
Assuming the User column in the CSV contains SamAccountNames:
$csvUsers = Import-Csv -Path 'D:\Test\USERAccountstocompare.csv'
$allADUsers = Get-ADUser -Filter * | Select-Object -ExpandProperty sAMAccountName
$notADUsers = $csvUsers | Where-Object { $allADUsers -notcontains $_.User }
# output on screen
$notADUsers | Format-Table -AutoSize
# output to new CSV file
$notADUsers | Export-Csv -Path 'D:\Test\UsersNOTinAD.csv' -NoTypeInformation
$alladusers = Get-ADUser -Filter * | Select sAMAccountName is not a very good idea if the Domain you are working on is big. Using Where-Object is also not a very good idea for filtering big objects, there was a really cool article in powershell.org where Dave Wyatt and Don Jones explained the different ways of filtering an object and their efficiency, sadly it was removed for some reason.
I would do something like this, assuming your Csv has a column 'User' for each user:
$result=New-Object System.Collections.ArrayList
#result array will be only the user that do not exist in AD
$csvfile = Import-CSV USERAccountstocompare.csv
foreach($line in $csvfile.User)
{
$filter="(|(Name=$line)(samAccountName=$line))"
$adusr=Get-ADuser -LDAPFilter $filter
if(!$adusr)
{
$result.add($line) > $null
}
}
If instead, you wanna have a list of the users that are on the Csv and on AD and those that are only in the Csv you could do something like this:
$result=New-Object System.Collections.ArrayList
#result array will be only the user that do not exist in AD
$csvfile = Import-CSV USERAccountstocompare.csv
foreach($line in $csvfile.User)
{
$filter="(|(Name=$line)(samAccountName=$line))"
$adusr=Get-ADuser -LDAPFilter $filter
if(!$adusr)
{
$result.add(
[pscustomobject]#{
'Not In AD'=$line
}) > $null
}
else
{
$result.add(
[pscustomobject]#{
'In AD and Csv'=$line
}) > $null
}
}

List user details from Username

I am trying to create a script that will check a list of user names and show the user full name and some attribute settings from AD. Basically I have been sent a list of usernames which are just numbers and management want to know the users full name for each username. they also want to know want division they work for.
Below is the script I have created which doesn't work.
$csv = Import-Csv "C:\temp\users.csv"
foreach ($user in $csv) {
$name = $user.myid
Get-ADUser -Filter {EmployeeID -eq $name} -Properties * |
Get-ADUser -Division $user.Programme
} | Export-Csv "C:\Temp\Results.csv"
So I'm working under the assumption that there is a column named myid in your csv file that contains the id you need to be looking up. Assuming that is the case you'll need to make a few changes here. You'll need to remove the second get-aduser as it is not really doing anything for you, and there is no -division switch available to the get-aduser cmdlet, if you need to restrict your results to just a few settings you can do that using the -properties switch and piping to select as shown below. Keep in mind that none of this will matter if the users do not have the "employeeid" and "division" properties set on their AD accounts, which is fairly rare in my experience but if your company does as a matter of policy when creating accounts should be fine. If you replace the get-aduser line in your script with this it should get the account of any user with an EmployeeID property that matches the one in your spreadsheet and then output that person's full name, division, and employeeid to your CSV file.
Get-ADUser -Filter {EmployeeID -eq $name} -Properties "displayname","division","employeeid" | Select-Object "employeeid","displayname","division"
When in doubt, read the documentation. Get-ADUser doesn't have a parameter -Division. You need to select the properties you want in the output file. Also, foreach loops don't pass output into the pipeline. You need a ForEach-Object loop if you want to pass the output directly into Export-Csv:
Import-Csv 'C:\temp\users.csv' |
ForEach-Object {
$name = $_.myid
Get-ADUser -Filter "EmployeeID -eq $name" -Properties *
} |
Select-Object SamAccountName, DisplayName, Division |
Export-Csv 'C:\Temp\Results.csv' -NoType
Otherwise you need to collect the output in a variable:
$users = foreach ($user in $csv) {
$name = $user.myid
Get-ADUser -Filter "EmployeeID -eq $name" -Properties *
}
$users | Export-Csv 'C:\Temp\Results.csv' -NoType
or run the loop in a subexpression:
$(foreach ($user in $csv) {
$name = $user.myid
Get-ADUser -Filter "EmployeeID -eq $name" -Properties *
}) | Export-Csv 'C:\Temp\Results.csv' -NoType
This is a generic code structure that can be adapted for data collection / enumeration and production of CSV files, tailored to your scenario. We use similar at my workplace. It contains some error handling - the last thing you'd want is inaccurate results in your CSV file.
# Create an array from a data source:
$dataArray = import-csv "C:\temp\users.csv"
# Create an array to store results of foreach loop:
$arrayOfHashtables = #()
# Loop the data array, doing additional work to create our custom data for the CSV file:
foreach($item in $dataArray)
{
try
{
$ADObject = Get-ADUser -Filter { EmployeeID -eq $item.MyID } -Properties DisplayName,Division -ErrorAction Stop
}
catch
{
Write-Output "$($item.MyID): Error looking up this ID. Error was $($Error[0].Exception.Message)"
}
if($ADObject)
{
# Create a hashtable to store information about a single item:
$hashTable = [ordered]#{
EmployeeID=$item.myID
DisplayName=$ADObject.DisplayName
}
# Add the hashtable into the results array:
$arrayOfHashtables += (New-Object -TypeName PSObject -Property $hashTable)
}
else
{
Write-Output "$($item.MyID): No result found for this ID."
}
}
# If the results array was populated, export it:
if($arrayOfHashtables.Count -gt 0)
{
$arrayOfHashtables | Export-CSV -Path "C:\Temp\Results.csv" -Confirm:$false -NoTypeInformation
}
As mentioned elsewhere, division isn't a property on an AD object so you might need to lookup this data elsewhere. If you can do that with another line of PowerShell inside your foreach loop, you could add this to your hashtable object like so:
$hashTable = [ordered]#{
EmployeeID=$item.myID
DisplayName=$ADObject.DisplayName
Division=$DivisionFromOtherSource
}

How to get list of selected AD Groups, that a large list of users are members of?

I have the below working script that checks if a large list of users in a CSV file are a member of an AD group and writes the results to results.csv.
Not sure how to convert the script so I can change $group = "InfraLite" to $group = DC .\List_Of_AD_Groups.CSV.
So the script doesn't just return matches for one AD group but so it returns matches for the 80 AD groups contained in the List_of_AD_groups.csv also. Writing a YES/NO for each AD group in a new column in the CSV (or if that's not possible creating a seperate .csv file for each group with results would do also.
I could do this manually by changing the value of $group and export file name, and re-running the script 80 times but must be a quick was with PS to do this?
e.g. results.csv:
NAME AD_GROUP1 AD_GROUP2 AD_GROUP80 etc etc.
user1 yes no yes
user2 no no yes
user3 no yes no
echo "UserName`InfraLite" >> results.csv
$users = GC .\user_list.csv
$group = "InfraLite"
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SAMAccountName
foreach ($user in $users) {
if ($members -contains $user) {
echo "$user $group`tYes" >> results.csv
} else {
echo "$user`tNo" >> results.csv
}
}
I played with this for a while, and I think I found a way to get you exactly what you were after.
I think Ansgar was on the right path, but I couldn't quite get it to do what you were after. He mentioned that he didn't access to an AD environment at the time of writing.
Here is what I came up with:
$UserArray = Get-Content 'C:\Temp\Users.txt'
$GroupArray = Get-Content 'C:\Temp\Groups.txt'
$OutputFile = 'C:\Temp\Something.csv'
# Setting up a hashtable for later use
$UserHash = New-Object -TypeName System.Collections.Hashtable
# Outer loop to add users and membership to UserHash
$UserArray | ForEach-Object{
$UserInfo = Get-ADUser $_ -Properties MemberOf
# Strips the LPAP syntax to just the SAMAccountName of the group
$Memberships = $UserInfo.MemberOf | ForEach-Object{
($_.Split(',')[0]).replace('CN=','')
}
#Adding the User=Membership pair to the Hash
$UserHash.Add($_,$Memberships)
}
# Outer loop to create an object per user
$Results = $UserArray | ForEach-Object{
# First create a simple object
$User = New-Object -TypeName PSCustomObject -Property #{
Name = $_
}
# Dynamically add members to the object, based on the $GroupArray
$GroupArray | ForEach-Object {
#Checking $UserHash to see if group shows up in user's membership list
$UserIsMember = $UserHash.($User.Name) -contains $_
#Adding property to object, and value
$User | Add-Member -MemberType NoteProperty -Name $_ -Value $UserIsMember
}
#Returning the object to the variable
Return $User
}
#Convert the objects to a CSV, then output them
$Results | ConvertTo-CSV -NoTypeInformation | Out-File $OutputFile
Hopefully that all makes sense. I commented as much of it as I could. It would be very simple to convert to using ADSI if you didn't have RSAT installed on whatever machine you're running this on. If you need that let me know, and I'll make some quick modifications.
I've also tossed a slightly modified version of this in a Gist for later reference.
The trivial solution to your problem would be to wrap your existing code in another loop and create an output file for each group:
$groups = Get-Content 'C:\groups.txt'
foreach ($group in $groups) {
$members = Get-ADGroupMember ...
...
}
A more elegant approach would be to create a group mapping template, clone it for each user, and fill the copy with the user's group memberships. Something like this should work:
$template = #{}
Get-Content 'C:\groups.txt' | ForEach-Object {
$template[$_] = $false
}
$groups = #{}
Get-ADGroup -Filter * | ForEach-Object {
$groups[$_.DistinguishedName] = $_.Name
}
Get-ADUser -Filter * -Properties MemberOf | ForEach-Object {
$groupmap = $template.Clone()
$_.MemberOf |
ForEach-Object { $groups[$_] } |
Where-Object { $groupmap.ContainsKey($_) } |
ForEach-Object { $groupmap[$_] = $true }
New-Object -Type PSObject -Property $groupmap
} | Export-Csv 'C:\user_group_mapping.csv' -NoType

Trying to rename several account types at once based on current displayName

This morning some awesome people helped me make a script to move user accounts based on their displayName to a certain OU. I tested and it worked. I cannibalized the script to make another one that will rename the same accounts based off of the same criteria. I've gone through several errors but basically it all boils down to "I am having an identity crisis!". I can't seem to figure out exactly what I need to input as the $Identity. Here is what I have:
Import-Module ActiveDirectory
$Renames = #(
#{
Filter = 'DisplayName -like "*Supply*"'
NewName = "Supplies"
},
#{
Filter = 'DisplayName -like "*Accountant*"'
NewName = "Accounting"
}
) | ForEach-Object {New-Object -TypeName PSCustomObject -Property $_}
$OriginOU = "OU=Test,OU=Standard Users,OU=Domain Users,DC=com"
foreach ($Rename in $Renames) {
Get-ADUser -SearchBase $OriginOU -Filter $Rename.Filter -Properties displayName |
Where-Object {($_.Enabled -eq 'True') -and ($_.DistinguishedName -notlike '*DontTouch*')} |
%{Set-ADUser $_ -DisplayName {$_.DisplayName -replace '(.EPSILON ).+',"`$1$Rename.NewName"}}
}
You can't use the current object variable ($_) if you have Set-ADUser read directly from the pipeline. And since Set-ADUser apparently doesn't play nice with scriptblock arguments, you have to put the statement in a loop:
... | % { Set-ADUser $_ -DisplayName ($_.DisplayName -replace '(.EPSILON ).+',"`$1$($Rename.NewName)") }
Note that if you want to expand object properties inside a string you have to put $Rename.NewName in a subexpression ($()), otherwise the whole object $Rename would be stringified and the string ".NewName" would be appended to it.