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.
Related
``
Hi,
I’m new to PowerShell so guidance would be very much appreciated.
I’ve been trying to create a security group report based of Departments.
The idea is that I can use a department variable e.g ‘Accounts’ and filter the specific users.
Get-ADUser -Filter { department -eq $Department -and enabled -eq $true }
The above works great
From there I get all the users security groups but I’m also trying to exclude certain groups from the final output. Now I tried to add filters and exclude before they passed into my foreach but I can’t get anything to work.
Clear-Host
$Department = 'Accounts'
#Get Users from Department Variable
$UsersTable = Get-ADUser -Filter { department -eq $Department -and enabled -eq $true } | Select-Object sAMAccountName
foreach ($user in $UsersTable) {
#Get Group
$Group = Get-ADPrincipalGroupMembership $user.sAMAccountName | Select-Object name,groupscope
Foreach ($G in $Group) {
$obj = [PSCustomObject]#{
'Name' = $user.sAMAccountName
'SG_Name' = $g.name
'Scope' = $g.groupScope
}
$table.Add($obj)
}
}
I’m probably going about this entirely the wrong way.
Departments-> Users -> Groups / Group Membership -> Exclude Certain Groups -> Output
I also wondered if I was using the correct Group cmdlet.
I tried filtering on Get-ADPrincipalGroupMembership but it doesn't seem to work
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
So Here is my code. Essentially this code will be used by a domain admin to run on our terminal server. I lists all of the currently logged in users, and check their individual group membership and then counts members. Easy Peasy.
99% of this works as expected but I am not a code guru by far. I'm having problems getting a proper list of names from Line 4 which uses quser. If I switch to using Line 5 as text the code works as expected.
I can't for the life of me get the output from line 4 into a format I can use in the rest of the code.
Import-Module ActiveDirectory
$calgary = 0
$edmonton = 0
$users = (quser) -replace '\s{2,}', ',' | ConvertFrom-Csv | Select-Object USERNAME
$usersold = "Thomas", "Scott", "jeremy"
$groups = 'Domain Admins'
foreach ($user in $users) {
foreach ($group in $groups) {
$members = Get-ADGroupMember -Identity $group -Recursive | Select -ExpandProperty SamAccountName
If ($members -contains $user) {
$calgary = $calgary + 1
Write-Host "$user is a member of $group"
Write-Host "$group has $calgary logged in users"
} Else {
Write-Host "$user is not a member of $group"
}
}
}
$users.GetType() returns an Array of elements of type PSCustomObject, so this is an object with properties, rather than just a list of strings.
When you do ($user in $users) then each $user is an object with the USERNAME property. So you have two options:
Access the USERNAME in the loop
When you need the username inside the loop, use $user.USERNAME
Get a list of strings rather than objects
Replace line 4 with:
$users = $((quser) -replace '\s{2,}', ',' | ConvertFrom-Csv | Select-Object USERNAME).USERNAME
On line 4, try using:
$users = ((quser) -replace '\s{2,}', ',' | ConvertFrom-Csv | Select-Object USERNAME).username
I think a better way to get a list of logged on users is to use Get-CimInstance to gather the sessions, filter for LogonType 3 (remote logon), and then get the users associated with those logon IDs. Then, since it looks like you want to be able to check multiple groups, I would get the members for each group, and just note if each user is a member of each group. At the end I would output a table of all sessions, including which groups each user is a member of, and how many users each group has logged on.
$LoggedOn = gcim Win32_LoggedOnUser
$GroupNames = 'pkiadmins'
$Groups = #{}
$GroupNames | ForEach-Object { $Groups.Add($_,(Get-ADGroupMember -Identity $_ -Recursive | Select -Expand SamAccountName)) }
$Sessions = gcim Win32_LogonSession -PipelineVariable Session|?{$_.LogonType -eq 3}|%{
$SesUser = $LoggedOn|?{$_.Dependent.LogonId -eq $Session.LogonId}
$SessionOut = [PSCustomObject]#{
Domain = $SesUser.Antecedent.Domain
User = $SesUser.Antecedent.Name
}
ForEach($Group in $GroupNames){
Add-Member -InputObject $SessionOut -NotePropertyName $Group -NotePropertyValue ($SessionOut.User -in $Groups[$Group])
}
If($SessionOut.User -notmatch '\$$'){$SessionOut} #skip computer accounts
}
$Sessions|FT -Auto
ForEach($Group in $GroupNames){
"Group '{0}' has {1} logged in user(s)" -f $Group,([array]($Sessions|?{$_.$Group})).Count
}
I would like to ask if anyone could help me with a script to extract all the AD groups with their members and their email addresses. I'm running the script bellow which I found it in one of the posts which extracts all the AD Groups and their members but I don't know how to include their email addresses also. Thank you very much for your help.
$Groups = Get-ADGroup -Filter * -SearchBase 'OU,OU,OU,OU,OU,DC,DC,DC' #creates a variable with the name Groups and stores all the groups into it
$Results = foreach( $Group in $Groups ){ #looks for members in each group and stores them in Results
Get-ADGroupMember -Identity $Group | foreach {
[pscustomobject]#{
GroupName = $Group.Name
Name = $_.Name
}
}
}
$Results| sort -Property GroupName | Export-Csv -Path c:\temp\groups.csv -NoTypeInformation #stores results in a csv
You'll need to capture the user's email address in your foreach loop, and you'll need to do that by looking up the user properties - a listing of group members only has the member DN and name.
Get-ADGroupMember -Identity $Group | foreach {
$u = get-aduser $_ -properties mail ##plus any other user properties you need
[pscustomobject]#{
GroupName = $Group.Name
Name = $u.Name
Email = $u.mail
}
}
I'm trying to figure out the logic to do something like this:
Query all AD groups in a specific OU
Query all the users in a specific OU
Query all the user's group memberships
If any user belongs to one or more groups in the initial group query, output that information
If any user belongs to none of the groups in the initial group query, also output that information
I've dug around on this site and found a script that works for the most part, but I'm stuck on how I can compare the user's group membership to the original group query that I'm pulling. It looks like I could use the compare-object cmdlet but the parameters don't seem to include anything that would let me keep track of how many groups the two objects have in common.
The code I found online is below:
$groups = Get-ADGroup -Filter * | where {$_.distinguishedname -like "*,OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv"}
$users = Get-ADUser -Filter * | where {$_.distinguishedname -like "*,OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv"}
foreach ( $User in $Users ) {
$userGroups = Get-ADPrincipalGroupMembership $User
if ( $userGroups.Count -gt 1 ) {
"{0} is a member of the following {1} groups:" -f $User.SamAccountName, $userGroups.Count
foreach ( $group in $userGroups ) {
"`t{0}" -f $group.Name
}
} elseif ( $userGroups.Count -lt 1 ) {
"{0} is a member of the following {1} groups:" -f $User.SamAccountName, $userGroups.Count
foreach ( $group in $userGroups ) {
"`t{0}" -f $group.Name
}
}
}
The problem with this is that I don't have a way of comparing the user group names to the names of the group query in line 1. I also can't determine that a user belongs to 1 or more groups from that list. I'm not sure if I can use the same count method.
You can validate that accounts are member of at least one group from your reference list by using Compare-Object:
foreach ( $User in $Users ) {
$userGroups = Get-ADPrincipalGroupMembership $User
if (!(Compare-Object $userGroups $groups -IncludeEqual -ExcludeDifferent)) {
"{0} doesn't belong to any reference group." -f $User.SamAccountName
}
}
Side note: use the -SearchBase parameter instead of filtering the results of Get-ADUser and Get-ADGroup by a wildcard match on the distinguished name:
$groups = Get-ADGroup -Filter * -SearchBase 'OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv' -SearchScope Subtree
$users = Get-ADUser -Filter * -SearchBase 'OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv' -SearchScope Subtree
I ended up doing the following and it works well for what I need. In case anyone is interested, sample code is below:
#gets a list of all groups in a given OU and stores the objects in the $groups variable
$groups = Get-ADGroup -Filter * -SearchBase 'OU=TUNE_TEST_GROUPS,OU=TUNE_TEST,DC=tune,DC=priv' -Properties name | select name
#pipe each group object into a foreach loop and output a string value of the same group name and stores it into the $groups_string variable
$groups_string = $groups | % {$_.name}
#gets a list of all users in a given OU and stores the objects in the $users variable
$users = Get-ADUser -Filter * -SearchBase 'OU=TUNE_TEST_USERS,OU=TUNE_TEST,DC=tune,DC=priv'
$results=#{
"Username" = ""
"Groupname" = ""
}
$table=#()
#iterates through every user in the $users variable and retrieves their group memberships
foreach ($user in $users) {
#selects each group name and stores it in the $groupMembership variable
$groupMembership = Get-ADPrincipalGroupMembership $user | select name
#compares the names of each user's group to the baseline group name.
$groupMembership | foreach ($_) {
#If there is a match add the group name and the username to the $results hash table
if ($groups_string -contains $_.name) {
$results."Groupname" = $_.name
$results."Username" = $user.Name
#create a new PS object and supply the properties of the $results hash table to each object
$objresults = New-Object psobject -Property $results
#add each object to the $table array
$table += $objresults
}
}
}
#display/output the $table array and format it to fit
$table | ft -AutoSize