All scripts
Microsoft 365 407

Bulk Create Distribution Groups From a CSV File

Creates Exchange Online distribution groups from a CSV file, including members, so you're not clicking through the same six fields in the admin center for every new group.

New-M365DistributionGroup.ps1
<#
.SYNOPSIS
    Creates Exchange Online distribution groups in bulk from a CSV file.

.DESCRIPTION
    Reads a CSV file where each row describes one distribution group (name,
    alias, primary SMTP address, and a semicolon separated list of member
    email addresses), creates the group if it doesn't already exist, and
    adds the listed members to it. Existing groups are skipped rather than
    recreated, so the same CSV can be run more than once without causing
    duplicates or errors.

    Requires the ExchangeOnlineManagement module and an active connection
    (Connect-ExchangeOnline) before running.

.PARAMETER CsvPath
    Path to the CSV file. Expected columns: Name, Alias, PrimarySmtpAddress,
    Members (a semicolon separated list of member email addresses).

.EXAMPLE
    .\New-M365DistributionGroup.ps1 -CsvPath ".\new-groups.csv"

    Reads new-groups.csv, creates any groups that don't already exist, and
    adds the listed members to each one.

.AUTHOR
    Shehryar Hassan
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true)]
    [string]$CsvPath
)

if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
    Write-Error "The ExchangeOnlineManagement module isn't installed. Run: Install-Module ExchangeOnlineManagement"
    return
}

if (-not (Test-Path $CsvPath)) {
    Write-Error "CSV file not found at path: $CsvPath"
    return
}

$rows = Import-Csv -Path $CsvPath

foreach ($row in $rows) {
    $name = $row.Name.Trim()
    $alias = $row.Alias.Trim()
    $primarySmtp = $row.PrimarySmtpAddress.Trim()

    $memberList = @()
    if ($row.Members) {
        $memberList = $row.Members -split ";" | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
    }

    $existingGroup = Get-DistributionGroup -Identity $primarySmtp -ErrorAction SilentlyContinue

    if ($existingGroup) {
        Write-Host "Group already exists, skipping creation: $name" -ForegroundColor Yellow
    }
    else {
        Write-Host "Creating distribution group: $name"
        New-DistributionGroup -Name $name -Alias $alias -PrimarySmtpAddress $primarySmtp | Out-Null
    }

    $currentMembers = Get-DistributionGroupMember -Identity $primarySmtp -ErrorAction SilentlyContinue

    foreach ($member in $memberList) {
        $alreadyMember = $currentMembers | Where-Object { $_.PrimarySmtpAddress -eq $member }

        if ($alreadyMember) {
            Write-Host "  Already a member: $member"
        }
        else {
            try {
                Add-DistributionGroupMember -Identity $primarySmtp -Member $member -ErrorAction Stop
                Write-Host "  Added member: $member"
            }
            catch {
                Write-Warning "  Could not add $member to $name : $($_.Exception.Message)"
            }
        }
    }
}

Write-Host "Done. Processed $($rows.Count) group definitions from $CsvPath"

Read it before you run it, and test in a safe tenant first.