I need to move a few hundred AD distro groups to a new OU. I was given their email address only, and wanted to make a script to move them based on samaccountname. I am wondering why the below does not return anything, if I do the groups one-off filtering for email address it works, but foreach returns nothing.
The "groups.txt" listed below is just a list of email addresses.
gc groups.txt | % {
Get-ADGroup -Filter {mail -eq "$_"}| Select-Object -ExpandProperty SamAccountName
}
Remove the quotes you have around $_.
gc groups.txt | % {
Get-ADGroup -Filter {mail -eq $_}| Select-Object -ExpandProperty SamAccountName
}
In your posted filter script block the variable is quoted. Since it is a script block, PowerShell doesn't do any processing first and the ActiveDirectory module expects a variable not surrounded in quotes. That would look for Mail that is literally "$_" and not the email address value of the variable.
Take the "" off of $_
gc groups.txt | %{ Get-ADGroup -Filter {mail -eq "$_"} | select -expandproperty samaccountname}
Related
Total beginner with powershell trying to become more efficient in my helpdesk job. Essentially I have a .csv file with a list of e-mail addresses that I want to use to query AD to see what accounts are enabled/disabled. I don't get any output from what I've tried so far which is:
Import-Module ActiveDirectory
$emails = Import-Csv -Path "c:\users\me\mycsvfile.csv"
foreach($email in $emails) {
Get-ADUser -Filter {userprincipalname -eq '$email.email'} | Select-Object name,enabled
}
The .csv file has a header for that column called email which is why I've used '$email.email'. Anyone have any idea as to what I'm doing wrong (probably lots).
Many thanks
These ad filters are a bit trial and error.
foreach($email in $emails) {
$upn = $email.email
Get-ADUser -Filter {userprincipalname -eq $upn} | Select-Object name,enabled
}
name enabled
---- -------
js True
A UserPrincipalName property can be the same as the users email address, but in fact they are different properties.
The similarity is that a UserPrincipalName (UPN) is the name of a system user in an email address format.
Let's assume in your domain the UPN is also the EmailAddress of a user.
I'm guessing your csv is really a text file with email addresses each on a separate line and that it has no header email.
Something like
you#yourdomain.com
him#yourdomain.com
her#yourdomain.com
someone.else#yourdomain.com
In that case, you either need to use Get-Content so you'll get all addresses (lines) as string array and loop over:
Import-Module ActiveDirectory
$emails = Get-Content -Path "c:\users\me\mycsvfile.csv"
foreach($email in $emails) {
Get-ADUser -Filter "UserPrincipalName -eq '$email'" | Select-Object Name, Enabled
}
OR use Import-Csv and supply a header for the field(s) in there:
Import-Module ActiveDirectory
$emails = Import-Csv -Path "c:\users\me\mycsvfile.csv" -Header 'email'
foreach($email in $emails) {
Get-ADUser -Filter "UserPrincipalName -eq '$($email.email)'" | Select-Object Name, Enabled
}
the -Filter parameter should actually be a string not a script block
when using object.property syntax in a filter, you need to surround it with $(), making it a sub expression that gets parsed by PowerShell into the value it contains.
Was looking around and found this little powershell script. BAsically want to be able to export all my primary SMTP and username from AD.
Get-ADUser -Filter * -Properties proxyaddresses | Select-Object Name, #{L = "ProxyAddresses"; E = {($_.ProxyAddresses | Where-Object {$_ -like "*SMTP:*" }) -join ';'} | Export-Csv -Path C:\Users\mohammad.anwar\Documents\proxyaddresses.csv -NoTypeInformation
The output would give me multiple smtp. Im looking for the Primary SMTP.
The logic of your query is fairly sound. There are just some syntax and operator issues with producing the result you want.
Get-ADUser -Filter * -Properties ProxyAddresses |
Select-Object Name, #{L = "ProxyAddresses"; E = {($_.ProxyAddresses -clike "SMTP:*") -join ';'}} |
Export-Csv -Path C:\proxyaddresses.csv -NoTypeInformation
Most PowerShell Comparison Operators are case-insensitive. The case-sensitive operators begin with -c. -clike should be used instead of -like when case matters.
Since ProxyAddresses will be a collection, you don't need Where-Object (if running PowerShell 3+). You can just compare the ProxyAddresses values directly. PowerShell will enumerate the collection.
I honestly can't think of a case where an account would have multiple primary SMTP addresses (the SMTP: address). So having a -join doesn't make much sense since -join attempts to join items of a resulting collection. If you want to query all SMTP addresses (including lowercase smtp:) for an account, then -join would make sense here.
In your attempt, you will see >> when the code is run at the console. The reason is because you did not close all of your open {. The hash table for the Select-Object calculated property (begins with #{) was never closed. The PowerShell console has this behavior when an opening (, {, or quote is not closed properly.
Try the below.
This is a very common thing with plenty of examples all over the web showing how to do this. Why do this from ADDS vs native Exchange cmdlets to get this info by default.
You can use remoting to get the Exchange cmdlets for use via your
workstation.
Of course, searching for 'PowerShell get primary SMTP address' delivers examples for Exchange native and ADDS options.
Examples:
Get-Mailbox -ResultSize Unlimited |
Select-Object SamAccoutnName, DisplayName, PrimarySmtpAddress
# Or this...
Get-Mailbox -Identity $UID |
Select -ExpandProperty PrimarySmtpAddress
# Or via ADDS cmdlets or other
Get-ADUser -LDAPFilter "(proxyAddresses=*)" -Properties sAMAccountName, proxyAddresses |
Select sAMAccountName, proxyAddresses |
ForEach {
$Name = $User.sAMAccountname
$Addresses = $User.proxyAddresses
ForEach ($Address In $Addresses)
{
If ($Address -cmatch "SMTP:")
{"User $Name has primary address $Address" }
}
}
Identify primary SMTP Address from Active Directory with PowerShell
# First, we need to get the ProxyAddresses attribute from AD :
Get-ADUser -Identity SomeUserName -Properties ProxyAddresses |
select -ExpandProperty ProxyAddresses
# Then, we need to identify the one that starts with “SMTP:” :
# We used the “clike” operators, which is case sensitive :
Get-ADUser -Identity SomeUserName -Properties ProxyAddresses |
select -ExpandProperty ProxyAddresses |
? {$_ -clike "SMTP:*"}
I have a list of names taken from Oracle that Im trying to find the SamAccountName for. The file is a CSV and some names are "last, first" or "last, first middle initial" and some have three or four names like "alpha bravo charlie delta". The names in the list are likely not the same as listed in AD. Trying to figure out how to sort through this to find AD accounts. The code I currently have is not producing any results.
Import-Module ActiveDirectory
Import-Csv "\\server\users\folder\Oracle_HR.csv"
ForEach-Object{
Get-ADUser -Filter { Name -like "'$($_.name)'"} -Properties Name |
Select-Object Name,SamAccountName |
Export-CSV "\\server\users\folder\Oracle_ADs.csv" -NoTypeInformation
}
The answers by Gabriel Luci and Mathias R. Jessen give good advice on "fuzzy" filtering of AD users.[1]
However, your primary problem is that your ForEach-Object call is not receiving pipeline input, because you haven't connected it to output from the Import-Csv call.
Simply join the two commands with |:
Import-Csv "\\server\users\folder\Oracle_HR.csv" | ForEach-Object { # ...
Secondarily, your -Filter argument { Name -like "'$($_.name)'"} mistakenly applies two layers of quoting and is missing wildcard characters, given that -like compares against the entire field, yet you want substring matching.
Since it's better to avoid the use of script blocks ({ ... }) as -Filter arguments, use a string:
"Name -like `"*$($_.name)*`"" # Note the enclosing '*' to match substrings
Note that I've used embedded " quoting (escaped as `") rather than ' quoting, so as not to break the filter with names that contain ', such as O'Malley.
That said, if, as your question suggests, the names in your CSV data aren't direct substrings of the AD users' .Name property values, the above filter will not be enough, and even the ANR (Ambiguous Name Resolution) technique shown in the linked answers may not be enough.
Thirdly, your Export-Csv call is misplaced: because it is inside the ForEach-Object script block, the same output file gets overwritten in every iteration.
Restructure your command as follows:
Import-Csv ... | ForEach-Object { ... } | Export-Csv ...
Optional reading: ForEach-Object behavior when not providing pipeline input:
The associated script block is executed once.
$_, the automatic variable that contains the current input object, is $null.
[1] Note that the search term in the LDAP filter may need escaping ; per this article, the characters * ( ) \ NUL require escaping and must be escaped as \<hh>, where <hh> is the two-digit hex representation of the char's ASCII code (e.g., * must be escaped as \2A):
$escapedName = -join $(foreach ($c in [char[]] $_.name) { if ('*', '\', '(', ')', "`0" -contains $c) { '\' + ([int] $c).ToString('X2') } else { $c } })
Get-ADUser -LDAPFilter "(anr=$escapedName)"
With $_.name containing string "James* (Jimmy) Smith\Smyth`0", $escapedName would evaluate to literal James\2A \28Jimmy\29 Smith\5CSmyth\00
Keep in mind that the property names in PowerShell are not named the same as the attributes in AD. The Name property corresponds to both the name and cn attributes in AD (both attributes are always the same).
There is also DisplayName (displayName in AD), GivenName (givenName), and Surname (sn). You could try matching against the DisplayName:
Get-ADUser -Filter "DisplayName -eq '$($_.name)'"
If none of those properties match your data exactly, you will have some trouble. No one thing you do will probably work for every account. Hopefully this is just a one-time job and you can work through them in pieces (try one thing, take out the ones that work, and try something different on the rest).
One thing you can try is using AD's Ambiguous Name Resolution (ANR), which will match a search string against several different attributes and even match a first and last name against givenName and sn. That might work with some in your list. You would use it like this:
Get-ADUser -LDAPFilter "(anr=$($_.name))"
If none of that works, you'll have to split the names (for example, by spaces: $_.name.Split(" ")) and try to match pieces of it to different attributes. You'll have to look at your data and see what works.
One approach is to use the Ambiguous Name Resolution feature in Active Directory.
It'll do fuzzy matching against multiple attributes, like the displayName, Name and mail attributes (among others), so it's pretty good for this exact kind of scenario where you don't necessarily know the order or the names or the full name up front:
Get-ADUser -LDAPFilter "(&(anr=$($_.name)))"
I recommend using LDAPFilter and Ambiguous Name Resolution (anr) with Get-ADUser. The algorithm looks up several name fields in different orders to find matches:
Get-ADUser -LDAPFilter "(anr=John Doe)"
Or modifying your code:
Get-ADUser -LDAPFilter "(anr=$($_.name))"
You could try something like the following:
Import-Module ActiveDirectory
$allUsers = Get-Content "\\server\users\folder\Oracle_HR.csv"
$users = #()
ForEach($obj in $allUsers){
$user = Get-ADUser -Filter { GivenName -like $obj} -Properties Name, samAccountName
if(!$user){
$user = Get-ADUser -Filter { SurName -like $obj} -Properties Name, samAccountName
}
if(!$user){
$user = Get-ADUser -Filter { Name -like $obj} -Properties Name, samAccountName
}
if(!$user){
Write-Host "User $obj could not be found" -ForegroundColor Red
}else{
$users += $user
}
}
$users | Select-Object Name,SamAccountName | Export-CSV "\\server\users\folder\Oracle_ADs.csv" -NoTypeInformation
You might need to split the values also like:
Import-Module ActiveDirectory
$allUsers = Get-Content "\\server\users\folder\Oracle_HR.csv"
$users = #()
ForEach($obj in $allUsers){
$objSplit = $obj.Split(",")
foreach($split in $objSplit){
$user = Get-ADUser -Filter { GivenName -like $split} -Properties Name, samAccountName
if(!$user){
$user = Get-ADUser -Filter { SurName -like $split} -Properties Name, samAccountName
}
if(!$user){
$user = Get-ADUser -Filter { Name -like $split} -Properties Name, samAccountName
}
if(!$user){
Write-Host "User $split could not be found" -ForegroundColor Red
}else{
if($users.samAccountName -notcontains $user.SamAccountName){
$users += $user
}
}
}
}
$users | Select-Object Name,SamAccountName | Export-CSV "\\server\users\folder\Oracle_ADs.csv" -NoTypeInformation
My question is how I split content prompted from a command line in powershell
I want to find all the OU's path on my server, so I can use them later on..
get-aduser -Filter * | Select DistinguishedName | findstr "OU="
OUTPUT
CN=Username1,OU=firma1,DC=bc,DC=local
CN=Username2,OU=firma2,DC=ad,DC=local
and I want only
OU=firma1,DC=bc,DC=local
OU=firma2,DC=ad,DC=local
This should work:
Get-ADUser -Filter * |
Where-Object { $_.DistinguishedName.IndexOf('OU=') -gt 0 } |
ForEach-Object { $_.DistinguishedName.Substring($_.DistinguishedName.IndexOf('OU=')) }
This code filters out any objects that aren't in an OU, which includes those which are in the Users built-in container, like CN=Administrator,CN=Users,DC=bc,DC=local. You could also handle those users with an if statement in the ForEach-Object.
How about using the .NET String.Split method directly
Get-ADUser -Properties DistinguishedName | ForEach-Object {
$_.DistinguishedName.Split(",", 2)[1]
}
This splits the DistinguishedName into two parts at the comma (i.e. everything before the first comma and everything after) and returns the second part only (array index [1]).
I would probably add a Sort-Object and a Select-Object -Unique to the end of that.
Worth a read: Get-ADOrganizationalUnit, maybe that is what you are actually are looking for?
This approach is similar, with the PowerShell-native -split operator:
$DNs = Get-ADUser -Properties DistinguishedName | Select -ExpandProperty DistinguishedName
$DNs -split ',',2 | where { $_.StartsWith("OU=") }
I need to check the following code against a txt file with computer names, so if the computer name exist in the file, it'll be copied to the shadow group.
$OU="OU=Computers,DC=mylab,DC=local"
$ShadowGroup="CN=SelectPCs,OU=Groups,DC=mylab,DC=local"
Get-ADComputer –SearchBase $OU –SearchScope OneLevel –LDAPFilter "(!memberOf=$ShadowGroup)" | ForEach-Object {Add-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup}
I tried a pipe and 'Where', but couldn't make it work:
$OU="OU=Computers,DC=mylab,DC=local"
$ShadowGroup="CN=SelectPCs,OU=Groups,DC=mylab,DC=local"
$PCList = c:\scripts\computernames.txt
Get-ADComputer –SearchBase $OU –SearchScope OneLevel –LDAPFilter "(!memberOf=$ShadowGroup)" **| Where-Object {$_.Name –Match $PCList}** |ForEach-Object {Add-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup}
I suggest you rewrite your command as follows:
Get-Content c:\scripts\computernames.txt | Get-ADComputer -Property MemberOf |
Where-Object { $_.DistinguishedName -match ('^[^,]+,' + $OU + '$') -and
$_.MemberOf -notcontains $ShadowGroup } |
ForEach-Object {Add-ADPrincipalGroupMembership –Identity $_ –MemberOf $ShadowGroup}
Start with your list of computer names, which Get-ADComputer should be able to identify directly, via the pipeline, implicitly bound to the -Identity parameter.
-Property MemberOf ensures that the non-default property MemberOf is included in each return object.
The Where-Object call then checks whether the computer is in the right OU ($_.DistinguishedName -match ('^[^,]+,' + $OU + '$') - perhaps you don't even need this, given that you've identified the computers by name[1]
) and whether it's not already a member of $ShadowGroup ($_.MemberOf -notcontains $ShadowGroup).
Finally, the Foreach-Object calls adds the matching computers to group $ShadowGroup.
[1] Computers are assumed to have unique names, so using Get-ADComputer with a computer name (which implies parameter -Identity) matches a computer in any OU, so there's generally no need to constrain the matches by OU.
That said, the regex used above is explicitly designed to only match computers located directly in $OU.
If you wanted to match computers in the specified OU and any descendent OU, use $_.DistinguishedName -match (',' + $OU + '$')
It looks like you are trying to match a name with the array of computer names. That would be like asking does 'computer1' contain 'computer1, computer2, computer3' which would be false. But asking if 'computer1, computer2, computer3' contains the string 'computer1' would be true.
In other words, instead of Where-Object {$_.Name -match $PCList} you could do Where-Object {$PCList -match $_.Name}.
Keep in mind though that this would also do partial matches. For example computer1 would match for computer1 and also computer11, computer12, etc.
The better approach would be to loop through the list of computernames in the text file to perform your check and action (as mklement0's answer demonstrates).