To bulk add members to a distribution group, run Add-DistributionGroupMember for each group and member pair in a CSV file, after exporting the current members as your backup and skipping anyone already in the group. The cmdlet adds 1 member per call, and it cannot add a Microsoft 365 group or a user who is not mail-enabled. Check first that your account can change groups it does not own, which takes a more privileged role.
| Method | Best fit |
|---|---|
| PowerShell | Many group and member pairs in a CSV file, with a backup of each group's members and a results file. |
| Admin centre | Adding several people to 1 group, 1 group at a time. |
| Altivane | Recommended for bulk changes to items you choose from a list, with every result in a change log. |
Before you start
Microsoft lists the Organization Management and Recipient Management role groups for distribution groups. The script adds -BypassSecurityGroupManagerCheck, so you need not own each group; Microsoft says that switch needs Organization Management or the Security Group Creation and Membership role. Remove it where you own the groups.
The examples change the tenant, so try them on 1 or 2 test groups first. Keep the CSV the script writes before it changes anything: it records who was in each group, and you need it to undo the change.
A distribution group can hold mailboxes, mail users, mail contacts and other mail-enabled groups. A dynamic distribution group has no member list to change.
Option 1
Bulk add members to a distribution group with PowerShell
List the pairs in ./group-members., with a Group column and a Member column, each a name or an email address. These rows are made up.
Group,Member
sales@example.com,alex.morgan@example.com
sales@example.com,sam.lee@example.com
support@example.com,sam.lee@example.comInstall the module once, then run the script. It exports the current members of every group in the file first, then adds each person who is not already in the group. Add-DistributionGroupMember has a -WhatIf switch, so the script previews until you change $preview to $false.
# Install once, if the module is not already installed.
Install-Module ExchangeOnlineManagement -Scope CurrentUser$ErrorActionPreference = 'Stop'
$preview = $true # Change to $false to make the changes.
$rows = @(Import-Csv -Path './group-members.csv')
if ($rows.Count -eq 0) { throw 'group-members.csv has no rows.' }
Connect-ExchangeOnline -ShowBanner:$false
try {
# 1. The backup: today's members of every group in the file, written before any change.
$groups = @($rows.Group | Where-Object { $_ } | ForEach-Object { $_.Trim() } |
Sort-Object -Unique)
$members = @{}
$unread = @{}
$backup = @()
foreach ($name in $groups) {
try {
$found = @(Get-DistributionGroupMember -Identity $name -ResultSize Unlimited)
$members[$name] = @($found | ForEach-Object { "$($_.PrimarySmtpAddress)" })
$backup += $found | Select-Object @{ Name = 'Group'; Expression = { $name } },
Name, PrimarySmtpAddress, RecipientType
} catch {
$unread[$name] = $_.Exception.Message
}
}
$backup | Export-Csv -Path './group-members-before.csv' -NoTypeInformation -Encoding utf8
# 2. Add each person who is not already in the group. One failure does not stop the others.
$results = @(foreach ($row in $rows) {
$name = "$($row.Group)".Trim()
$person = "$($row.Member)".Trim()
try {
if (-not $name -or -not $person) { throw 'The Group or Member cell is blank.' }
if ($unread.ContainsKey($name)) { throw $unread[$name] }
$recipient = @(Get-EXORecipient -Identity $person -ResultSize 2)
if ($recipient.Count -ne 1) { throw "$person does not match exactly 1 recipient." }
$address = "$($recipient[0].PrimarySmtpAddress)"
if ($members[$name] -contains $address) {
$result = 'Already a member'
} else {
$add = @{ Identity = $name; Member = $address; WhatIf = $preview
BypassSecurityGroupManagerCheck = $true }
Add-DistributionGroupMember @add | Out-Null
$members[$name] += $address
$result = if ($preview) { 'Would add' } else { 'Added' }
}
} catch {
$result = "Failed: $($_.Exception.Message)"
}
[pscustomobject]@{ Group = $name; Member = $person; Result = $result }
})
$results | Export-Csv -Path './group-members-results.csv' -NoTypeInformation -Encoding utf8
$failed = @($results | Where-Object { $_.Result -like 'Failed*' }).Count
Write-Output "$($results.Count) rows processed, $failed failed."
} finally {
Disconnect-ExchangeOnline -Confirm:$false
}Run it as it is first: group-members-results. says Would add for each person who would be added, and Already a member for those in the group. Then set $preview to $false and run it again. Only direct members count as already in the group. A row that matches no recipient, or more than 1, is recorded as a failure and the script goes on.
Option 2
Add members to a group in the admin centre
- Open the Exchange admin centre for the correct tenant.
- Go to Recipients > Groups > Distribution list, or Mail-enabled security for a security group.
- Select the group by clicking anywhere in its row, then open the Members tab.
- Under Members, select View all and manage members, then + Add members.
- Select the people to add, several at once if you like, then select Add and close the pane.
Microsoft's steps work on 1 group at a time, so this suits adding several people to 1 group. It cannot add a Microsoft 365 group or a user who is not mail-enabled.
Option 3 ยท Recommended for bulk changes with a record of each result
Bulk add members to groups with Altivane
- Open Altivane and choose the tenant.
- Choose the groups in Altivane's list of them, then the Add members change. In its window, name the people to add, or search the tenant for them. The window shows the groups it applies to and the tenant.
- Run it from that window. For each group Altivane reads the current members and adds each person not already in the group. A group that cannot hold one of the people is refused before anything is added to it. The result for each group is written to the change log.
Every chosen group gets the same people. When each group needs different people, the Add Group Members from a CSV File sample workflow reads a CSV file with Group and Member columns, as above, and writes the result of each row to a CSV file. The change also covers Microsoft 365 groups, which that workflow does not. The change log is a CSV file for the tenant with the time, who signed in, the group, the result and the exact commands sent.
Contact usCommon questions
Why did a row fail?
The Result column says why. The usual causes are a name that matches no recipient or more than 1, a member the group cannot hold, such as a Microsoft 365 group, and an account without permission to change the group.
How do I undo it?
Run Remove-DistributionGroupMember -Identity sales@example. for each row that says Added, or remove the people in the admin centre. In Altivane, run Remove members on the same groups. The backup CSV lists who was in each group before the change.
How do I check the groups afterwards?
Export the members again and compare them with the backup, as in how to export distribution group members.
Microsoft references
Technical references checked on 20 October 2026. The example scripts have been reviewed and syntax checked; they have not been run against a live tenant.