My goal is to list extended properties of a list of users by Display Name or SamAccountName pulling those names from a Csv. I am using the following script and it works but it either skips names in the Csv or repeats them. If I do one at a time it returns what I need but from the Csv it doesn’t. Csv has one column named Name.
Import-Csv C:\Users\Administrator\Documents\test.txt | Foreach {
Get-ADUser -Filter "DisplayName -eq '$($_.Name)'" -Properties *
} | Select-Object DisplayName, SamAccountName, Title, Department, EmailAddress, ObjectGUID | Sort-Object Displayname | FT
There is nothing wrong with your current code, except for using Import-Csv on a .txt file (test.txt), I would assume this was a typo. I've added an if condition to help you troubleshoot so at least you would know which users where not found.
You should also avoid the use of -Properties *, querying all properties for the users is inefficient and slow.
$properties = #(
'DisplayName'
'SamAccountName'
'Title'
'Department'
'EmailAddress'
'ObjectGUID'
)
Import-Csv C:\Users\Administrator\Documents\test.csv | ForEach-Object {
$adUser = Get-ADUser -Filter "DisplayName -eq '$($_.Name)'" -Properties $properties
if(-not $adUser) {
Write-Warning "'$($_.Name)' could not be found on AD"
return # Go next
}
$adUser
} | Select-Object $properties | Sort-Object Displayname | Format-Table
Related
I want to have a script that i can start and it gives me all the groups starting with APS- and its members
I want to get a HTML where it says in the Header : the group and in the table the Users
this is the code i got so far :
$properties = 'GivenName', 'Surname', 'UserPrincipalName'
Get-ADGroup -Filter {name -like "APS-*"} | ForEach {
$groupName = $_.Name
Get-ADGroupMember -Identity $_.SamAccountName |Get-ADUser -Property $properties |Select
#{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'}
But this give me a list of group,givenname,Surename,userprincipal
i want to get :
Organized like this
Is there a way of getting this ?
please consider the following points in order to accomplish the needed result
the variable $properties contains common values, you do not need to pass it to `Get-ADUser to get its values
You need to specify the parameter recursive to the command Get-ADGroupMember in order to get all child users within child groups.
If you need to output the result in HTML format you can use the command ConvertTo-Html
I modified your code to output the needed format, please check it
$groups = Get-ADGroup -Filter {name -like "APS-*"}
$list = foreach ($group in $groups) {
$groupName = $group.Name
Get-ADGroupMember -Identity $group.SamAccountName -Recursive | Get-ADUser | select #{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'
}
$out = $list | Group-Object GroupName
[string]$out_html = foreach ($item in $out){
$item.group | Select-Object 'GivenName', 'Surname', 'UserPrincipalName' | ConvertTo-Html -PreContent "<br>$($item.Name)<br><br>"
}
$out_html| Out-File .\groups.html
if you need to exclude specific users from the report, you can filter the output of Get-ADGroupMember using the Where clause as follow
# i.e. to exclude specific samaccountnames
$execlusionList = #("SamAccountName1","SamAccountName2")
Get-ADGroupMember -Identity $group.SamAccountName -Recursive | where {$_.samaccountname -notin $execlusionList} | Get-ADUser | select #{N='GroupName';E={$groupName}},'GivenName', 'Surname', 'UserPrincipalName'
I have the following code:
$FilePath_Prefix = "C:\temp\UserLastLogon-"
function Msg ($Txt="") {
Write-Host "$([DateTime]::Now) $Txt"
}
#Cycle each DC and gather user account lastlogon attributes
$List = #() #Define Array
(Get-ADDomain).ReplicaDirectoryServers | Sort | % {
$DC = $_
Msg "Reading $DC"
$List += Get-ADUser -Server $_ -Filter "samaccountname -like '*'" -Properties LastLogon |
Select samaccountname, lastlogon, #{n='DC';e={$DC}}
}
Msg "Sorting for most recent lastlogon"
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$LatestLogOn += ($_.Group | Sort -prop lastlogon -Descending)[0]
}
$List.Clear()
$FileName = "$FilePath_Prefix$([DateTime]::Now.ToString("yyyyMMdd-HHmmss")).csv"
try {
$LatestLogOn |
Select samaccountname, lastlogon,
#{n='lastlogondatetime';e={[datetime]::FromFileTime($_.lastlogon)}}, DC |
Export-CSV -Path $FileName -NoTypeInformation -Force
Msg "Exported results. $FileName"
} catch {
Msg "Export Failed. $FileName"
}
I use it to interrogate AD Users for most up-to-date lastLogon information across all my domains. It works, and it works really fast.
Now, I need to get more details into my output, such as givenName and Surname lets say.
What would be the best approach to achieve this, because I don't want to interrogate redundantly all my DC's for those kind of attributes.
My idea here, is to create another array with Get-ADuser -Filter * -Properties givenName, surname, etc..etc and then bind together the two arrays. And I don't seem to get it right. Could someone help, or point me in the right direction to achieve this task.
I would fetch all the last logon information and save it into a hash table, which is designed for fast lookups. I'd try something like this:
$FileName = 'C:\temp\UserLastLogon-{0:yyyyMMdd-HHmmss}.csv' -f [DateTime]::Now
$DCs = (Get-ADDomain).ReplicaDirectoryServers | Sort-Object
# This *may* be more semantically accurate; I don't remember how it works with multiple domains
# $DC = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName | Sort-Object
# Create the hash table
$UserLogonInfo = #{}
foreach ($DC in $DCs) {
Write-Host "Reading logon data from $DC..."
# Fetch all users that have a LastLogon value from the current DC
# We specify LastLogon>=1 because some users that never log on have LastLogon of
# 0 or null, both of which would show up as Jan 1, 1601.
Get-ADUser -Server $DC -LDAPFilter '(LastLogon>=1)' -Properties LastLogon | ForEach-Object {
if (!$UserLogonInfo.ContainsKey($_.DistinguishedName)) {
# If the accountname doesn't exist, add it
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
elseif (($UserLogonInfo[$_.DistinguishedName].LastLogon -lt $_.LastLogon)) {
# If the account name exists, update it if it's more recent
$UserLogonInfo[$_.DistinguishedName] = #{
LastLogon = $_.LastLogon
LastLogonDateTime = ([DateTime]::FromFileTime($_.LastLogon))
LastLogonDC = $DC
}
}
}
}
Write-Host "Fetching user data..."
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n='LastLogonDateTime';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDateTime}},
#{n='LastLogonDC';e={$UserLogonInfo[$_.DistinguishedName].LastLogonDC}} |
Export-CSV -Path $FileName -NoTypeInformation -Force
If an account has a blank LastLogonDateTime and blank LastLogonDC, then that account has never logged on.
It's more correct to use DistinguishedName instead of SamAccountName as the key for the $UserLogonInfo hash table, and that is essentially a required change if you are querying multiple domains at once. Note that I do mean multiple domains and not merely multiple domain controllers in the same domain (which is what I believe you're actually doing in spite of the question title).
This whole process on my domain with 3 DCs and ~10,000 users takes about 15 seconds.
Note that there are a ton of ways that LastLogon can be inaccurate. It can be updated without a full logon or without an interactive logon and some logons won't force an update of the field. If you really want to track logons, you should use security auditing for logon events.
Edit:
When we populate $UserLogonInfo we're fetching all accounts except for accounts that either don't have a LogonDate attribute at all or when that attribute is 0 or null. Each of those indicate that there has been no login. So, we know that any user that isn't in the $UserLogonInfo hash table has never logged in.
If you want to use some special value for when a user account has never logged on, you should just use an if statement and check to see if the user is in the $UserLogonInfo hash table:
Get-ADUser -Filter * -Properties LastLogon, givenName, surname |
Select-Object -Property SamAccountName, givenName, surname,
#{n = 'LastLogonDateTime'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDateTime } else { 'Never' }}},
#{n = 'LastLogonDC'; e = {if ($UserLogonInfo.ContainsKey($_.DistinguishedName)) { $UserLogonInfo[$_.DistinguishedName].LastLogonDC } else { 'N/A' }}} |
Export-CSV -Path $FileName -NoTypeInformation -Force
The thing is that you're actually asking your DCs for that data as it's returned by default. So the only thing to add is
, givenName, surname
to your Select-Object. Check this by running
Get-AdUser yourLogin -Properties LastLogon
You'll receive the following properties in your output: DistinguishedName Enabled GivenName LastLogon
Name
ObjectClass
ObjectGUID
SamAccountName
SID
Surname
UserPrincipalName
So the only thing you lose will be some memory. The alternative approach would be to create another array using
$names = Get-ADuser -Filter * | Select-Object SamAccountName, givenName, surname
and then match the date based on SamAccountName like this:
$LatestLogOn = #() #Define Array
$List | Group-Object -Property samaccountname | % {
$t = ($_.Group | Sort -prop lastlogon -Descending)[0]
$n = $names | Where-Object samaccountname -eq $t.samaccountname
$LatestLogOn += $t | select *, #{n="givenName";e={$n.givenName}}, #{n="surname";e={$n.surname}}
}
I've only just started to learn PowerShell but I'm banging my head on the wall with this one. When I Import-CSV a list of users to get the userprincipalname on Get-Aduser with the header SamAccountName, I am able to see all the emails on the console:
ForEach ($user in $allUsers)
{
Get-ADUser $user.samaccountname -Properties userprincipalname | select -Property userprincipalname
}
However, when I try to export them to a csv-file only 1 user comes out. I always have a problem when using a foreach loop for this...
ForEach ($user in $allUsers)
{
Get-ADUser $user.samaccountname -Properties userprincipalname | select -Property userprincipalname | export-csv $path -NoTypeInformation
}
I'm always run into this problem when making my foreach loops. I only ever get or change 1 variable. Why is this? x_X Please help!
And my CSV file has a header of SamAccountName and samaccount names. Without exporting I get good data from this code on the console:
user#test.com
user2#test.com
etc...
You were exporting in each iteration of the loop, which would overwrite the file. I haven't tried it but the changes below should work.
$allUsers | Foreach-Object {
$user = $_
Get-ADUser $user.samaccountname -Properties userprincipalname | select -Property userprincipalname
} | export-csv $path -NoTypeInformation
You must use this syntax vs foreach(x in y) if you want to directly pipeline the results to export-csv
I am trying to write a Powershell script that will allow me to query the samaccountname and other fields from a list of e-mail addresses. (reverse lookup)
My email.csv file has a users header, followed by the list of e-mail addresses.
What am I doing wrong?
$users = Get-Content .\email.csv
$users | ForEach-Object {
Get-ADUser -LDAPFilter "(emailaddress=$users)" -Properties samaccountname | Select-Object -Property samaccountname
} | Export-Csv -Path .\emailexport.csv
you will need to change $_.emailaddress depending on your column title in your csv file. also, samaccountname is returned by default for get-aduser, so you can leave that part off.
$users = Get-Content .\email.csv
$users | ForEach-Object {
$email = $_.emailaddress
Get-ADUser -LDAPFilter "(emailaddress=$email)" -Properties emailaddress |
Select-Object -Property samaccountname, emailaddress
} | Export-Csv -Path .\emailexport.csv
I'm working on a script that will run down a csv of LastName and FirstName of users on a domain and return someinfo about them in another csv.
Returning the properties is not an issue, but when I try to convert pwdLastSet and LastLogonTimeStamp to a readable format, it crashes when writing to the csv.
Here is my code. in this example, pwdLastSet will result in an unreadable 64bit number.
$names = import-csv C:\Users\me\Desktop\input.csv
$users = #()
foreach ($name in $names){
$filter = "givenName -like ""*$($name.FirstName)*"" -and sn -like ""$($name.LastName)"""
$users += get-aduser -filter $filter -Properties * | select-object employeeID, sn, givenName, distinguishedName, whencreated, passwordnotrequired, enabled, admincount, pwdlastset
}
$users | select employeeID, sn, givenName, distinguishedName, whencreated, passwordnotrequired, enabled, admincount, pwdlastset | export-csv c:\users\me\desktop\results.csv -NoTypeInformation
I'd like to throw $([datetime]::FromFileTime($user.pwdLastSet)) so it's readable in the output.
Any ideas?
You can use a calculated property to replace the value of a property or create an additional property:
$users += Get-ADUser -Filter $filter -Properties * |
select employeeID, ..., admincount,
#{n='pwdLastSet';e={[DateTime]::FromFileTime($_.pwdLastSet)}}
In this case it's an unnecessary step, though, because Get-ADUser already did that for you and placed that value in the PasswordLastSet property, as #user3815146 already pointed out (+1).
To get an overview of the properties of a user object you can use the Get-Member cmdlet:
Get-ADUser -Filter * -Property * | Get-Member
or list the members of any given user object:
Get-ADUser -Identity 'someuser' -Property * | Format-List *
On a more general note: never append to an array in a loop. The construct
$arr = #()
foreach ($item in $list) {
$arr += Do-StuffWith $item
}
guarantees poor performance, because with each iteration a new array is created (size + 1) and all elements are copied from the old array to the new one. Using a ForEach-Object loop in a pipeline provides far better performance:
$arr = $list | ForEach-Object { Do-StuffWith $_ }
Don't use pwdLastSet, try using PasswordLastSet.
On a different note, have you considered shortening your command to this:
$users += get-aduser -filter $filter -Properties employeeID,sn,givenName,distinguishedName, whencreated,passwordnotrequired,enabled,admincount,passwordlastset