Export groups and username of a user in Active Directory - powershell

I am trying to find out the Active Directory groups all our active users are in and want to export it to a CSV file. However the following command presents garbage in the related CSV file.
This is my code failing:
Import-Module ActiveDirectory
Get-ADUser -SearchBase "CN=Users,DC=Mycompany,DC=de" -Filter * | where { $_.enabled -eq "true" } | foreach-object {
write-host "User:" $_.Name
Get-ADPrincipalGroupMembership $_.SamAccountName | foreach-object {
write-host "Member of:" $_.name | export-csv "C:\scripts\output\ad-user-with-group-memberhip.csv" -NoTypeInformation -Encoding UTF8
}
}
Any idea what am I doing wrong here?

Write-Host only writes text to the console window. It doesn't output anything useful to pipe through to Export-Csv.
Also, unless you add switch -Append, you should set the Export-Csv cmdlet as last line in the code, otherwise you will overwrite it in every iteration.
Try
with Select-Object
Import-Module ActiveDirectory
Get-ADUser -SearchBase "CN=Users,DC=Mycompany,DC=de" -Filter "Enabled -eq $true" |
Select-Object Name, #{Name = 'Groups'; Expression = {($_ | Get-ADPrincipalGroupMembership).Name -join '; '}} |
Export-Csv -Path "C:\scripts\output\ad-user-with-group-memberhip.csv" -NoTypeInformation -Encoding UTF8
or with ForEach-Object
Import-Module ActiveDirectory
$result = Get-ADUser -SearchBase "CN=Users,DC=Mycompany,DC=de" -Filter "Enabled -eq $true" |
ForEach-Object {
[PsCustomObject]#{
Name = $_.Name
Groups = ($_ | Get-ADPrincipalGroupMembership).Name -join '; '
}
}
$result | Export-Csv -Path "C:\scripts\output\ad-user-with-group-memberhip.csv" -NoTypeInformation -Encoding UTF8

Related

Powershell script Get-AdComputer

Import-CSV -Path C:\Users\*******\Desktop\Powershell\Input.csv |
ForEach {
Get-ADComputer -identity $_.computer -properties * |
select CN, extensionAttribute1, created, Description, DistinguishedName, enabled
} |
export-csv -path C:\Users\*******\Desktop\Powershell\Output.csv -Append -Encoding UTF8 -Delimiter ";"
Hi, how i can change my PS script.
If user from CSV not found then paste NAME;"NOT_FOUND", now my script just skip users that were not found with errors.
What you could do is a try/catch block, so that if an error occurs (when it does not exists) it outputs not found for the value "CN".
Import-CSV -Path C:\Users\*******\Desktop\Powershell\Input.csv | ForEach {
$computer = $_.computer
try{
Get-ADComputer -identity $computer -properties *
}catch{
#{
CN = "$computer not found"
}
}
} | select #{n="CN";e={$_.CN}}, extensionAttribute1, created, Description, DistinguishedName, enabled | Export-Csv -path C:\Users\*******\Desktop\Powershell\Output.csv -Append -Encoding UTF8 -Delimiter ";"
Or as your less readable one-liner:
Import-CSV -Path C:\Users\*******\Desktop\Powershell\Input.csv | ForEach {try{Get-ADComputer -identity $_.computer -properties *}catch{#{CN = "Not found"}}} | select #{n="CN";e={$_.CN}}, extensionAttribute1, created, Description, DistinguishedName, enabled | export-csv -path C:\Users\*******\Desktop\Powershell\Output.csv -Append -Encoding UTF8 -Delimiter ";"
Note that at the select we use an expression. It says name (n)="CN";expression (e)=$_.CN which is the CN and in case of an error it consists of the value "not found" from the catch block. You can also choose to add this expression to more/different values of the select statement if you enrich the object at the catch block. Or use if/else in the expression.

Getting canonical name for each members of the users via powershell

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

Export csv of groups that are not members of some groups

Im exporting some users and groups that "are not member of". But Im having troubles with groups, I did it with users but I don't know how to do the same with group.
In this example I export users that are not members of that group.
$groups = 'GG_LCS_UsersType4', 'GG_LCS_UsersType3', 'GG_LCS_UsersType2', 'GG_LCS_SpecialUsers'
$whereFilter = $groups | Foreach-Object {
$g = (Get-ADGroup -server $domain $_).DistinguishedName
"{0} '{1}'" -f '$_.memberOf -notcontains',$g
}
$whereFilter = [scriptblock]::Create($whereFilter -join " -and ")
$users = (Get-ADUser -server $Domain -filter {objectclass -eq "user"} -properties memberof).where($whereFilter)
$users | Select-Object SamAccountName,Enabled |
Export-Csv "${Domain}_Users_withoutGG.csv" -NoTypeInformation -Encoding UTF8 -Append
So I want the groups too. Could you help me please?
Thank you!
Technically, you should be able to just add a Get-ADGroup command at the end of the posted code and then export the desired data.
$FilteredGroups = (Get-ADGroup -Server $Domain -Filter * -Properties MemberOf).where($whereFilter)
$FilteredGroups | Select-Object SamAccountName |
Export-Csv "groups.csv" -NoTypeInformation -Encoding UTF8 -Append

How to save Active Directory list of all users in all e-mail distributions to .CSV?

I found this example but I am not sure how I can properly save the output to a .csv.
Import-Module ActiveDirectory
$Groups = Get-ADGroup -Filter {GroupCategory -eq "Distribution"} -Properties Members
ForEach ($g in $Groups) {
Write-Host $g.name
Write-Host $g.members `n
}
I have tried something such as:
Import-Module ActiveDirectory
$Groups = Get-ADGroup -Filter {GroupCategory -eq "Distribution"} -Properties Members
ForEach ($g in $Groups) {
$g.name | Export-CSV C:\log.csv -notypeinformation -Append
$g.members | Export-CSV C:\log.csv -notypeinformation -Append
}
It only saves 1 column to the CSV which is called length.
This also makes me remove the 'n at the end of Write-Host $g.members `n
Is there a way that I can grab this data and save it to .csv properly?
UPDATE
With help from TheMadTechnician and this link https://blogs.technet.microsoft.com/heyscriptingguy/2013/07/22/export-user-names-and-proxy-addresses-to-csv-file/ I was able to get closer to what I want.
Import-Module ActiveDirectory
$Groups = Get-ADGroup -Filter {GroupCategory -eq "Distribution"} -Properties Members
ForEach ($g in $Groups) {
$g.name | Export-CSV C:\log.csv -notypeinformation -Append
$g.members | Export-CSV C:\log.csv -notypeinformation -Append
}
$Groups | Select Name,#{L='Members_1'; E={$_.members[0]}}, #{L='Members_2';E={$_.Members[1]}}, #{L='Members_3';E={$_.Members[2]}}, #{L='Members_4';E={$_.Members[3gq]}} | Export-Csv C:\log.csv -notype
This gives me an output of the below in my CSV:
Name Members_1 Members_2 ETC...
NameOfGroup CN=Stormy Daniels,OU=IT,DC=DomainName,DC=com CN=Joe Bob,OU=IT,DC=DomainName,DC=com
Now the list of users can be huge so I would have to continue creating Members_3, Members_4, etc...
I'm not sure if there is a way I can specify all users or loop
#{L='Members_1'; E={$_.members[0]}}
and increment the number until all users are displayed.
I also only need the CN with the name. I don't need the Ou= or Dc=.
Ah this proved harder than I expected - due to the member counting (you have to do a count which can be comparable to integer). I have added a possibility to limit result size as for large queries the active directory produces timeouts.
$limit_result_size = 10
$group_name = Get-ADGroup -Filter {GroupCategory -eq "Distribution"} -Properties Name, Members -ResultSetSize:$limit_result_size | Select-object name
ForEach ($name in $group_name.name) {
If ((![String]::IsNullOrEmpty("$name")) -And ("$name" -notlike 'index')) {
$count_members = Get-ADGroupMember -Identity "$name" | Measure-Object | Select-Object Count
Write-Output "The AD group $name has $($count_members.Count) members.`n"
For($counter = 0; $counter -lt $count_members.Count; $counter++) {
$person = Get-ADGroup -Filter {Name -eq $name} -Properties Name, Members | Select-Object Name, #{N='Members';E={$_.Members[$counter]}}
$person.Members = $person.Members | Select-String 'CN=[0-9a-zA-Z]+' -AllMatches | % { $_.Matches } | % { $_.Value }
$person | export-csv -NoTypeInformation -Append -Path '<your_path>\log.csv'
}
}
}
Short description:
(![String]::IsNullOrEmpty("$name")) -And ("$name" -notlike 'index')) conditions which the AD group should satisfy.
Select-String 'CN=[0-9a-zA-Z]+' -AllMatches | % { $_.Matches } | % { $_.Value } Selects only CN=string_with_numbers. You could replace it with CN=\w+ if you prefer.
The script produces a pair in CV AD group and the CN=user_name. If anything else is unclear please ask.
EDIT
If you have spaces in the names of the Common Names (CN) you have to adjust the regexp to CN=[0-9a-zA-Z\s]+.
EDIT 2 Adding user's email addresses.
Since your question has in the title request for emails I'll answer here without new question. Note that this solution uses lookbehind in regexp to exclude the CN= from the output so it can be used as source for the user query. It also uses a PSCustomObject which gathers all the information together. I have renamed some variables to make better sense in the context of user details.
$limit_result_size = 10
$group_name = Get-ADGroup -Filter {GroupCategory -eq "Distribution"} -Properties Name, Members -ResultSetSize:$limit_result_size | Select-object name
ForEach ($name in $group_name.name) {
If ((![String]::IsNullOrEmpty("$name")) -And ("$name" -notlike 'index')) {
$count_members = Get-ADGroupMember -Identity "$name" | Measure-Object | Select-Object Count
Write-Output "The AD group $name has $($count_members.Count) members.`n"
For($counter = 0; $counter -lt $count_members.Count; $counter++) {
$person = Get-ADGroup -Filter {Name -eq $name} -Properties Name, Members | Select-Object Name, #{N='Members';E={$_.Members[$counter]}}
$person.Members = $person.Members | Select-String '(?<=CN=)[0-9a-zA-Z\s]+' -AllMatches | % { $_.Matches } | % { $_.Value }
$person_details = Get-AdUser -filter {name -eq $member} -Properties mail | Select-Object mail
$person_additional_details = [PSCustomObject]#{ group_name = $group.Name
user_name = $group.Members
email = $person_details.mail
}
If ([String]::IsNullOrEmpty($($person_additional_details.email))) {
$person_additional_details.psobject.properties["email"].value = '<empty>'
}
# For user to see the written data
Write-Output "AD Group: $($person_additional_details.group_name) `
AD User: $($person_additional_details.user_name) `
Users`'s email: $($person_additional_details.email)`n"
# writing into the CSV file
$person_additional_details | export-csv -NoTypeInformation -Append -Path '<your_path>\log.csv'
}
}
}

Get group member names from Get-Acl and Append to CSV

I need to extract all the members of a group and then format that into Names, so I only get the Full Names like "Don Joe" is member of my-domain\UNCShareDrive
I need that to appear in my CSV after I have exported the ACL lists.
As an example:
Path FileSystemRights AccessControlType IdentityReference
---- ---------------- ----------------- -----------------
Microsoft.PowerShell.Core\FileSystem::\\fileshare\management\DK FullControl Allow MY-DOMAIN\Drev Management
Microsoft.PowerShell.Core\FileSystem::\\fileshare\management\ FullControl Allow BUILTIN\Administratorer
Microsoft.PowerShell.Core\FileSystem::\\fileshare\management\ FullControl Allow MY-DOMAIN\Drev Management
Microsoft.PowerShell.Core\FileSystem::\\fileshare\management\ FullControl Allow BUILTIN\Administratorer
**These persons has access to the drive:**
**Finding group: MY-DOMAIN\Administrators**
Name SamAccountName Mail
---- -------------- ----
Administrator Administrator Administrator#my-domain.dk
This is what I have so far done in Powershell:
$Searching = Get-ADGroup -Filter * -SearchBase "DC=my-domain,DC=local"
$mypath = "U:\mytest.csv"
if((Test-Path -Path "U:\mytest.csv" -pathtype Container) -eq $True){
Write-Host -ForegroundColor red "We found a file: $mypath --> REMOVING!`r`n"
Remove-Item $mypath -Force
}
$ShareName = "\\\sharename\D$"
$shares = Get-Childitem -path $ShareName |
Where-Object {$_.PSIsContainer} |
Get-ACL |
Select-Object Path -ExpandProperty Access |
Select Path, FileSystemRights,AccessControlType,IdentityReference |
export-csv $mypath -Delimiter ';' -NoTypeInformation -Encoding UTF8
$foldertotal = 0
Add-Content $mypath ""
Add-Content $mypath ""
Add-Content $mypath "Disse personer har adgang til share navnet"
Add-Content $mypath ""
Add-Content $mypath ""
$myLoop = ''
foreach ($testLoop in $myLoop) {
$Group = Get-ADGroup -Filter * -SearchBase "DC=my-domain,DC=local" -Properties IdentityReference |
Select-Object Name # Need only groups in $Shares - who are displayed
$myLoop += Write-Host -ForegroundColor Yellow "Finding group: $($Group.name).....`n"
$myLoop += ForEach ($Group in $share) {
Get-ADGroupMember -identity $($Group.name) -recursive |
Get-ADUser -Properties Name,SamAccountName,Mail |
Select-Object Name,SamAccountName,Mail
}
$testLoop | Out-File -filePath $mypath -append -encoding utf8
}
Write-Host -ForegroundColor Cyan "$($Group.name) is now exported to: $mypath`n"
I have also a hard time to filter out only the share path:
$shares.Replace('Microsoft.PowerShell.Core\FileSystem::', ' ') |
Out-File -filePath $mypath -append -encoding utf8
How come that this thing Works when I use User Input and not just automatically loop through it without the console prompt:
$Searching = Get-ADGroup -Filter * -SearchBase "DC=MY-DOMAIN,DC=local"
$mypath = "U:\mytest.csv"
$networkPath = "\\ShareName\D$"
$acl = Get-acl -path $networkPath
if((Test-Path -Path "U:\mytest.csv" -pathtype Container) -eq $True){
Write-Host -ForegroundColor red "We found a file: $mypath --> REMOVING!`r`n"
Remove-Item $mypath -Force
}
Write-Host -ForegroundColor Yellow "Eksempel på share: "`r`n\\ShareName\D$`r`n\\ShareTwo\\E$`r`n\\ShareName\management`r`n""
$ShareName = Read-host "Hvilket sharenavn vil du finde Access Control List fra?"
if($ShareName -eq "1"){
$Sharename = "\\ShareName\D$"
}
if($ShareName -eq "2"){
$Sharename = "\\ShareTwo\\E$"
}
if($ShareName -eq "3"){
$Sharename = "\\ShareName\management"
}
Get-Childitem -path $ShareName |
Where-Object {$_.PSIsContainer} |
Get-ACL |
Select-Object Path -ExpandProperty Access |
Select Path, FileSystemRights,AccessControlType,IdentityReference |
FT -AutoSize |
Out-File -Encoding utf8 $mypath
Add-Content $mypath ""
Add-Content $mypath ""
Add-Content $mypath "Disse personer har adgang til share navnet"
Add-Content $mypath ""
$users = get-aduser -Filter {Name -Like "*"} -Searchbase "dc=MY-DOMAIN,dc=local" -Properties MemberOf |
Where-Object { $_.Enabled -eq 'True' }
Write-Host -ForegroundColor red "Example: Drev Management`r`n"
$myGroups = Read-Host "Hvilken Gruppe vil du bruge?"
if($myGroups -eq "1"){
$myGroups = " Drev Management"
}
$Groups = Get-ADGroup -filter {Name -like $myGroups} | Select-Object Name
Add-Content $mypath "Finding group: $($group.name)"
Write-Host -ForegroundColor Yellow "Finding group: $($Group.name).....`n"
Add-Content -Path $mypath $result
ForEach ($Group in $Groups) {
Get-ADGroupMember -identity $($Group.name) -recursive |
Get-ADUser -Properties Name,SamAccountName,Mail |
Select-Object Name,SamAccountName,Mail |
FT -AutoSize |
Out-File -Append -encoding utf8 $mypath
}
Write-Host -ForegroundColor Cyan "$($Group.name) is now exported to: $mypath`n"
Your second code sample works, because it doesn't make the same mistakes as the first one.
$myLoop = ''
foreach ($testLoop in $myLoop) {
$Group = Get-ADGroup -Filter * ...
$myLoop += Write-Host ...
$myLoop += ForEach ($Group in $share) {
Get-ADGroupMember ...
}
$testLoop | Out-File -filePath $mypath -append -encoding utf8
}
You make $myLoop an empty string, then iterate over each element of $myLoop (one empty string), so you get a single loop cycle, no matter what you append to $myLoop inside the loop.
Instead of the group members you write the content of $testLoop (an empty string) to the output file.
You read AD groups into the variable $Group, and then overwrite its content when you use $Group as the loop variable in foreach ($Group in $share).
$share is emtpy, because it's never initialized anywhere. Even $shares is empty, because when you write the ACL information to the CSV, no output is left that could be assigned to the variable.
And as a side-note: you can't append Write-Host output to anything, because the cmdlet writes to the host process (i.e. the console), not a stream that could be captured or redirected.
You need to capture the ACL information in $shares before writing it to a file:
$shares = Get-Childitem -Path $ShareName |
Where-Object {$_.PSIsContainer} |
Get-ACL |
Select-Object Path -ExpandProperty Access |
Select Path, FileSystemRights,AccessControlType,IdentityReference
$shares | Export-Csv $mypath -Delimiter ';' -NoTypeInformation -Encoding UTF8
Then you can determine the members of the groups having access to shares like this:
$shares | Select-Object -Expand IdentityReference |
Select-Object -Expand Value |
ForEach-Object {
$name = $_ -replace '^DOMAIN\\' # <-- replace with actual domain name
Get-ADObject -Filter { Name -eq $name }
} |
Where-Object { $_.ObjectClass -eq 'group' } |
Get-ADGroupMember |
Get-ADUser -Properties * |
Select-Object Name, SamAccountName, Mail |
Out-File $mypath -Append -Encoding UTF8
Note that this will not resolve nested groups, and also won't distinguish between shares.
If you want to store both the group name and its members you need to add a loop to the pipeline:
$shares | Select-Object -Expand IdentityReference |
Select-Object -Expand Value |
ForEach-Object {
$name = $_ -replace '^DOMAIN\\' # <-- replace with actual domain name
Get-ADObject -Filter { Name -eq $name }
} |
Where-Object { $_.ObjectClass -eq 'group' } |
ForEach-Object {
$_
Get-ADGroupMember -Identity $_ |
Get-ADUser -Properties * |
Select-Object Name, SamAccountName, Mail
} |
Out-File $mypath -Append -Encoding UTF8