Automating with PowerShell: Creating dynamic distribution groups in all O365 tenants

Someone on the /r/msp reddit, and a Slack that I frequent asked if it is possible to create an “all users” distribution group with PowerShell, and keep it up to date. I figured to spend some time on it.

To achieve a list that is always up to date, we can use Dynamic Distribution groups. Dynamic groups allow you to add specific users based on their properties. for example; if a user has a full mailbox, it will be added. You can make these queries as complex as you want.

Our script uses the Secure Application Model to create a couple of new dynamic distribution groups; a single one that contains all users called “AllCompanyUsers” and one per domain that the client has. The domain group only contains people that use that domain’s email address. For example; contains John, Hank, and Bob. It does not contain Janet because she uses as an e-mail.

The script

$ApplicationId = 'APPLICATIONID'
$ApplicationSecret = 'APPLICATIONSECRET' | Convertto-SecureString -AsPlainText -Force
$RefreshToken = 'Freakishly long refreshtoken'
$ExchangeRefreshToken = 'Freakishly long refresh'
$UPN = "UPN-Of-User-Generating-Tokens"

$credential = New-Object System.Management.Automation.PSCredential($ApplicationId, $ApplicationSecret)

$aadGraphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes '' -ServicePrincipal -Tenant $tenantID 
$graphToken = New-PartnerAccessToken -ApplicationId $ApplicationId -Credential $credential -RefreshToken $refreshToken -Scopes '' -ServicePrincipal -Tenant $tenantID 

Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken
$customers = Get-MsolPartnerContract -All
foreach ($customer in $customers) {
    $token = New-PartnerAccessToken -ApplicationId 'a0c73c16-a7e3-4564-9a95-2bdf47383716'-RefreshToken $ExchangeRefreshToken -Scopes '' -Tenant $customer.TenantId
    $tokenValue = ConvertTo-SecureString "Bearer $($token.AccessToken)" -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential($upn, $tokenValue)
    $customerId = $customer.DefaultDomainName
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "$($customerId)&BasicAuthToOAuthConversion=true" -Credential $credential -Authentication Basic -AllowRedirection
    Import-PSSession $session -AllowClobber -DisableNameChecking -CommandName "Get-DynamicDistributionGroup","New-DynamicDistributionGroup","Set-DynamicDistributionGroup"
    write-host "Logged into tenant $($customer.defaultdomainname)" -ForegroundColor Green
    write-host "Checking if all users list exist, if not, creating them." -ForegroundColor Green
    $ExisitingAllUserList = Get-DynamicDistributionGroup -anr "AllCompanyUsers"

    if (!$ExisitingAllUserList) { 
        write-host "Creating AllCompanyUsers group" -ForegroundColor Green
        New-DynamicDistributionGroup -Name "AllCompanyUsers" -RecipientFilter "(RecipientType -eq 'UserMailbox')" | out-null
        Get-DynamicDistributionGroup -anr "AllCompanyUsers" | Set-DynamicDistributionGroup -RequireSenderAuthenticationEnabled $False -HiddenFromAddressListsEnabled $true | out-null
    write-host "Checking all domains, and creating a list for each domain."  -ForegroundColor Green
    $Domains = Get-MsolDomain -TenantId $customer.TenantId
    foreach ($Domain in $ {
        write-host "Checking domain $($Domain) creating if it does not exist."  -ForegroundColor Green
        $ExisitingDomainList = Get-DynamicDistributionGroup -anr $Domain
        if (!$ExisitingDomainList) {
            write-host "   Creating $domain list"  -ForegroundColor Green
            New-DynamicDistributionGroup -Name "$domain" -RecipientFilter "(RecipientType -eq 'UserMailbox') -and (EmailAddresses -like '$Domain')" | out-null
            Get-DynamicDistributionGroup -anr "$Domain" | Set-DynamicDistributionGroup -RequireSenderAuthenticationEnabled $False -HiddenFromAddressListsEnabled $true | out-null
    Remove-PSSession $session

So, that’s it! as always, Happy PowerShelling!


  1. Pierre May 28, 2020 at 7:43 am

    Should the GET / SET on dynamic groups not be in the IF statement ?
    IF is for create when it does not exists, then updates.
    But as the GET/SET is in the IF, it does not updates the list if it exists .. 🙂

    Am I Wrong ?

    1. Kelvin Tegelaar May 28, 2020 at 9:04 am

      Yes you’re wrong. 🙂 The list only needs to be created once, its a dynamic list so never requires an update, you’ll only need to set it up once.

  2. Pingback: ICYMI: PowerShell Week of 29-May-2020 |

  3. Mircea Mitu October 12, 2020 at 8:56 am

    AAD P1 license is required for all dynamic group members?

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.