I have a list of AD security groups contained in a .csv file, in the following format:
Groups,
Groupname1,
Groupname2,
Groupname3,
What I want to do is feed this .csv file to a PowerShell script, so it can report on the group membership of the groups listed in the .csv file (I don't care about recursive groups, as I don't have any). The script should then dump the results to a further .csv file.
So, ideally, I want the final .csv produced output from the scripts to be something like.
Groupname1, name
Groupname1, name
Groupname2, name
Groupname2, name
Groupname3, name
Groupname3, name
You get the idea.
What I am struggling with is getting some sort of for loop going, to look through all the groups from the .csv and output the results as shown above (groupname and then user).
$list = Import-Csv -Path C:\temp\ADGroups.csv
foreach ($groups in $list) {
$ADGroup = $groups.groupname
Get-ADGroupmember -Identity $ADGroup | Get-ADUser -Property Name
Export-Csv -Path "c:\temp\dump.csv"
}
I've seen other suggestions (see example here), but these don't read the groups from a .csv file.
You need to add the group name to the output. I would do this with a calculated property. You also need to move the Export-Csv outside the loop or use -Append. If not, the groups will overwrite the file every time and it will only contain the results from the last group. Try this:
Import-Csv -Path C:\temp\ADGroups.csv | Foreach-Object {
$ADGroup = $_.Groups
Get-ADGroupmember -identity $ADGroup | Select-Objects #{n="Groupname";e={$ADGroup}}, Name
} | Export-CSV -Path "c:\temp\dump.csv" -NoTypeInformation
Related
A bit of background on this.
I have approx 900 user accounts in active directory that are disabled and need to be deleted. They all have mailboxes attached, and to prevent these email addresses being recycled and reused for new users, I want to add each email address, along with any email aliases to a distribution group that essentially won't go anywhere.
I have a list of the FQDNs off all 900 users, and I have created a script in Powershell that will take my list, and then it will do a "for each" loop for each user. The script gathers their email addresses and then exports them to a CSV file.
The end goal is to have a long list of email addresses so that I can add them to the distribution group.
Because the emailaddresses attribute has multiple values, I'm finding that when it exports it to excel, it exports each email address to the same line, currently separated by a semicolon.
What I am looking to achieve, is for each individual email address to be listed on a brand new line on the export. Is this possible?
# Get list of users that we want to delete.
$Users = Get-Content "c:\temp\UsersToDelete.txt"
# For each user - find their email addresses and append them to a CSV.
foreach ($User in $Users) {
Get-Mailbox -Identity "$User" | select #{Name=’emailaddresses’;Expression={$_.emailaddresses -join “;”}} | export-csv -append "c:\temp\CollectedEmailAddresses.csv"
}
You would need an inner loop for each value in the .emailaddresses property:
Get-Content "c:\temp\UsersToDelete.txt" | ForEach-Object {
foreach($email in (Get-Mailbox -Identity $_).emailaddresses) {
[pscustomobject]#{
User = $_
Email = $email
}
}
} | Export-Csv "c:\temp\CollectedEmailAddresses.csv" -NoTypeInformation
Also your code would be more efficient moving Export-Csv as the last statement in your pipeline and removing -Append assuming this is a new file. Appending a CSV line per loop iteration is inefficient and only slowing down your script.
I got user information from the user group in AD. every column has no problem except the user name.
On csv, User name is normal but there is a format when I get content from csv for using powershell like as below;
#{Name=abc}
for compare-object with two CSV, I need to use -expand.
Is there anyway to avoid this result?
I want to get a same content on CSV and powershell.
get-adgroup $path -server server.com | get-adgroupmember -recursive | select-object -unique | get-aduser -properties mail | name, mail | export-csv c:\result.csv
Use import-csv cmdlet to import the csv and not get-content. Also the provided code sample won't work - e.g. you missed select-object here:
| name, mail |
You do not need to query the group, as you already know the name ($path), you can directly query the groupmemberships, e.g.:
get-adgroupmember -identity $path -recursive
But in the end you could achieve the same in a much more efficient way, e.g.:
get-aduser -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=[groupDistinguishedName])" -property mail | select-object -property mail,name | export-csv [path]
replace [groupDistinguishedName] with the distinnguishedName of the group. This will give you all users back which are member (transitive) of the defined group.
see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/4e638665-f466-4597-93c4-12f2ebfabab5
I'm attempting to extract licencing information via o365 with the current script:
Foreach($User in $Users){
$UPN = $User.UserPrincipalName
Get-MsolUser -UserPrincipalName "$UPN" | Select-Object DisplayName,Licenses | Export-Csv -Append -Path c:\test\$exportpath.csv -NoTypeInformation
}
I'm noticing I'm getting two different outputs, whenever I run this via ISE I'll get the following:
However, when I look at the exported CSV I get the following:
So my question is how exactly should I set this up so when I extract information and export it into a CSV that it'll capture all information and not simply state it as a generic list?
Your license field is actually a list of multiple licenses and you need to 'expand' this out before you export to csv. You should be able to do this with a join function or you can iterate through foreach loop.
Get-MsolUser -All | Select-Object office,#{name="licenses";expression={$_.licenses.accountskuid}},userprincipalname | Export-Csv .\licenses.csv -NoTypeInformation
Is it possible to get the permissions of a folder and its sub-folders then display the path, group, and users associated to that group? So, to look something like this. Or will it have to be one folder at a time.
-Folder1
-Line separator
-Group
-Line separator
-List of users
-Folder2
-Line separator
-Group
-Line separator
-List of users
The script I've come up with so far be warned I have very little experience with powershell. (Don't worry my boss knows.)
Param([string]$filePath)
$Version=$PSVersionTable.PSVersion
if ($Version.Major -lt 3) {Throw "Powershell version out of date. Please update powershell." }
Get-ChildItem $filePath -Recurse | Get-Acl | where { $_.Access | where { $_.IsInherited -eq $false } } | select -exp Access | select IdentityReference -Unique | Out-File .\Groups.txt
$Rspaces=(Get-Content .\Groups.txt) -replace 'JAC.*?\\|','' |
Where-Object {$_ -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | ForEach-Object {$_.TrimEnd()}
$Rspaces | Out-File .\Groups.txt
$ErrorActionPreference= 'SilentlyContinue'
$Groups=Get-Content .\Groups.txt
ForEach ($Group in $Groups)
{Write-Host"";$Group;Write-Host --------------
;Get-ADGroupMember -Identity $Group -Recursive | Get-ADUser -Property DisplayName | Select Name}
This only shows the groups and users, but not the path.
Ok, let's take it from the top! Excellent, you actually declare a parameter. What you might want to consider is setting a default value for the parameter. What I would do is use the current directory, which conveniently has an automatic variable $PWD (I believe that's short for PowerShell Working Directory).
Param([string]$filePath = $PWD)
Now if a path is provided it will use that, but if no path is provided it just uses the current folder as a default value.
Version check is fine. I'm pretty sure there's more elegant ways to do it, but I honestly don't have never done any version checking.
Now you are querying AD for each group and user that is found (after some filtering, granted). I would propose that we keep track of groups and members so that we only have to query AD once for each one. It may not save a lot of time, but it'll save some if any group is used more than once. So for that purpose we're going to make an empty hashtable to track groups and their members.
$ADGroups = #{}
Now starts a bad trend... writing to files and then reading those files back in. Outputting to a file is fine, or saving configurations, or something that you'll need again outside of the current PowerShell session, but writing to a file just to read it back into the current session is just a waste. Instead you should either save the results to a variable, or work with them directly. So, rather than getting the folder listing, piping it directly into Get-Acl, and losing the paths we're going to do a ForEach loop on the folders. Mind you, I added the -Directory switch so it will only look at folders and ignore files. This happens at the provider level, so you will get much faster results from Get-ChildItem this way.
ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){
Now, you wanted to output the path of the folder, and a line. That's easy enough now that we aren't ditching the folder object:
$Folder.FullName
'-'*$Folder.FullName.Length
Next we get the ACLs for the current folder:
$ACLs = Get-Acl -Path $Folder.FullName
And here's where things get complicated. I'm getting the group names from the ACLs, but I've combined a couple of your Where statements, and also added a check to see if it is an Allow rule (because including Deny rules in this would just be confusing). I've used ? which is an alias for Where, as well as % which is an alias for ForEach-Object. You can have a natural line brake after a pipe, so I've done that for ease of reading. I included comments on each line for what I'm doing, but if any of it is confusing just let me know what you need clarification on.
$Groups = $ACLs.Access | #Expand the Access property
?{ $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
%{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
Select -Unique #Select only unique values
Now we'll loop through the groups, starting off by outputting the group name and a line.
ForEach ($Group in $Groups){
$Group
'-'*$Group.Length
For each group I'll see if we already know who's in it by checking the list of keys on the hashtable. If we don't find the group there we'll query AD and add the group as a key, and the members as the associated value.
If($ADGroups.Keys -notcontains $Group){
$Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
$ADGroups.Add($Group,$Members)
}
Now that we're sure that we have the group members we will display them.
$ADGroups[$Group]
We can close the ForEach loop pertaining to groups, and since this is the end of the loop for the current folder we'll add a blank line to the output, and close that loop as well
}
"`n"
}
So I wrote this up and then ran it against my C:\temp folder. It did tell me that I need to clean up that folder, but more importantly it showed me that most of the folders don't have any non-inherited permissions, so it would just give me the path with an underline, a blank line, and move to the next folder so I had a ton of things like:
C:\Temp\FolderA
---------------
C:\Temp\FolderB
---------------
C:\Temp\FolderC
---------------
That doesn't seem useful to me. If it is to you then use the lines above as I have them. Personally I chose to get the ACLs, check for groups, and then if there are no groups move to the next folder. The below is the product of that.
Param([string]$filePath = $PWD)
$Version=$PSVersionTable.PSVersion
if ($Version.Major -lt 3) {Throw "Powershell version out of date. Please update powershell." }
#Create an empty hashtable to track groups
$ADGroups = #{}
#Get a recursive list of folders and loop through them
ForEach($Folder in (Get-ChildItem $filePath -Recurse -Directory)){
# Get ACLs for the folder
$ACLs = Get-Acl -Path $Folder.FullName
#Do a bunch of filtering to just get AD groups
$Groups = $ACLs |
% Access | #Expand the Access property
where { $_.IsInherited -eq $false -and $_.AccessControlType -eq 'Allow' -and $_.IdentityReference -notmatch 'BUILTIN|NT AUTHORITY|CREATOR|-----|Identity'} | #Only instances that allow access, are not inherited, and aren't a local group or special case
%{$_.IdentityReference -replace 'JAC.*?\\'} | #Expand the IdentityReference property, and replace anything that starts with JAC all the way to the first backslash (likely domain name trimming)
Select -Unique #Select only unique values
#If there are no groups to display for this folder move to the next folder
If($Groups.Count -eq 0){Continue}
#Display Folder Path
$Folder.FullName
#Put a dashed line under the folder path (using the length of the folder path for the length of the line, just to look nice)
'-'*$Folder.FullName.Length
#Loop through each group and display its name and users
ForEach ($Group in $Groups){
#Display the group name
$Group
#Add a line under the group name
'-'*$Group.Length
#Check if we already have this group, and if not get the group from AD
If($ADGroups.Keys -notcontains $Group){
$Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name
$ADGroups.Add($Group,$Members)
}
#Display the group members
$ADGroups[$Group]
}
#output a blank line, for some seperation between folders
"`n"
}
I have managed to get this working for me.
I edited the below section to show the Name and Username of the user.
$Members = Get-ADGroupMember $Group -Recursive -ErrorAction Ignore | % Name | Get-ADUser -Property DisplayName | Select-Object DisplayName,Name | Sort-Object DisplayName
This works really well, but would there be a way to get it to stop listing the same group access if it's repeated down the folder structure?
For example, "\example1\example2" was assigned a group called "group1" and we had the following folder structure:
\\example1\example2\folder1
\\example1\example2\folder2
\\example1\example2\folder1\randomfolder
\\example1\example2\folder2\anotherrandomfolder
All the folders are assigned the group "group1", and the current code will list each directory's group and users, even though it's the same. Would it be possible to get it to only list the group and users once if it's repeated down the directory structure?
The -notcontains doesn't seem to work for me
If that makes sense?
I've been trying for hours to create a script that outputs the members within each OU and saves that list of members to a document with the title of the OU.
Here's what I have so far:
$COU = Import-CSV C:\Users\hp\COU.csv foreach ($OU in $COU) {Get-ADUser
-filter * -searchbase "OU=$($COU.OU),OU=TestOU,DC=DOMAIN,DC=NET"}
| Foreach ($_) {Export-CSV "C:\Users\hp\$($COU.OU).csv"}
I keep getting errors that the directory cannot be found, no matter what formatting I use when attempting to get the info from the OUs I get those errors, and I get even more when attempting the export.
The CSV is formatted like so:
OU
FirstOU
SecondOU
ThirdOU
...
Getting the list of users does work without the variable if I just type the name of the OU and if I "Write-Output" the variable I get the names of the OUs so I'm guess the issue relates to how to properly insert the variable into the string.
This is my first post so apologies for any formatting errors, please let me know and I can correct them.
I also though instead of importing the CSV I could use the following command:
Get-ADObject -LDAPfilter "(ou=*)" -searchbase "OU=TestOU,DC=DOMAIN,DC=NET"
This one's simple. You define your ForEach loop to be ForEach($OU in $COU) but then within the loop you continue to reference $COU instead of $OU. This could be simplified even more though by simply expanding the OU property when you import the CSV, and working with an array of strings instead.
$COU = Import-CSV C:\Users\hp\COU.csv | Select -Expand OU
ForEach ($OU in $COU){
Get-ADUser -filter * -searchbase "OU=$OU,OU=TestOU,DC=DOMAIN,DC=NET" | Export-CSV "C:\Users\hp\OU.csv" -NoType
}