I have to check the lastlogon for different users.
My script queries my domain controllers to output my report, however I have an issue.
My report does not come out in descending order. I added sort-object lastlogon -descending, but the dates don't come out correctly in my file. Can you help me?
$data = #()
$DCs = Get-ADDomainController -Filter * | Select-Object -ExpandProperty name
$users =
#'
samaccountname;
user1
user2
'# | ConvertFrom-Csv -Delimiter ';'
foreach ($DC in $DCs) {
foreach($user in $users)
{$data += Get-ADUser $User.samaccountname.Trim() -Properties displayname, userprincipalname, samaccountname, lastlogon -server $DC | Select-Object DisplayName, UserPrincipalName, SamAccountName, Enabled, #{name='LastLogon';expression={[datetime]::fromFileTime($_.lastLogon).ToString('yyyy-MM-dd')}} }
}
$data | Group-Object Lastlogon | Foreach-Object {$_.Group | Sort-Object lastLogon -Descending | Select-Object -Last 10 | Export-Excel "C:\temp\lastlogon ($(Get-Date -Format "yyyy-MM-dd")).xlsx"}
write-host Done! -ForegroundColor Green
It's unclear what you want to accomplish with your script but basically, if you .ToString(..) a DateTime object then Sort-Object will not know how to sort it correctly. Here is how you can approach your code:
$DCs = (Get-ADDomainController -Filter *).Name
$users = #'
samaccountname;
user1
user2
'# | ConvertFrom-Csv -Delimiter ';'
& {
foreach ($DC in $DCs) {
foreach($user in $users) {
$params = #{
Properties = 'displayname', 'lastlogon'
Server = $DC
Identity = $User.samaccountname.Trim()
}
Get-ADUser #params | Select-Object #(
'DisplayName'
'UserPrincipalName'
'SamAccountName'
'Enabled'
#{
Name = 'LastLogon'
Expression = {
[datetime]::fromFileTime($_.LastLogon)
}
}
)
}
}
} | Group-Object { $_.Lastlogon.ToString('yyyy-MM-dd') } | Foreach-Object {
$_.Group | Sort-Object LastLogon -Descending | Select-Object -Last 10 |
Export-Excel "C:\temp\lastlogon ($(Get-Date -Format "yyyy-MM-dd")).xlsx"
}
You also want to avoid adding elements (+=) to a fixed collection (#()).
Related
I need some way to report which users in our AD are having duplicated ProxyAddresses or aliases.
Get-ADUser -filter * -properties proxyaddresses |
Select-Object Name,
#{ L = "proxyAddresses"; E = { ($_.ProxyAddresses -like 'smtp:*') -join ";" } } |
export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
I need only the duplicated AD user, not the whole lot, how can I get that report to . CSV file?
You need to wait before concatening your proxy addresses until you are done working with them.
You can get the duplicates by comparing the count of proxy addresses with the count of unique proxy addresses (Select-Object -Unique). If the count mismatch, then you have some dupe in there. If it is the same, then no duplicates.
Here is an example:
$Users = Get-ADUser -filter * -properties proxyaddresses |
Select-Object Name,
#{ L = "proxyAddresses"; E = { $_.ProxyAddresses -like 'smtp:*' } }
$Dupes = $Users | Where-Object -FilterScript { $_.proxyaddresses.Count -ne ($_.ProxyAddresses | Select-Object -Unique).Count }
$Dupes | Select Name, #{'Name' = 'ProxyAddresses' ; 'Expression' = { $_.proxyAddresses -join ';' } } | export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
Reference dataset used
$Users = #(
[PSCustomObject]#{Name = 'Value'; proxyaddresses = #('SMTP:a#a.com', 'SMTP:a#a.com' ) }
[PSCustomObject]#{Name = 'Bob Value'; proxyaddresses = #('SMTP:a#a.com', 'b#a.com') }
)
Not sure if you want:
Users that have a duplicated address in their proxy list (see answer #SagePourpre), or
All users that have the same proxy addresses in their list as another user (this answer)
Create an index (hashtable) where each proxy address refers to a list of users that own that specific proxy address:
$ADUserByProxy = #{}
Get-ADUser -filter * -properties proxyaddresses |
ForEach-Object {
ForEach ($Proxy in $_.ProxyAddresses) {
if (!$ADUserByProxy.Contains($Proxy)) {
$ADUserByProxy[$Proxy] = [Collections.Generic.List[Object]]::new()
}
$ADUserByProxy[$Proxy].Add($_)
}
}
Than list all the values that contain more then 1 user:
$ADUserByProxy.GetEnumerator() |
Where-Object { $_.Value.Count -gt 1 } |
ForEach-Object { $_.Value } |
Export-csv -Path C:\proxyaddresses.csv -NoTypeInformation
Perhaps not the fastest method, but here's an alternative:
Get-ADUser -Filter * -Properties proxyaddresses | Foreach-Object {
$unique = $_.ProxyAddresses | Select-Object -Unique
$dupes = Compare-object -ReferenceObject $unique -DifferenceObject $_.ProxyAddresses -PassThru
if (#($dupes).Count) {
$_ | Select-Object Name, #{Name = 'DuplicateAddresses'; Expression = {$dupes -join ';'}}
}
} | Export-Csv -Path 'C:\proxyaddresses.csv' -NoTypeInformation
I want to get canonical name for each groups as well. how can I do that ?
Here is my script :
Get-ADUser -Filter {Enabled -eq $true} -Properties * | Select displayname ,#{Name="MemberOf";Expression={($_.MemberOf | %{(Get-ADGroup $_).sAMAccountName}) -Join ";"}} | Export-Csv -Path "c:\temp\users.csv" -NoTypeInformation -Encoding UTF8
My output :
"displayname","MemberOf"
"User01","Group01;Group02;Group03"
My desired output:
"displayname","MemberOf"
"User01","Group01;Contoso.com/OU1/OU2;Group02;Contoso.com/OU21/OU52;Group03;Contoso.com/OU1/OU21/OU22"
I agree with Mathias but this is how you can do it using the code you already have. Definitely recommend you to only call the properties that you need to query.
Get-ADUser -Filter {Enabled -eq $true} -Properties Displayname,MemberOf |
Select-Object Displayname,
#{
Name="MemberOf"
Expression={
# ($_.MemberOf | ForEach-Object{
# (Get-ADGroup $_ -Properties CanonicalName).CanonicalName
# }) -Join ";"
# You can pipe $_.MemberOf to Get-ADGroup, since it's an array of
# distinguishedNames it should work fine
($_.MemberOf | Get-ADGroup -Properties CanonicalName).CanonicalName -Join ";"
}
} | Export-Csv -Path "c:\temp\users.csv" -NoTypeInformation -Encoding UTF8
An alternative to that code, using a more classical approach:
$users = Get-ADUser -Filter {Enabled -eq $true} -Properties DisplayName
$result = foreach($user in $users)
{
$params = #{
LDAPFilter = "(member=$($user.DistinguishedName))"
Properties = "CanonicalName"
}
$membership = (Get-ADGroup #params).CanonicalName -join ";"
[pscustomobject]#{
DisplayName = $user.DisplayName
MemberOf = $membership
}
}
$result | Export-Csv -Path "c:\temp\users.csv" -NoTypeInformation -Encoding UTF8
First of all, you shouldn't be using Properties * when you only need two properties.
Then, the -Filter should be a string, not a scriptblock.
With just a small adaptation to your code, this should work:
Get-ADUser -Filter "Enabled -eq 'True'" -Properties DisplayName, MemberOf |
Select-Object DisplayName,
#{Name = "MemberOf"; Expression = {
($_.MemberOf | ForEach-Object {
($_ | Get-ADGroup -Properties CanonicalName).CanonicalName -Join ";" })
}
} |
Export-Csv -Path "c:\temp\users.csv" -NoTypeInformation -Encoding UTF8
Looking at your latest comment, I believe you want the group NAME joined with the canonicalname of the group as well.
You can do this like so:
Get-ADUser -Filter "Enabled -eq 'True'" -Properties DisplayName, MemberOf |
Select-Object DisplayName,
#{Name = "MemberOf"; Expression = {
$_.MemberOf | ForEach-Object {
$group = $_ | Get-ADGroup -Properties CanonicalName
'{0};{1}' -f $group.Name, ($group.CanonicalName -join ';')
}
}
} |
Export-Csv -Path "c:\temp\users.csv" -NoTypeInformation -Encoding UTF8
I have a list of users (their CN), and I want a list of the groups they are member of.
I already have a code which almost does the trick, but it shows as follows:
User1 - group1;group2
User2 - group1;group2;group3 etc...
Also, groups are shown as distinguished name (with container etc), so very long. I only want the name.
I want to show it as follows:
User1 - group1
User1 - group2
User2 - group1, etc
The code that shows the groups the users are member of, but not in the visual way i like is below:
Import-Csv -Path .\Input_CN.csv |
ForEach-Object {
$User = Get-ADUser -filter "CN -eq '$($_.CN)'" -properties memberof
[PSCustomObject]#{
SourceCN = $_.CN
MemberOf = $User.MemberOf -join ";"
}
} | Export-Csv -Path .\Output.csv -Delimiter ";" -NoTypeInformation
.\Output.csv
I have some other code that list the groups how I want, but I am unable to list it per user. And unable to combine it with the above code.
get-aduser -filter {cn -eq "Testuser"} -properties memberof |
Select -ExpandProperty memberof |
ForEach-Object{Get-ADGroup $_} |
Select -ExpandProperty Name
Thanks in advance :)
You could combine both code pieces like this:
Import-Csv -Path .\Input_CN.csv |
ForEach-Object {
$user = Get-ADUser -Filter "CN -eq '$($_.CN)'" -Properties MemberOf, CN -ErrorAction SilentlyContinue
foreach($group in $user.MemberOf) {
[PSCustomObject]#{
SourceCN = $user.CN
MemberOf = (Get-ADGroup -Identity $group).Name
}
}
} | Export-Csv -Path .\Output.csv -Delimiter ";" -NoTypeInformation
Edit
Although I have never seen an AD user to have no group membership at all (should have at least the default Domain Users in the MemberOf property), You commented that you would like to have a test for that aswell.
Import-Csv -Path .\Input_CN.csv |
ForEach-Object {
$user = Get-ADUser -Filter "CN -eq '$($_.CN)'" -Properties MemberOf, CN -ErrorAction SilentlyContinue
if (!$user) {
Write-Warning "No user found with CN '$($_.CN)'"
# skip this one and resume with the next CN in the list
continue
}
$groups = $user.MemberOf
if (!$groups -or $groups.Count -eq 0) {
[PSCustomObject]#{
SourceCN = $user.CN
MemberOf = 'No Groups'
}
}
else {
foreach($group in $groups) {
[PSCustomObject]#{
SourceCN = $user.CN
MemberOf = (Get-ADGroup -Identity $group).Name
}
}
}
} | Export-Csv -Path .\Output.csv -Delimiter ";" -NoTypeInformation
This is a bit clunky, but you can use nested loops:
Import-Csv -Path .\Input_CN.csv | ForEach-Object {
$user = Get-ADUser -filter "CN -eq '$($_.CN)'" -properties cn, memberof
$user | ForEach-Object {
$_.MemberOf |
ForEach-Object {
[PSCustomObject]#{
SourceCN = $user.CN
MemberOf = $_.split('[=,]')[1]
}
}
}
} | Where-Object {$null -ne $_.MemberOf} |
Export-Csv -Path .\Output.csv -Delimiter ";" -NoTypeInformation
UPDATE: Updated to show only the 'CN' part of the group name and to filter any users who are not a member of any group.
All in one line could be
Get-ADUser -filter {Enabled -eq $True} -Properties Name, Created | Select-Object Name, Created, #{Name="Groups";Expression={Get-ADPrincipalGroupMembership -Identity $_.SamAccountName | Where-Object {$_.GroupCategory -Eq 'Security'} | Join-String -Property Name -Separator ", "}}
I want to read users from different Active Directory groups and then sort and group the results.
From a list like
UserName UserGroup
UZZ GAA
UKK GAA
UZZ GBB
ULL GBB
I want to get that:
Username UserGroup
UKK GAA
ULL GBB
UZZ GAA
So, from User UZZ I want to get only one entry in the list with the first value of UserGroup (first in the alphanumeric sort).
Till now I have the following code:
Import-Module ActiveDirectory
$Groups = (Get-AdGroup -filter * | Where {$_.name -like "G-Q-T*"} | select name -expandproperty name)
$Table = #()
$Record = #{"GroupName" = """Username" = ""}
Foreach ($Group in $Groups) {
$Arrayofmembers = Get-ADGroupMember -identity $Group | select name, samaccountname
foreach ($Member in $Arrayofmembers) {
$Record."GroupName" = $Group
$Record."UserName" = $Member.samaccountname
$objRecord = New-Object PSObject -property $Record
$Table += $objRecord
}
}
$Table | Sort-object -property Username | Group-object -property Username | export-csv "U:\members.csv" -NoTypeInformation**
The part making the list works fine. But not the sort and group part.
Thank you a lot for an answer and help.
Meanwhile I found out, that I have also to add the SID into the .csv File.
The SID is also in the Get-AdGroupMember. But then I try to implement is as the following, the output in case of SID stays empty. What did I wrong where? Thank you in advance for an answer:
Import-Module ActiveDirectory
$Groups = (Get-AdGroup -filter "name -like 'G-Q-T*'" | select name -expandproperty name)
$Table = #()
$Record = #{
"GroupName" = ""
"Username" = ""
"SID" = ""
}
Foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $Group | select name,samaccountname,SID
foreach ($Member in $Arrayofmembers)
{
$Record."GroupName" = $Group
$Record."UserName" = $Member.samaccountname
$Record."SID" = $Member.SID
$objRecord = New-Object PSObject -property $Record
$Table += $objRecord
}
}
$Table | Group-Object -Property Username |
Select-Object #{n="UserName";e={$_.Name}} , #{n="GroupName";e={$_.Group | Sort-Object GroupName | Select-Object -First 1 -ExpandProperty GroupName}} , #{n="SID";e={$_.SID | Sort-Object SID | Select-Object -First 1 -ExpandProperty SID}}| Export-Csv "U:\member.csv" -NoTypeInformation
I would group on username and use calculated properties to create the desired result. Sort the groupnames in the group and pick out the first value. Try to replace your last line with:
$Table | Group-Object -Property Username |
Select-Object #{n="UserName";e={$_.Name}}, #{n="GroupName";e={$_.Group | Sort-Object GroupName | Select-Object -First 1 -ExpandProperty GroupName}} |
Export-Csv "U:\members.csv" -NoTypeInformation
Avoid -Filter * as it retrieves every group. Use it to get only the groups you need
$Groups = Get-ADGroup -Filter "name -like 'G-Q-T*'"
Alternative using the famous pipeline:
Get-ADGroup -Filter "name -like 'G-Q-T*'" | ForEach-Object {
$groupname = $_.Name
$_ | Get-ADGroupMember | ForEach-Object {
New-Object -TypeName psobject -Property #{
UserName = $_.SamAccountName
SID = $_.SID
GroupName = $groupname
}
}
} | Group-Object -Property UserName |
Select-Object #{n="UserName";e={$_.Name}}, #{n="SID";e={$_.Group[0].SID}}, #{n="GroupName";e={$_.Group | Sort-Object GroupName | Select-Object -First 1 -ExpandProperty GroupName}} |
Export-Csv "U:\members.csv" -NoTypeInformation
I have a list of users and I want to export their group names, sorted A-Z. The following script is not working in the inner ForEach-Object loop.
Get-Content users.txt | ForEach-Object {
$user = $_;
Get-ADUser –Identity $user –Properties MemberOf | Select-Object -ExpandProperty MemberOf | sort
ForEach-Object {
New-Object PSObject -property #{User=$user;Group=$_;}
}
} | Export-Csv -Path 'your_file_path.csv' -NoTypeInformation
The ForEach-Object is just sitting out there alone--you have to either pipe an object to it, or assign the object to a variable and use foreach to loop through the object instead. I took the second approach below as excessive piping makes scripts difficult to read (for me).
Get-Content c:\temp\users.txt | ForEach-Object {
$user = $_;
$AdUser = Get-ADUser –Identity $user –Properties MemberOf | Select-Object -ExpandProperty MemberOf | get-adgroup | select -ExpandProperty Name | sort
foreach($group in $AdUser) {
New-Object PSObject -property #{User=$user;Group=$group;} | Export-Csv -Path 'c:\temp\out.csv' -NoTypeInformation -Append
}
}
...and if you want to pipe it and use ForEach-Object, you just need to put a pipe after the sort and move the Export-Csvso that it exports the new object that was created:
Get-Content c:\temp\users.txt | ForEach-Object {
$user = $_;
Get-ADUser –Identity $user –Properties MemberOf | Select-Object -ExpandProperty MemberOf | get-adgroup | select -ExpandProperty Name | sort |
ForEach-Object {
New-Object PSObject -property #{User=$user;Group=$_;} | Export-Csv -Path 'c:\temp\out.csv' -NoTypeInformation -Append
}
}