I'm trying to pull out a listing of all groups in our Azure Active Directory org along with all the associated members (be them users, groups, contacts, etc).
Since I was unable to locate a method to do this through the various Microsoft portals with a simple export button I began the process of obtaining access to the Microsoft Graph API/SDK via Powershell.
I'm by no means a PowerShell expert as it's not one of my go-to scripts; however, from what I can tell the ability to pull group info in this fashion is fairly limited.
The following is what I've been able to accomplish thus far:
Pull in a list of the groups using Get-MgGroup -All
Use Get-MgGroupMembers to pull back a list of Directory Objects.
This is where I get stuck. From what I've read it looks like a Directory Object by default only returns the ID and the Deleted Date. I'd like to get a display Name for these objects; I can obviously do this by running the appropriate 'Get' cmdlet for the type of directory object (i.e. Get-MgUser); From what I can tell the type of directory object can't be gleaned via PowerShell with out 'trial-and-error'... This seems highly inefficient to simply get a displayName.
Is there a more effective way to determine either the displayName of a Directory Object via a PowerShell cmdlet or at the very least a type so I can write a case statement to run the right cmdlet on the first try?
For the record this is going to be incorporated in to a Powershell Script, the current iteration of which looks like this and sorta works okay... assuming the Id passed in $member.Id belongs to a User type directory object.
Connect-MgGraph
$groups=Get-mgGroup -All
ForEach ($group in $groups){
$members = #{}
$members = Get-MgGroupMember -GroupId $group.Id -All
ForEach ($member in $members){
$user = Get-MgUser $member.Id
Write-Output $object.ODataType
Write-output $group.DisplayName "," $member.Id "," $user.UserType"," $user.DisplayName "," $user.UserPrincipalName "," $user.Mail >> C:scripts\Azure_Groups.txt
}
}
Would appreciate any direction/assistance on this. Thanks in advance!
Not sure why its not returning all the details on the PowerShell query:
This is working fine in MS Graph Explorer with the results showing all the details of the members:
For more details:https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=http#example-1-get-the-direct-membership-in-a-group
In preparation for a test, we need to load a Windows Server VM with up to 400,000 users and 100,000 groups, and various mappings between them.
A powershell script has been written to achieve this, running on a Server 2012 R2 VM (4 cores, 8GB RAM). However, at the rate the script is running, it's looking like it could take more than a month to complete.
We've tried the script using both the net command and the Add-AD commands to see if there's any speed increase. There doesn't seem to be. The script uses several For loops to iterate through creating users, creating groups, and adding certain users numbers to group numbers.
Command examples were:
#net users $userName mypassword /add
#New-ADUser -Name $userName -SamAccountName $userName -DisplayName $userName -AccountPassword mypassword -Enabled $true
and
net group $groupName $userName /add
#Add-ADGroupMember -Identity $groupName -Members $userName
Any suggestions on the fastest way to load an AD with a mass of new users/groups/mappings?
Thanks
The PowerShell cmdlets for AD are convenient, but they are not efficient.
Using ADSI directly will likely be faster because it gives you more control of what's going on. PowerShell has a shortcut notation of [ADSI]"LDAP://thepath" to create objects (they're technically DirectoryEntry object, but the examples here use the IADs methods).
There are instructions on creating users here, but I can summarize it:
[ADSI]$OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
$new = $OU.Create("user","CN=Ginger Snaps")
$new.put("samaccountname","gsnaps")
$new.setinfo()
#Account is created disabled, so we need to enable and set a password
#(the password can't be set until it's created)
$new.put("userAccountControl",544)
$new.setpassword("P#ssw0rd")
$new.setinfo()
You use $new.put() for whatever other attributes you want to set. You can also create groups this way too, just use "group" instead of "user" in the Create() method.
This is still going to take a while. It's the network connections that will hurt you the most. So you have to:
Get as physically close to a DC as you can (run it on a DC if you can), and
Keep the number of network requests down
If you do run this on a DC, then (if the domain has more than one DC) make sure to target the DC that you're on. You can do that by injecting the DC name into the LDAP:// strings, like this:
"LDAP://dc1.domain.com/OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
Number 2 is limited by the fact that you have to do 2 requests per new user (one to create, one to set password). But you can do other things to keep the number down, like create all the users first and store the distinguishedName of each new user, which you can calculate yourself (rather than asking AD for it) because it's the CN=user that you pass to Create() plus the OU. So for the example above, the DN of the new user is:
CN=Ginger Snaps,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local
Once you have all those, you can create the groups and add all the members in one go. For example:
[ADSI]$OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
$new = $OU.Create("group","CN=group1")
$new.put("samaccountname","group1")
$members = #("CN=Ginger Snaps,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local", `
"CN=Another User,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local")
$new.put("member", $members)
$new.setinfo()
Where $members is your array of the distinguishedName for each member.
This way you have one network request that creates the whole group with the members already set, rather than one network request for each member.
I am a beginner in PowerShell, so I have been tinkering around it to learn more about its uses. Currently I have a task that requires me to remove a list of chosen groups from a user.
The user is in a different domain, hence I have used LDAP.
I am able to find that user and bind it to an object using the command below:
[ADSI]$user = "LDAP://CN=xxx,OU=xxx,DC=xxx"*
so I am able to display whatever information required from $user e.g. $user.sAMAccountName, $user.DisplayName it works. Except for the users groups...
I try the command to remove the groups listed in the user based on sAMAccountName; as all the users in that domain is identified easier by sAMAccountName, it doesnt work.
this command was used just to see if I can remove one group first; if $user can be found and read by the command; it doesnt work
remove-adgroupmember "GroupName" $user.sAMAccountName
or when I tried to display the groups instead to see if $user can be read, it also doesnt work,
Get-ADPrincipalGroupMembership [ADSI]$user = "LDAP://CN=xxx,OU=xxx,DC=xxx" |
select name
I have tried to search regarding this issue, but almost all the tips does not involve different domains or used ldap for other domains, it uses the primary domain instead, and I am not sure on how to edit that part.
If anyone can advice me I would be very grateful, thank you =)
Im in a SharePoint environment where:
distribution lists are in use instead of security lists
can not switch to security list
distribution lists are nested
no user profile sync
So to sync my new SharePoint group "something" I was thinking of running a PowerShell script regulary to fetch all the members hierarchically and put them in the SharePoint group, including checks. Unfortunately I cant install the AD add-in so I installed the Quest AD add-in.
update: I noticed that direct syncing took too long. So I have decided to split the solution: the first AD script output all nested distribution groups (Get-QADGroupMember -Indirect -Type 'group'), although this takes as long as getting all users, this feels safer, since probably this doesnt have to been run that much. Then script 2 read the groups and creates a csv file per group, this is certainly not unique, but with tens of thousands of users, this is the safest way. Then script 3 (underneath) reads the csv files with users and update the SharePoint user group. Then script 4 reads again the usergroup and deletes all the one where the date (in notes) is not equal to the most recent date.
<#
.SYNOPSIS
Syncs All Users in AD distribution Group To SharePoint group:
- Adds new Users
- Updates display name, email when changed
- Deleted Users no longer present
#>
# --------------------------------------------------------
# Variables to Change / Set:
# --------------------------------------------------------
$site = "http://abc-def-ghi:7777"
$SPgroup = "MYCOOLSPGROUP"
$ADgroup = "MYADGROUP"
# --------------------------------------------------------
# Add-SSuser : adds a user to Sharepoint Group
# --------------------------------------------------------
function Add-SSUser {
Param( [string]$account,
[string]$displayname,
[string]$email,
[string]$sitecollectionurl,
[string]$group,
[string]$date)
Get-SPWeb -Identity $sitecollectionurl | foreach-object {
$identityClaimWindowsNTAccount = "i:0#.w|$account"
[Microsoft.SharePoint.SPUser] $ssUser =
$_.EnsureUser($identityClaimWindowsNTAccount)
$_.Groups.GetByName($group).AddUser($ssUser)
$_.Groups.GetByName($group).Update()
Set-SPUser -Identity $ssUser -Web $_.Url -Group $group
# Update properties
$ssUser.Email = $email
$ssUser.Name = $displayname
$ssUser.Notes = "[[$date]]"
$ssUser.Update()
#Write-Host $ssUser.Xml
}
}
I'm trying to get a list of the users of 2 different DDL using PS for Exchange. I keep seeing the same code pop up in all my searches (see below). However, the results that are returned are of all the users in my Exchange 2010. So what am I doing wrong??
$SRA = Get-DynamicDistributionGroup -Identity "DDL_name"
Get-Recipient -RecipientPreviewFilter $SRA.RecipientFilter