Share via

Need help keeping certain emails in Distribution Group while add or removing other emails with Azure Automation

Tyler Johnson 120 Reputation points
2026-03-12T18:48:22.56+00:00

Hello,

I have a few administrator emails that were added into each DG and stayed there until I starting using this new code. Now they are not there anymore because they are not in the list the automation runbook is using.

Is there away in the code to designated certain email address to stay(skip over) in the DG while it updates/removes others?

# 1. Connect to Exchange Online (Managed Identity)
Connect-ExchangeOnline -ManagedIdentity -Organization "<domain>"

# 2. Define target Distribution Group identity
$targetGroup = "JohnsonTest2"

# 3. Import members from CSV (current class list)
$memberList = Import-Csv "C:\Test\JohnsonTest.csv"

# 4. Build a list of desired email addresses from the CSV
$desiredMembers = $memberList.UserEmail

# 5. Fetch current members (email addresses) in the DG
$existingMembers = Get-DistributionGroupMember -Identity $targetGroup |
    Select-Object -ExpandProperty PrimarySmtpAddress

# 6. ADD: members that are in CSV but not yet in the group
foreach ($email in $desiredMembers) {
    if ($existingMembers -contains $email) {
        Write-Output "${email} is already a member of $targetGroup, skipping add."
    } else {
        try {
            Add-DistributionGroupMember -Identity $targetGroup -Member $email -ErrorAction Stop
            Write-Output "Successfully added ${email} to $targetGroup"
        } catch {
            Write-Warning "Failed to add ${email}: $($_.Exception.Message)"
        }
    }
}

# 7. REMOVE: members that are in the group but not in the CSV
foreach ($email in $existingMembers) {
    if ($desiredMembers -notcontains $email) {
        try {
            Remove-DistributionGroupMember -Identity $targetGroup -Member $email -Confirm:$false -ErrorAction Stop
            Write-Output "Removed ${email} from $targetGroup because it is no longer in the class list."
        } catch {
            Write-Warning "Failed to remove ${email}: $($_.Exception.Message)"
        }
    }
}

Azure Automation
Azure Automation

An Azure service that is used to automate, configure, and install updates across hybrid environments.

{count} votes

Answer accepted by question author
  1. Q&A Assist
    2026-03-12T18:48:39.5166667+00:00
    AI generated content. This question contains an answer created with AI from Q&A Assist. Learn more

    Define a static list of “protected” admin addresses and exclude them from the removal loop (and optionally ensure they’re always present in the group). For example:

    # 1. Connect to Exchange Online (Managed Identity)
    Connect-ExchangeOnline -ManagedIdentity -Organization "<domain>"
    
    # 2. Define target Distribution Group identity
    $targetGroup = "JohnsonTest2"
    
    # 2a. Define admin addresses that must always stay in the DG
    $protectedAdmins = @(
        "******@contoso.com",
        "******@contoso.com"
    )
    
    # 3. Import members from CSV (current class list)
    $memberList = Import-Csv "C:\Test\JohnsonTest.csv"
    
    # 4. Build a list of desired email addresses from the CSV
    $desiredMembers = $memberList.UserEmail
    
    # 5. Fetch current members (email addresses) in the DG
    $existingMembers = Get-DistributionGroupMember -Identity $targetGroup |
        Select-Object -ExpandProperty PrimarySmtpAddress
    
    # 5a. Ensure protected admins are always in the group
    foreach ($admin in $protectedAdmins) {
        if ($existingMembers -notcontains $admin) {
            try {
                Add-DistributionGroupMember -Identity $targetGroup -Member $admin -ErrorAction Stop
                Write-Output "Ensured protected admin ${admin} is a member of $targetGroup"
            } catch {
                Write-Warning "Failed to add protected admin ${admin}: $($_.Exception.Message)"
            }
        }
    }
    
    # 6. ADD: members that are in CSV but not yet in the group
    foreach ($email in $desiredMembers) {
        if ($existingMembers -contains $email) {
            Write-Output "${email} is already a member of $targetGroup, skipping add."
        } else {
            try {
                Add-DistributionGroupMember -Identity $targetGroup -Member $email -ErrorAction Stop
                Write-Output "Successfully added ${email} to $targetGroup"
            } catch {
                Write-Warning "Failed to add ${email}: $($_.Exception.Message)"
            }
        }
    }
    
    # 7. REMOVE: members that are in the group but not in the CSV,
    #    but never remove protected admins
    foreach ($email in $existingMembers) {
        if ($protectedAdmins -contains $email) {
            Write-Output "${email} is a protected admin, skipping removal."
            continue
        }
    
        if ($desiredMembers -notcontains $email) {
            try {
                Remove-DistributionGroupMember -Identity $targetGroup -Member $email -Confirm:$false -ErrorAction Stop
                Write-Output "Removed ${email} from $targetGroup because it is no longer in the class list."
            } catch {
                Write-Warning "Failed to remove ${email}: $($_.Exception.Message)"
            }
        }
    }
    

    Key changes:

    • $protectedAdmins holds the addresses that must never be removed.
    • Before removal, the script checks if ($protectedAdmins -contains $email) and skips those members.
    • Optionally, step 5a ensures protected admins are always present even if they’re not in the CSV.

    References:

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. Suchitra Suregaunkar 10,425 Reputation points Microsoft External Staff Moderator
    2026-03-12T19:42:38.59+00:00

    Hello Tyler Johnson

    Your Azure Automation runbook updates Distribution Group (DG) membership based on a CSV list. After the runbook runs, some administrator email addresses that were previously in the DG are removed.

    This is happening because your script is enforcing CSV-based desired state. In the REMOVE section, it removes any current DG member that is not present in the CSV. Since your admin addresses are not in the CSV, they are treated as “not desired” and removed by Remove-DistributionGroupMember. The cmdlet removes exactly the member you specify—there is no built-in “protected/permanent member” flag in Exchange Online.

    As a workaround maintain a protected/static list of email addresses inside the script and ensure your REMOVE logic skips those addresses. This keeps admins in the DG while the runbook continues to add/remove other members based on the CSV.

    Add this PowerShell command just before your REMOVE loop, then update the REMOVE logic as shown:

    # List of members that must always stay in the DG
    $protectedMembers = @(
        "******@contoso.com",
        "******@contoso.com"
    )
    
    # REMOVE: members that are in the group but not in the CSV (except protected)
    foreach ($email in $existingMembers) {
        if (($desiredMembers -notcontains $email) -and ($protectedMembers -notcontains $email)) {
            try {
                Remove-DistributionGroupMember -Identity $targetGroup -Member $email -Confirm:$false -ErrorAction Stop
                Write-Output "Removed $email from $targetGroup because it is no longer in the class list."
            } catch {
                Write-Warning "Failed to remove $email: $($_.Exception.Message)"
            }
        } else {
            if ($protectedMembers -contains $email) {
                Write-Output "Skipping protected member: $email"
            }
        }
    }
    
    

    Updated script (with protected members)

    # 1. Connect to Exchange Online (Managed Identity)
    Connect-ExchangeOnline -ManagedIdentity -Organization "<domain>"
    
    # 2. Define target Distribution Group identity
    $targetGroup = "JohnsonTest2"
    
    # 3. Import members from CSV (current class list)
    $memberList = Import-Csv "C:\Test\JohnsonTest.csv"
    
    # 4. Build desired members from CSV (normalize)
    $desiredMembers = $memberList.UserEmail |
        Where-Object { $_ } |
        ForEach-Object { $_.Trim().ToLowerInvariant() } |
        Select-Object -Unique
    
    # 4a. Define protected/static members that must NEVER be removed (normalize)
    $protectedMembers = @(
        "******@contoso.com",
        "******@contoso.com",
        "******@contoso.com"
    ) | ForEach-Object { $_.Trim().ToLowerInvariant() } | Select-Object -Unique
    
    # 4b. Optional: ensure protected members are always included in the "desired" universe
    # (This prevents them from ever being treated as removable and also ensures they're added if missing)
    $effectiveDesired = ($desiredMembers + $protectedMembers) | Select-Object -Unique
    
    # 5. Fetch current DG members (normalize)
    $existingMembers = Get-DistributionGroupMember -Identity $targetGroup |
        Select-Object -ExpandProperty PrimarySmtpAddress |
        ForEach-Object { $_.ToString().Trim().ToLowerInvariant() } |
        Select-Object -Unique
    
    # 6. ADD: members in effective list but not yet in the group
    foreach ($email in $effectiveDesired) {
        if ($existingMembers -contains $email) {
            Write-Output "$email is already a member of $targetGroup, skipping add."
        } else {
            try {
                Add-DistributionGroupMember -Identity $targetGroup -Member $email -ErrorAction Stop
                Write-Output "Successfully added $email to $targetGroup"
            } catch {
                Write-Warning "Failed to add $email: $($_.Exception.Message)"
            }
        }
    }
    
    # 7. REMOVE: members in the group but not in effective list
    # Protected members are safe because they're included in $effectiveDesired
    foreach ($email in $existingMembers) {
        if ($effectiveDesired -notcontains $email) {
            try {
                Remove-DistributionGroupMember -Identity $targetGroup -Member $email -Confirm:$false -ErrorAction Stop
                Write-Output "Removed $email from $targetGroup because it is no longer in the class list."
            } catch {
                Write-Warning "Failed to remove $email: $($_.Exception.Message)"
            }
        } else {
            # Optional logging: show why something was not removed
            if ($protectedMembers -contains $email) {
                Write-Output "$email is protected, skipping removal."
            }
        }
    }
    
    

    This is the supported approach because Exchange Online membership changes are driven by the logic in your automation; the Remove-DistributionGroupMember cmdlet is the supported cmdlet to remove a member.

    Reference: https://learn.microsoft.com/en-us/azure/automation/shared-resources/schedules

    Thanks,

    Suchitra.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.