I am trying to search a list of AD security groups and create a report of users in each security group. The report should have the Group Name, Name, UserName and UPN or Email Address.
I found some code that will help me with a majority of this. I need to modify it to display UPN or email address. Also I need to have it recursively search any groups. Currently the major issue I am tackling is displaying all of the information in the security membership object.
$Group = (Get-Content -Path C:\Users\myusername\Documents\test\list.txt)
$Table = #()
$Record = [ordered] #{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -Identity -Group|selectname,samaccountname
foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $Group
$Record."Name" = $Member.name
$Record."Username" = $Member.samaccountname
$objRecord = New-Object psobject -Property $Record
$Table += $objrecord
}
}
$Table |export-csv "C:\users\myusername\Documents\securitygroups.csv" -NoTypeInformation
The code is not pulling in all of the objects listed. For example a security group my have 3 users and 1 group listed as members. It looks as thought the script is only displaying the first 2 entries.
Since Get-AdGroupMember does not return userprincipalname or mail, you will need to get that data another way. One way is to call Get-ADUser.
$Record.UserPrincipalName = (Get-ADUser $Member).UserPrincipalName
You can make this slightly more efficient by replacing the New-Object command with the [pscustomobject] type accelerator. Also, you can just output the object in your foreach loop and assign that output to a variable. The way you are doing it (+=) forces PowerShell to expand the variable into memory before doing the reassignment. As the variable stores more and more data, that process becomes increasingly less efficient. The code below reflects the ideas I have mentioned.
$Groups = (Get-Content -Path C:\Users\myusername\Documents\test\list.txt)
$Table = foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -Identity $Group | select name,samaccountname
$Output = foreach ($Member in $Arrayofmembers)
{
[pscustomobject]#{
"Group Name" = $Group
"Name" = $Member.name
"Username" = $Member.samaccountname
"UserPrincipalName" = (Get-ADUser $Member.samaccountname).UserPrincipalName
}
}
$Output
}
$Table | export-csv "C:\users\myusername\Documents\securitygroups.csv" -NoTypeInformation
Related
Good Morning World
I have written the below Powershell Script which gives me almost what I want.
The error is that this script gives me an incorrect value for the total of members (users).
The incorrect value of members is always 0 (zero).
I need to the number of members (users) PER group.
I welcome your help.
$Groups = (Get-AdGroup -filter 'Name -notlike "Domain Computers"' | select name -expandproperty name)
$Table = #()
$Record = [ordered]#{
"Group Name" = ""
"Name" = ""
"Username" = ""
"Membercount" = 0
}
Foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $Group | select name,samaccountname
foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $Group
$Record."Name" = $Member.name
$Record."UserName" = $Member.samaccountname
$Record."Membercount" = $Membercount.count
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
}
$Table | export-csv C:\MB\GROUP-D.CSV -NoTypeInformation
$Arrayofmembers.Count is the correct parameter. Thanks to
Santiago Squarzon
I'm trying to pull a report using PowerShell and export it as CSV. I want to grab an AD Group, get the "Members" and "Members Of", then export it.
The final export, I want it to look like this:
Group Name Member Of Member ID Member Name Member Email
Finance Group AD Group 1 User 1 John Smith JSmith#example.com
Finance Group AD Group 2 User 2 Ryan Smith RSmith#example.com
Finance Group AD Group 3 User 3 Amanda Smith ASmith#example.com
Finance Group AD Group 4
Finance Group AD Group 5
I have the following script to get the AD Group Members and Member Of:
$Groups = "Finance Group"
$Object = New-Object PSObject
ForEach ($Group in $Groups) {
$MemberOf = Get-ADPrincipalGroupMembership -Identity $Group
ForEach ($Access in $MemberOf.Name) {
$Object | Add-Member -NotePropertyName "Group Name" -NotePropertyValue $Group
$Object | Add-Member -NotePropertyName "Member Of" -NotePropertyValue "$MemberOf
}
}
I'm just trying to make the first 2 columns, however, Add-Member seems to just replace the current values, and I can't seem to find a way to append the values. Afterwards I will try to add in the users information columns. The reason I want the "Group Name" to repeat is because I want to use a Pivot Table to group "Finance Group" to its respective "Member Of" and "Members". Am I going about this the right way or is there some better way to do this?
Thanks in advance.
Following Abraham's helpful answer which just needs a slight modification to get the user's DisplayName and Mail properties:
$Groups = "Finance Group"
$export = foreach ($Group in $Groups)
{
$thisGroup = Get-ADGroup $Group -Properties MemberOf
$memberOf = $thisGroup.MemberOf
$member = #(Get-ADGroupMember $group).where({
$_.objectClass -eq 'user'
}) | Get-ADuser -Properties DisplayName, mail
$max = [Math]::Max($memberOf.Count, $member.Count)
for ($i = 0; $i -lt $max; $i++)
{
[PSCustomObject]#{
GroupName = $thisGroup.Name
MemberOf = $memberOf[$i] -replace '^CN=(.*?)(?<!\\),.*','$1'
MemberID = $member[$i].Name
MemberName = $member[$i].DisplayName
MemberEmail = $member[$i].mail
}
}
}
$export | Export-Csv .... -NoTypeInformation
The use of -replace on MemberOf is because the MemberOf property of AD Group are DistinguishedName and this would get their CN (Common Name). See https://regex101.com/r/jrbwVb/1 for more details.
If I'm not mistaken, this is your intentions:
$Groups = "Finance Group"
foreach ($Group in $Groups)
{
$groupObj = Get-ADGroup -Identity $Group -Properties Members, MemberOf
for ($i = 0; $i -lt [Math]::Max($groupObj.Members, $groupObj.MemberOf; $i++)
{
[PSCustomObject]#{
GroupName = $Group
MemberOf = $groupObj[$i].MemberOf
Members = $groupObj[$i].Members # here you can substitute this field, or add new ones, with a new Get-ADUser call -
# to get the display name or other properties.
}
}
}
. . .as noted in the in-line comment, you can substitute the returned field for a call to AD using Get-ADUser to swap for a display name or other fields instead.
Unfortunately, I do not have AD installed on my computer, nor have access to an AD environment anymore so this was all based off what I though is correct. I believe that Get-ADGroup returns it's membersof property as well; so only one call would be needed in that aspect.
I have 10 security groups all that are called " 'companyname' RDS Users"
I am trying to create a script that does the following: List all the groups and then list all of the members excluding the disabled members, then have it email a csv. I have done the following but cant get the disabled user excluded.
The Script belows shows how far i got but the disabled users show in there which basically means the script is pointless.
$mailServer = ""
$mailFrom = ""
$mailTo = ""
$mailSubject = ""
$file = "somepath\RDSUsers.csv"
Import-Module ActiveDirectory
$US = Get-ADUser -Filter * -Property Enabled |where {$_.Enabled -eq "True"}| FT Name, Enabled -Autosize
$Groups = (Get-AdGroup -filter * | Where {$_.name -like "*RDS Users" -and $_.name -ne "RDS Users"}| select name -expandproperty name)
$Table = #()
$Record = [ordered]#{
"Group Name" = ""
"Name" = ""
"Username" = ""
}
Foreach ($Group in $Groups)
{
$Arrayofmembers = Get-ADGroupMember -identity $Group |select name,samaccountname
foreach ($Member in $Arrayofmembers)
{
$Record."Group Name" = $Group
$Record."Name" = $Member.name
$Record."UserName" = $Member.samaccountname
$objRecord = New-Object PSObject -property $Record
$Table += $objrecord
}
}
if ($Table -eq "RDS Users") {}
$Table
there is usualy a line here that sends the email with excel attachment
The following should produce the output you want in the $Table variable. You can then pipe $Table to one of the format-* commands.
Import-Module ActiveDirectory
$US = Get-ADUser -Filter "Enabled -eq '$true'" -Property Enabled
$Groups = Get-ADGroup -Filter "Name -like '*RDS Users' -and Name -ne 'RDS Users'" |
Select-Object -ExpandProperty Name
$Table = Foreach ($Group in $Groups)
{
try
{
$Arrayofmembers = Get-ADGroupMember -Identity $Group -ErrorAction Stop | Select-Object Name, SamAccountName
$compare = Compare-Object -ReferenceObject $US -DifferenceObject $Arrayofmembers -ExcludeDifferent -IncludeEqual -PassThru -Property SamAccountName -ErrorAction Stop |
Select-Object Name, SamAccountName
$compare | ForEach-Object {
[pscustomobject]#{
"Group Name" = $Group
"Name" = $_.Name
"UserName" = $_.SamAccountName
}
}
}
catch
{
[pscustomobject]#{
"Group Name" = $Group
"Name" = $null
"UserName" = $null
}
Continue
}
}
$Table
Explanation:
The Get-ADGroupMember command will not provide the Enabled property of its returned objects. You will need to feed its output into another command like Get-ADUser for that data. Since you already stored all of the enabled users in $US, we can simply compare $US collection to the results of each Get-ADGroupMember output.
I removed most of the Where-Object commands in favor of using the -Filter parameter on the AD commands. Almost always, the -Filter parameter will be faster especially when you are comparing AD indexed attributes like Name and Enabled.
You do not need to store each output object in a variable unless you are going to further manipulate it. This is why $Record was removed. Instead, all returned objects are stored in the array $Table. I removed the += operator mainly because of its inefficiency when repeatedly building arrays. Also, you can simply set a variable to the output of a foreach loop, which will result in the array you require. Since we created a custom object on each loop iteration and provided the properties at the time of declaration, [ordered] is not required. However, if you create the hash table first and then create a corresponding object, you will potentially need to use [ordered]. As an aside when you are creating custom objects that are involved in a loop, it is usually best practice to create a new object each time. Otherwise, you could unintentionally update values on the wrong objects. Just because you add an object to an array, you can still update its properties after the fact.
The Compare-Object command ties everything together. The -ExcludeDifferent -IncludeEqual parameter combination will only output objects with matching property values. Since we are comparing $Arrayofmembers and $US, that is ideal. The -PassThru switch allows the objects to be returned with all of the properties that were passed into the command. Then you can use the Select-Object command to pick which properties matter to you.
I have the below working script that checks if a large list of users in a CSV file are a member of an AD group and writes the results to results.csv.
Not sure how to convert the script so I can change $group = "InfraLite" to $group = DC .\List_Of_AD_Groups.CSV.
So the script doesn't just return matches for one AD group but so it returns matches for the 80 AD groups contained in the List_of_AD_groups.csv also. Writing a YES/NO for each AD group in a new column in the CSV (or if that's not possible creating a seperate .csv file for each group with results would do also.
I could do this manually by changing the value of $group and export file name, and re-running the script 80 times but must be a quick was with PS to do this?
e.g. results.csv:
NAME AD_GROUP1 AD_GROUP2 AD_GROUP80 etc etc.
user1 yes no yes
user2 no no yes
user3 no yes no
echo "UserName`InfraLite" >> results.csv
$users = GC .\user_list.csv
$group = "InfraLite"
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SAMAccountName
foreach ($user in $users) {
if ($members -contains $user) {
echo "$user $group`tYes" >> results.csv
} else {
echo "$user`tNo" >> results.csv
}
}
I played with this for a while, and I think I found a way to get you exactly what you were after.
I think Ansgar was on the right path, but I couldn't quite get it to do what you were after. He mentioned that he didn't access to an AD environment at the time of writing.
Here is what I came up with:
$UserArray = Get-Content 'C:\Temp\Users.txt'
$GroupArray = Get-Content 'C:\Temp\Groups.txt'
$OutputFile = 'C:\Temp\Something.csv'
# Setting up a hashtable for later use
$UserHash = New-Object -TypeName System.Collections.Hashtable
# Outer loop to add users and membership to UserHash
$UserArray | ForEach-Object{
$UserInfo = Get-ADUser $_ -Properties MemberOf
# Strips the LPAP syntax to just the SAMAccountName of the group
$Memberships = $UserInfo.MemberOf | ForEach-Object{
($_.Split(',')[0]).replace('CN=','')
}
#Adding the User=Membership pair to the Hash
$UserHash.Add($_,$Memberships)
}
# Outer loop to create an object per user
$Results = $UserArray | ForEach-Object{
# First create a simple object
$User = New-Object -TypeName PSCustomObject -Property #{
Name = $_
}
# Dynamically add members to the object, based on the $GroupArray
$GroupArray | ForEach-Object {
#Checking $UserHash to see if group shows up in user's membership list
$UserIsMember = $UserHash.($User.Name) -contains $_
#Adding property to object, and value
$User | Add-Member -MemberType NoteProperty -Name $_ -Value $UserIsMember
}
#Returning the object to the variable
Return $User
}
#Convert the objects to a CSV, then output them
$Results | ConvertTo-CSV -NoTypeInformation | Out-File $OutputFile
Hopefully that all makes sense. I commented as much of it as I could. It would be very simple to convert to using ADSI if you didn't have RSAT installed on whatever machine you're running this on. If you need that let me know, and I'll make some quick modifications.
I've also tossed a slightly modified version of this in a Gist for later reference.
The trivial solution to your problem would be to wrap your existing code in another loop and create an output file for each group:
$groups = Get-Content 'C:\groups.txt'
foreach ($group in $groups) {
$members = Get-ADGroupMember ...
...
}
A more elegant approach would be to create a group mapping template, clone it for each user, and fill the copy with the user's group memberships. Something like this should work:
$template = #{}
Get-Content 'C:\groups.txt' | ForEach-Object {
$template[$_] = $false
}
$groups = #{}
Get-ADGroup -Filter * | ForEach-Object {
$groups[$_.DistinguishedName] = $_.Name
}
Get-ADUser -Filter * -Properties MemberOf | ForEach-Object {
$groupmap = $template.Clone()
$_.MemberOf |
ForEach-Object { $groups[$_] } |
Where-Object { $groupmap.ContainsKey($_) } |
ForEach-Object { $groupmap[$_] = $true }
New-Object -Type PSObject -Property $groupmap
} | Export-Csv 'C:\user_group_mapping.csv' -NoType
I found this script online. It was original designed to get all members of one security group and if there are nested group it will write to the host the nested group name and members in hierarchy form.
I tweaked it to import AD security groups from a CSV file and to export the results to CSV with table format. CSV files has two security group with both security groups has nested groups. Script will only list the users in the second security group and it doesn't list the nested security group.
CSV File format:
Groupname groupad name
test.testdl office\test.testdl test.testdl
test.testsg office\test.testsg test.testsg
Import-Module ActiveDirectory
$GroupList = #{}
$Table = #()
$Record = #{
"Name" = ""
"nested" = ""
"domain" = ""
"userName" =""
}
function Get-GroupHierarchy {
param()
$searchGroups = Import-Csv -Path C:\temp\ad1.csv
foreach ($item in $searchGroups) {
$groupMember = Get-ADGroupMember -Identity $item.Groupname |
Select-Object name, samaccountname, distinguishedName, objectClass
}
}
foreach ($member in $groupMember) {
$username = $member.samaccountname
$distinguishedName = $member.distinguishedName
$dc = [regex]::Match($distinguishedName,'DC=([^,|$]+)').Groups[1].Value
$domainuser = '{0}\{1}' -f $dc, $username
$Record."userName" = $member.samaccountname
$Record."Name" = $member.name
$Record."nested" = $member.objectclass
$Record."Domain" = $domainuser
$objRecord = New-Object PSObject -Property $Record
$Table += [array]$objrecord
if ($member.ObjectClass -eq "group") {
$GroupList.add($member.name, $member.name)
Get-GroupHierarchy $member.name
}
Get-GroupHierarchy
}
$Table | Export-Csv "C:\temp\SecurityGroups01.csv" -NoTypeInformation
Error message:
Get-ADGroupMember : Cannot validate argument on parameter 'Identity'. The
argument is null or empty. Provide an argument that is not null or empty, and
then try the command again.
At line:1 char:48
+ $groupMember = Get-ADGroupMember -Identity $item.name | Select-Object name, ...
+ ~~~~~~~~~~
I Know it has been ages since you asked this question. But i was working last week on something similar and obtained some results through some work. I saw this question here working on that piece of work and thought to share my work if it can help somebody.
$members = Get-ADGroupMember 'GroupName'
foreach ($member in $members){
if ($member.objectClass -eq 'Group')
{$NestGroupUsers = Get-ADGroupMember $member | select name, objectclass }
Else {
$hash = [pscustomobject]#{
'name' = $member.name
'objectclass' = $member.objectClass
}
$hash | Export-Csv C:\users.csv -Append -NoTypeInformation
}
}
$NestGroupUsers |Export-Csv C:\users.csv -Append -NoTypeInformation