Convert Array value as object property dynamically - powershell

I have list of users and i am trying to see if they belong to specific Azure AD group. At the end i want the result to be something like this.
EmailAddress Group1 Group2
User1#email.com Y N
User2#email.com N Y
User3#email.com Y Y
Here is what i got so far:
#authenticate
Connect-MsolService
$users = "User1#email.com", "User2#email.com", "User3#email.com"
$groupLists = "Group1", "Group2"
#create the object with Email, and group name as property name
$output = New-Object -TypeName psobject
$output | Add-Member -MemberType NoteProperty -Name Email -Value ""
$groupLists | ForEach-Object{
$output | Add-Member -MemberType NoteProperty -Name $_ -Value ""
}
#go through each group and user and update the output array
$userExistsInGroup;
foreach ($groupName in $groupLists) {
#get group info
$group = Get-Msolgroup -All | Where-Object {$_.DisplayName -eq $groupName}
#get all members of the group
$members = Get-MsolGroupMember -GroupObjectId $group.ObjectId | Select-Object -ExpandProperty EmailAddress
foreach ($user in $users) {
If ($members -contains $user) {
$userExistsInGroup; = "Y"
} Else {
$userExistsInGroup = "N"
}
# update Email and group property in $output object
......
}
}
Need help updating $output object so that i can display the result the way i want it on the top?
since the same user might show up in different group during loop, if there is existing user in the object, then it will need to update property of the same user that matches with the group so that at the end each row output belongs to one user similar to what i have on the top.

To accommodate a dynamic list of groups, you can use this approach.
$userlist = "User1#email.com", "User2#email.com", "User3#email.com"
$grouplist = "Managers","Directors","Information Technology"
$grouphash = #{}
foreach($group in $grouplist)
{
$grouphash[$group] = Get-MsolGroupMember -GroupObjectId (Get-Msolgroup | Where-Object {$_.DisplayName -eq $group}).objectid
}
foreach($user in $userlist)
{
$userhash = [ordered]#{
EmailAddress = $user
}
$grouplist | ForEach-Object {
$userhash.add($_,($user -in $grouphash[$_].emailaddress))
}
[PSCustomObject]$userhash
}
Each group name will be the property for that group containing true/false if the user is a member.
To collect all the output in a variable simply put $variable = in front of the user foreach loop.
Here is what the output looks like in this example

Related

Get AD Group Members from AD using powershell

I need to find AD group members from given groups in csv file as input. the groups contains Users and Groups also. below is sample input data
I wrote the below code. for users I am getting the output (i.e. for the first entry), but for the second one, as they are groups within group, I am not able to fetch the email.
$GroupCollection= Import-csv -Path "C:\Groups.csv"
$Report = #()
Foreach($Group in $GroupCollection){
$MemberGroup=#()
$Group = $Group.'OPE DLs'
if($Group -match '#')
{
$pos = $Group.IndexOf("#")
$leftPart = $Group.Substring(0, $pos)
}
else
{
$leftPart = $Group
}
$MemberGroup = Get-ADGroupMember -identity $leftPart -recursive | Get-ADUser -Properties mail | Select-Object mail
$MemberGroups = ($MemberGroup.mail) -join "`r`n"
if($MemberGroups -ne ""){
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name 'Contract Details' -Value $Group.'Customer subset'
$Out | Add-Member -MemberType noteproperty -Name 'Group Name' -Value $leftPart
$Out | Add-Member -MemberType noteproperty -Name 'Member Groups' -Value $MemberGroups
$Report += $Out
}
}
$Report | Sort-Object Name | FT -AutoSize
$Report | Sort-Object Name | Export-Csv -Path ‘C:\Group-MemberGroups-Report.csv’ -NoTypeInformation
Please let me know how to get the details. Is the approach is fine or there is any other way to do this.
expected output
As commented, if the object returned by Get-ADGroupMember is a group, you won't get results by piping it through to Get-ADUser, because... it is a group, not a user.
You need to loop over the results and depending on what type the object is (group, user or computer) you use either Get-ADUser or Get-ADGroup (not interested in computer objects).
Try
$Report = foreach ($Group in $GroupCollection){
$groupName = ($Group.'OPE DLs' -split '#')[0]
$groupMembers = Get-ADGroupMember -Identity $groupName -Recursive | ForEach-Object {
$adObject = $_
switch ($adObject.objectClass) {
'group' { ($adObject | Get-ADGroup -Properties mail).mail }
'user' { ($adObject | Get-ADUser -Properties EmailAddress).EmailAddress }
}
}
if (#($groupMembers).Count) {
[PsCustomObject]#{
'Group Name' = $groupName
'Contract Details' = $Group.'Customer subset'
'Member Groups' = $groupMembers -join [environment]::NewLine
}
}
}
$Report = $Report | Sort-Object 'Group Name'
$Report | Format-Table -AutoSize
$Report | Export-Csv -Path 'C:\Group-MemberGroups-Report.csv' -NoTypeInformation
Notes:
adding to an array with += is extremely wasteful because the entire array needs to be rebuilt in memory on each iteration
To take the group name as the part left of the # character, I simply use the -split operator and take the first element ([0])
To output an object, I'm using a [PsCustomObject]#{..} construct rather than the old (pre PowerShell 3.0) New-Object PSObject method

Pscustomobject adding dynamic values

I am trying to read group membership of computers from a particular OU and write to a CSV file. The input criteria for the group membership is like if the computer is part of say "admin" and i need the csv file in the below format
---------------------------------------------------------
Computer Group1 Group2 Group3 Group4
ABCD admin1 admin2 admin3 admin4
EFGH admin2 admin3
XYZZ admin1 admin4
--------------------------------------------------------------
but end up like this.
---------------------------------------------------------
Computer Group1 Group2 Group3 Group4
ABCD admin1 admin2 admin3 admin4
EFGH admin2 admin3
XYZZ admin1 admin4
--------------------------------------------------------------
The code is like this
$All_computer = Get-ADComputer -Filter * -Property * -SearchBase $ou -Server $server | Select-object Name,DNSHostName,Description,memberof
$computerExport = $All_computer |
ForEach-Object {
$ComputerName = $_.Name
$Description = $_.description
$DNSHostname = $_.DNSHostName
$memberof = $_.memberof
$groups = $memberof.where{$_ -like "*$unput_group*"}
$Group_member = [pscustomobject]#{
Workstation = $ComputerName
Comp_Description = $Description
DNS_Hostname = $DNSHostname
}
$i = 0
foreach($group in $Groups)
{
$i++
$member = $group.split('=')[1].split(',')[0]
$Group_member | add-member -MemberType NoteProperty -Name "Group$i" -Value $member
}
$Group_member
}
}
$computerExport | export-csv .\Membership_status.csv -NoTypeInformation
What do i need to do to get the group membership to populate to proper column.
Well of course it does. I mean it's doing what you're asking it to do.
You're only adding the number of properties to the custom object that is found from the where object query. I am really struggling to understand what you're trying to do this for but I THINK what you really want is for each object to have all the possible properties but to have null values for those that don't match that particular computer or better yet to use a boolean.
So... Maybe like this:
[string]$GroupSearch = "admin"
$All_computer = Get-ADComputer -Filter * -Property DNSHostName, Description, memberof -SearchBase $ou -Server $server | Select-Object Name, DNSHostName, Description, memberof
$MatchedGroups = $All_Computer.MemberOf | Sort -Unique | ?{$_.Name -match $GroupSearch}
$computerExport = ForEach ($C in $All_computer) {
$Group_member = [pscustomobject]#{
Workstation = $($C.Name)
Comp_Description = $($C.Description)
DNS_Hostname = $($C.DNSHostName)
}
ForEach ($group in $MatchedGroups) {
[string]$GrpName = $($group.split('=')[1].split(',')[0])
If ($C.MemberOf -contains $group) {
$Group_member | Add-Member -MemberType NoteProperty -Name $GrpName -Value $true
} else {
$Group_member | Add-Member -MemberType NoteProperty -Name $GrpName -Value $false
}
}
$Group_member
}
$computerExport | Export-Csv .\Membership_status.csv -NoTypeInformation
If I understand the question, you need to get all computers from a certain OU that are member of group(s) with a similar partial name.
To do that, I would suggest creating an array of computer objects at first with a temporary extra property called 'Groups' in which the group names that match the partial name are stored.
Later, we'll put these in the correct order as new properties called 'Group1', 'Group2' etc.
# the partial groupname to search for
$unput_group = 'admin'
# Get-ADComputer by default already returns these properties:
# DistinguishedName, DNSHostName, Enabled, Name, ObjectClass, ObjectGUID, SamAccountName, SID, UserPrincipalName
# get an array of computer objects that are member of 'admin*' group(s) with the desired properties
# one extra temporary property is added which contains an array of 'admin*' group names
$All_computer = Get-ADComputer -Filter * -Property Description, MemberOf -SearchBase $ou -Server $server |
Where-Object { $_.MemberOf -match $unput_group} |
Select-Object #{Name = 'Workstation'; Expression = {$_.Name}},
#{Name = 'Comp_Description'; Expression = {$_.Description}},
#{Name = 'DNS_Hostname'; Expression = {$_.DNSHostName}},
#{Name = 'Groups'; Expression = { #($_.MemberOf |
Where-Object { $_ -match "CN=($unput_group[^,]+)" } |
ForEach-Object { $matches[1] }) }}
# get all the group names from the computers we have collected and sort unique
$All_Groups = $All_computer.Groups | Sort-Object -Unique
# build a lookup hashtable with property names ('Group1', 'Group2' etc)
$hash = [ordered]#{}
for ($i = 1; $i -le $All_Groups.Count; $i++) {
$hash["Group$i"] = $All_Groups[$i - 1]
}
# now loop through the collection and add the group properties in order
$result = foreach ($computer in $All_computer) {
foreach ($propertyName in $hash.Keys) {
$group = if ($computer.Groups -contains $hash[$propertyName]) { $hash[$propertyName] }
$computer | Add-Member -MemberType NoteProperty -Name $propertyName -Value $group
}
# output the updated object and remove the temporary 'Groups' property
$computer | Select-Object * -ExcludeProperty Groups
}
# finally, save the results to disk
$result | Export-Csv -Path .\Membership_status.csv -NoTypeInformation

Remove duplicate items in object but not all of it

How can I remove duplicates before exporting to a csv?
foreach ($group in $ADGroups) {
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = $user.name
}
#Remove-ADGroupMember -Identity $Group.DistinguishedName -Members $UserInfo.DistinguishedName -Confirm:$false
$group = $null
}
In the above bit of code the result comes out like
aduser | adgroup
x | a
x | s
x | v
The aduser is the same for each group. How can I post the user once
so it comes out like the following without the duplicate user?
aduser | adgroup
x | a
| s
| v
This will create the output as you requested. You'll lose the ability to sort and filter if you plan to work with the output data in Excel but it's your choice.
#Users Group Membership
$ADGroups = Get-ADGroup -Filter *
$groupMembership = foreach ($group in $ADGroups) {
$members = Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' }
foreach ($member in $members) {
[PSCustomObject]#{
ADuser = $member.name
ADGroup = $group.name
}
}
}
#Get Unique Users
$usersUnique = $groupMembership | Select-Object -Property ADuser -Unique | Sort-Object -Property ADuser
#Loop each Unique user and add a new NoteProperty ADuser2. This will be populated with ADuser if its the first occurrence, else blank.
$results = foreach ($user in $usersUnique) {
$firstOccurrence = $true
$userMembership = $groupMembership | Where-Object { $_.ADuser -eq $user.ADuser } | Sort-Object -Property ADGroup
foreach ($membership in $userMembership) {
if ($firstOccurrence) {
$membership | Add-Member -MemberType NoteProperty -Name ADuser2 -Value $user.ADuser -PassThru
$firstOccurrence = $false
}
else {
$membership | Add-Member -MemberType NoteProperty -Name ADuser2 -Value "" -PassThru
}
}
}
#Export to CSV
$results | Sort-Object -Property ADuser, ADGroup | Select-Object ADuser2, ADGroup | Export-Csv -Path C:\support\user-group_membership_report.csv -NoTypeInformation
I would go with something like this:
$i = 0
foreach ($group in $ADGroups) {
if($i -eq 0){
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = $user.name
}
$i = 1
}
else {
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = ""
}
}
}

Powershell to get active user OU, username, and group memberships

I am trying to get a powershell script to export all users in an OU and sub OUs which I can do fine, but when I try to get the user's OU, I get nothing for the OU. I have looked all over online and found a few scripts that pull just the user's OU, but they are a little slow and I can't seem to get them to pull groups or is for pulling from one group instead of listing all users and their groups.
I am trying to export this list and sort by OU so that I can ensure each student is in the proper groups. We have had a few students that were in extra groups and I want a quick and easy look to find those students.
#Student
$Report = #()
#Collect all users
$Users = Get-ADUser -Filter * -SearchBase 'OU=Student,DC=domain,DC=com' -Properties distinguishedname, Name, GivenName, SurName, SamAccountName, UserPrincipalName, MemberOf, Enabled -ResultSetSize $Null
# Use ForEach loop, as we need group membership for every account that is collected.
# MemberOf property of User object has the list of groups and is available in DN format.
Foreach($User in $users){
$UserGroupCollection = $User.MemberOf
#This Array will hold Group Names to which the user belongs.
$UserGroupMembership = #()
#To get the Group Names from DN format we will again use Foreach loop to query every DN and retrieve the Name property of Group.
Foreach($UserGroup in $UserGroupCollection){
$GroupDetails = Get-ADGroup -Identity $UserGroup
#Here we will add each group Name to UserGroupMembership array
$UserGroupMembership += $GroupDetails.Name
}
#As the UserGroupMembership is array we need to join element with ',' as the seperator
$Groups = $UserGroupMembership -join ','
#Creating custom objects
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
$Out | Add-Member -MemberType noteproperty -Name Name -Value $User.Name
$Out | Add-Member -MemberType noteproperty -Name UserName -Value $User.SamAccountName
$Out | Add-Member -MemberType noteproperty -Name Status -Value $User.Enabled
$Out | Add-Member -MemberType noteproperty -Name Groups -Value $Groups
$Report += $Out
}
#Output to screen as well as csv file.
$Report | Sort-Object DistinguishedName | FT -AutoSize
$Report | Sort-Object DistinguishedName | Export-Csv -Path $env:temp\students.csv -NoTypeInformation
There you go, I added some comments to help you understand the thought process.
This should be a lot faster than what you were doing.
The problem while adding your OUs was here:
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
Which should've been:
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value ($user.distinguishedname -replace '^.+?(?<!\\),','')
#Student
$Report = [system.collections.generic.list[pscustomobject]]::new()
# Using Collection.Generic.List instead of System.Array for efficiency
#Collect all users
$Users = Get-ADUser -Filter * -SearchBase 'OU=Student,DC=domain,DC=com' -Properties MemberOf
# -> Use -SearchScope Subtree if you want to go all the way down in OU recursion starting from the 'OU=Student'
# -> distinguishedname, Name, GivenName, SurName, SamAccountName, UserPrincipalName and Enabled are Default Properties
# of Get-ADUser, no need to call them.
# -> -ResultSetSize $Null is default for Get-ADUSer, no need to call it
# Use ForEach loop, as we need group membership for every account that is collected.
# MemberOf property of User object has the list of groups and is available in DN format.
Foreach($User in $users)
{
#This Array will hold Group Names to which the user belongs.
$UserGroupMembership = [system.collections.generic.list[string]]::new()
#To get the Group Names from DN format we will again use Foreach loop to query every DN and retrieve the Name property of Group.
Foreach($UserGroup in $User.MemberOf)
{
# $GroupDetails = Get-ADGroup -Identity $UserGroup
# -> Instead of this, we can do some string manipulation
# which will be a lot faster and give you the same results.
$UserGroupMembership.Add($UserGroup.Split(',OU=')[0].replace('CN=',''))
}
#As the UserGroupMembership is array we need to join element with ',' as the seperator
$Groups = $UserGroupMembership -join ','
#Creating custom objects
<#
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
$Out | Add-Member -MemberType noteproperty -Name Name -Value $User.Name
$Out | Add-Member -MemberType noteproperty -Name UserName -Value $User.SamAccountName
$Out | Add-Member -MemberType noteproperty -Name Status -Value $User.Enabled
$Out | Add-Member -MemberType noteproperty -Name Groups -Value $Groups
$Report += $Out
-> Again, Add-Member is highly inefficient compared to casting PSCustomObject
-> += is evil ( •̀ᴗ•́ )و ̑̑
#>
$Report.Add(
[pscustomobject]#{
OrganizationalUnit = ($user.DistinguishedName -replace '^.+?(?<!\\),','')
Name = $user.Name
UserName = $user.sAMAccountName
Status = $user.Enabled
Membership = $Groups
})
}
#Output to screen as well as csv file.
$Report | Sort-Object OrganizationalUnit | FT -AutoSize
$Report | Sort-Object OrganizationalUnit | Export-Csv -Path $env:temp\students.csv -NoTypeInformation
I don't know how many users you have but every time you += on an array the entire array plus the new element is copied to a completely new array. This is a bad practice and gets exponentially worse with every item added the array. You can avoid this by building the arrays as a loop result or by using dotnet list object with an efficient add() method.
You also look up the same group names repeatedly. I don't know the numbers but it's probably a lot better to put all your groups in a hashtable once and then look them up.
Your question is unclear, but if you want a list of users and their groups, you are going the long way around. You mention the ou but AFAICS there is no org unit used in the code. Do you want the AD ou property or a part of the DN? You don't seem to be using either.
Note that the DN is a string and sorting by DN will just give an alpha string sort which is not helpful. Are your students in separate org units under OU=students ? This is not clear. If so, use the AD canonicalName to sort the list.
No need to include default properties in -property. Splatting is nice.
You should improve your question by indicating what your AD structure looks like and what you think your output should look like.
Also, format your code for readability.
You want something along these lines:
# group hashtable, for efficient name lookup
$groupName = #{}
$ignoredGroups = #( 'AllStudents','AllUsers', 'etc' ) # don't clutter list with these groups
Get-AdGroup -filter '*' | # any restrictions? searchbase, etc
ForEach-Object {
if ( $ignoredGroups -notcontains $_.Name ) {
$groupName[ $_.distinguishedName ] = $_.Name
}
}
# ADsplat, for readability
$AD_Splat = #{
Filter = '*'
SearchBase = 'OU=Student,DC=domain,DC=com'
Properties = 'MemberOf,CanonicalName,sn,givenName'.split(',') # split to array
ResultSetSize = $Null # !? also, there are system limits to size
}
$results = Get-ADUser #ad_splat |
ForEach-Object {
$DN = $_.distinguishedName # do you need this at all?
$CName = $_.canonicalName # for sorting by AD org unit
$XName = $_.sn + ', ' + $_.givenName
if ( $_.Enabled ) { $Enabled = 'Y'} else { $Enabled = '.' }
$groups = (
$_.memberOf |
ForEach-Object { $GroupName[ $_ ] } | # lookup name
where-Object { $_ } | # ignore nulls (when group not in hashtable)
sort-object # consistent ordering between users
) -join ';' # don't use comma, csv conflict
# leave custom object in pipe! This builds the array efficiently.
New-Object PSObject -Property #{
DistinguishedName = $dn
Name = $_.name
XName = $XName
Login = $_.SamAccountName
CName = $CName
Groups = $Groups
}
} | Sort-Object CName # sort the objects by canonical name
$results | format-table
$results | Export-Csv -Path 'c:\temp\usersgroups.csv' -NoTypeInformation

PowerShell: Get all groups a member belongs to, include group type

I have a script that gives me all members of a group with certain desired information. I want this same format but for all groups that a specified username belongs to. I want information about each group, such as group type (ie security, distribution list). How would I do this? I want a different row for each group, with information about each group in the columns.
Add-PSSnapin Quest.ActiveRoles.ADManagement
$myCol = #()
ForEach ($Group in (Get-QADGroup "CN=research,OU=Security,OU=Groups,DC=xxx,DC=com" -GroupType Security))
{
ForEach ($Member in (Get-QADGroupMember $Group -SizeLimit 0))
{
$myObj = "" | Select Group, Type, Member, Email, Username, Department
$myObj.Group = $Group.Name
$myObj.Type = $Group.groupType
$myObj.Member = $Member.Name
$myObj.Email = $Member.Email
$myObj.Department = $Member.Department
$myObj.Username = $Member.sAMAccountName
$myCol += $myObj
}
}
$myCol | Export-Csv -Path "C:\Users\sdevito\Desktop\test.csv" -NoTypeInformation
or. there is this code that i found that does something similar, but each group is in the same row, different column. i cannot figure out how to edit this code to make each group on a new row.
$alist = "Name`tAccountName`tDescription`tEmailAddress`tLastLogonDate`tManager`tTitle`tDepartment`tCompany`twhenCreated`tAcctEnabled`tGroups`n"
$userlist = Get-ADUser sdevito -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,Company,whenCreated,Enabled,MemberOf | Sort-Object -Property Name
$userlist | ForEach-Object {
$grps = $_.MemberOf | Get-ADGroup | ForEach-Object {$_.Name} | Sort-Object
$arec = $_.Name,$_.SamAccountName,$_.Description,$_.EmailAddress,$_LastLogonDate,$_.Manager,$_.Title,$_.Department,$_.Company,$_.whenCreated,$_.Enabled
$aline = ($arec -join "`t") + "`t" + ($grps -join "`t") + "`n"
$alist += $aline
}
$alist | Out-File C:\Users\sdevito\Desktop\testt.csv
How about something like:
#Requires -Version 3.0
Add-PSSnapin Quest.ActiveRoles.ADManagement
function Get-UsersGroups {
[cmdletbinding()]
param (
[Parameter(Position=0,Mandatory)][string]$Identity,
[Parameter(Position=1)][ValidateSet('all','nested','normal')][string]$MemberType
)
$user = Get-QADUser -Identity $Identity
switch ( $MemberType ) {
'all' { $groups = $user.AllMemberOf }
'nested' { $groups = $user.NestedMemberOf }
default { $groups = $user.MemberOf }
}
foreach ( $group in $groups ) {
$groupinfo = Get-QADGroup -Identity $group
$props = [ordered]#{
Group = $groupinfo.Name
Type = $groupinfo.GroupType
Member = $user.Name
Email = $user.Email
Department = $user.Department
Username = $user.sAMAccountName
}
$obj = New-Object -TypeName PSObject -Property $props
$obj
}
}
Get-UsersGroups -Identity bob | Export-Csv -Path "C:\Users\sdevito\Desktop\test.csv" -NoTypeInformation