Scroll Top
Evotec Services sp. z o.o., ul. Drozdów 6, Mikołów, 43-190, Poland

Office365-AddBulkEmailAddressses.ps1

Microsoft Office 365 offers a truly great experience for hosting your e-mails. If you have small company adding domain and multiple different email addresses to every single person is easy enough and shouldn't take much time. However things get a bit complicated if you've got lots of people to handle (even 30 is a lot) and you have to add multiple combinations of email templates for each domain you support.

If you have Exchange On-Premise and Office 365 in Hybrid Scenario you can use On-Premise Exchange to create all your Email Address Policies. If you're just having Office 365 you have to either go thru PowerShell or do things manually. Below you can find a script which is supposed to simplify this a lot. I'll try to update it if there will be some interest.

💡 Script Parameters

Script has multiple customization options that you can use during setup. It takes few parameters such as EmailTemplate, Commit, ExcludeUsers, ShowSummaryTable, ShowSummaryLines.

EmailTemplate – uses known to Microsoft Exchange products usage of %g (given name/first name) and %s (secondary name/last name) variables. So if we want to add all users on Office 365 online a new email template we can use combinations just like in standard Exchange Console. Feel free to explore possibilities. Generally script should cover most uses.

- "%g.%s@company.org.pl"  # would give przemyslaw.klys@company.org.pl
- "%1g.%s@company.org.pl" # would give p.klys@company.org.pl
- "%1g%s@company.org.pl"  # would give pklys@company.org.pl
- "%3g.%s@company.org.pl" # would give prz.klys@company.org.pl
- "%g.%1s@company.org.pl" # would give przemyslaw.k@company.org.pl
- "%g@company.pl"         # would give przemyslaw@company.pl

Commit – $true/$false (default $false) – is required to add new emails to existing users
ExcludeUsers – takes UPN (UserPrincipalName) and allows excluding of users that shouldn't be touched. Takes multiple UPN's. UPN's are also used to find mail accounts and match it with users.
ShowSummaryTable – $true/$false (default $true) – shows summary table
ShowSummaryLines – $true/$false (default $true) – shows summary lines

Additional fields are required to connect to Exchange Online (Connect-Ex function). Those are:

Login – is required to connect to online Exchange, user with administrative rights on Office 365 in form of email address

Password – is required to connect to online Exchange

SessionName – is optional but can be used to distinguish multiple Exchange connections

💡 Script Usage

Usually usage should be something like this::

  • Connect to Exchange Online
  • Add 3 different types of email templates to users
  • Disconnects from Exchange Online

Example output:

Or something different:

Feel free to put your $login and $password directly in text form into script as it should be parsed correctly.

cls
$login = Read-Host "Please enter your login (in form of email)"
$password = Read-Host "Please enter your password"
$sessionName = Read-Host "Please enter session name (leave blank for default)"

Connect-Ex -login $login -Password $password -sessionName $sessionName
Add-EmailAddressToUsersFromTemplate -EmailTemplate "%g.%s@company.org.pl" -Commit $true -ExcludeUsers "company@companykatowice.onmicrosoft.com","kalendarz@company.org.pl"
Add-EmailAddressToUsersFromTemplate -EmailTemplate "%1g.%s@company.org.pl" -ExcludeUsers "company@companykatowice.onmicrosoft.com"
Add-EmailAddressToUsersFromTemplate -EmailTemplate "%1g%s@company.org.pl" -ShowSummaryTable $true -ShowSummaryLines $true -Commit $true -ExcludeUsers "company@companykatowice.onmicrosoft.com","kalendarz@company.org.pl","przemyslaw.klys@companykatowice.onmicrosoft.com"
Disconnect-EX -sessionName $sessionName

💡 Script References

Function Remove-StringLatinCharacters – Non-latin characters in FirstName and LastName are replaced for easy generation of e-mails such us Przemysław Kłys will be converted to Przemyslaw Klys as Exchange wouldn't like it. 

Function Write-Color – allows for nice display of colors on one or multiple lines

Function Connect-Ex – allows to connect to Exchange Online remotely. It also allows to connect to On-Premise Exchange if parameters are given properly.

Function Disconnect-Ex – disconnects Exchange Online (or on-premise version).

💡 PowerShell Script Code

# credit goes to:
# - Mike Pfeiffer for Add-EmailAddress
# - Paul Cunningham for his article and script on Office 365 email address policies (https://practical365.com/exchange-online/office-365-email-address-policies/)
# - Paul Cunningham for Add-SMTPAddresses.ps1 which I've based my script on
# - TessellatingHeckler for help with parsing templates

# Changelog
# ver 0.1 - 02.08.2016 -Initial script
# ver 0.2 - 03.08.2016 - Fix for $null-valued expression in Connect-Ex/Disconnect-Ex 

Function Connect-EX ([string] $login, [string] $password, [string] $URL = "https://ps.outlook.com/powershell", [string] $sessionName = "Exchange Online", $Authentication = "Basic", $SupressWarnings = $true) {
    if ($sessionName -eq $null -or $sessionName.Trim() -eq "") { $sessionName = "Exchange Online" }
    Write-Color -Text "[*] Connecting to $sessionName PowerShell" -Color Yellow
    # Kerberos for On-Premise Exchange on Active Directory joined machines, Basic for Office365 or non-domain joined computer
    $WarningPreference = $SupressWarnings
    $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
    $credentials = New-Object System.Management.Automation.PSCredential ($login, $secpasswd)
	$sessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck 
    if ($SupressWarnings) { $suppress = "SilentlyContinue" } else { $suppress = "Continue" }
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $URL -Credential $Credentials -Authentication $Authentication -AllowRedirection -Name $sessionName -SessionOption $sessionOption -WarningAction $suppress
    $output = Import-PSSession $session -AllowClobber -DisableNameChecking 
    if (-not $SupressWarnings) { $output }
}
Function Disconnect-EX ($sessionName = "Exchange Online") { 
   if ($sessionName -eq $null -or $sessionName.Trim() -eq "") { $sessionName = "Exchange Online" }
	Remove-PSSession -Name $sessionName
	Write-Color -Text "[*] Disconnected from $sessionName" -Color Yellow
}
Function Remove-StringLatinCharacters {
    PARAM ([string]$String)
    [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($String))
}
Function Write-Color([String[]]$Text, [ConsoleColor[]]$Color = "White", [int]$StartTab = 0, [int] $LinesBefore = 0,[int] $LinesAfter = 0) {
    $DefaultColor = $Color[0]
    if ($LinesBefore -ne 0) {  for ($i = 0; $i -lt $LinesBefore; $i++) { Write-Host "`n" -NoNewline } } # Add empty line before
    if ($StartTab -ne 0) {  for ($i = 0; $i -lt $StartTab; $i++) { Write-Host "`t" -NoNewLine } }  # Add TABS before text
    if ($Color.Count -ge $Text.Count) {
        for ($i = 0; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine } 
    } else {
        for ($i = 0; $i -lt $Color.Length ; $i++) { Write-Host $Text[$i] -ForegroundColor $Color[$i] -NoNewLine }
        for ($i = $Color.Length; $i -lt $Text.Length; $i++) { Write-Host $Text[$i] -ForegroundColor $DefaultColor -NoNewLine }
    }
    Write-Host
    if ($LinesAfter -ne 0) {  for ($i = 0; $i -lt $LinesAfter; $i++) { Write-Host "`n" } }  # Add empty line after
}
Function Get-NameSection {
    # Returns the first $num characters of a name
    # unless $num is 0, missing or longer than the name
    # then returns the entire name

    param([string]$name, [int]$num)

    if (-not $num -or $num -gt $name.Length) { 
        $name 
    } else {
        $name.Substring(0, $num)
    }
}
Function Add-EmailAddress {
    param($Identity, $EmailAddress)

    begin {
        $mb = Get-Mailbox $Identity
        if($mb.EmailAddressPolicyEnabled) {
            Set-Mailbox $Identity -EmailAddressPolicyEnabled $false
            $policy += 1
        }
        $addresses = $mb.EmailAddresses += $EmailAddress
    }

    process {
        Set-Mailbox $Identity -EmailAddresses $addresses
    }

    end {
        if($policy) {Set-Mailbox $Identity -EmailAddressPolicyEnabled $true}
    }
}
Function ProcessEmail ($Mailbox, $FullEmail) {
    $RequireAdd = $true
    $addresses = $Mailbox.EmailAddresses
    foreach ($address in $addresses) {
        if ($address -imatch "sip:") { continue }
        if ($address -ireplace("smtp:","") -ieq $FullEmail) {
            #$FullEmail
            $requireAdd = $false
            break
        }
    }
   $Mailbox | Add-Member -MemberType NoteProperty -Name NewEmailToAdd -Value $FullEmail 
   $Mailbox | Add-Member -MemberType NoteProperty -Name NewEmailRequiresAdding -Value $RequireAdd  
   return ,$mailbox
}

Function Add-EmailAddressToUsersFromTemplate([string] $EmailTemplate, [bool] $ShowSummaryTable = $true, [bool] $ShowSummaryLines = $true, [bool] $Commit = $false, [string[]] $ExcludeUsers) {
    $collection = @()

    Get-User -ResultSize Unlimited | Where { $_.RecipientType -eq 'UserMailbox' } | ForEach { $Users = @{} } { $Users[$_.SamAccountName] = $_ }
    $AllMailboxes = Get-Mailbox -ResultSize Unlimited | Where { $_.RecipientTypeDetails -eq "UserMailbox" } | ForEach {
    $PrimarySmtpDomain = $_.PrimarySmtpAddress.split("@")
    $firstNameNonLatin = Remove-StringLatinCharacters -String $Users[$_.SamAccountName].FirstName
    $lastnameNonLatin = Remove-StringLatinCharacters -String $Users[$_.SamAccountName].LastName
    New-Object psobject | 
        Add-Member -PassThru NoteProperty Alias $_.Alias | 
        Add-Member -PassThru NoteProperty Name $_.Name |        
        Add-Member -PassThru NoteProperty DisplayName $_.DisplayName | 
        Add-Member -PassThru NoteProperty FirstName $Users[$_.SamAccountName].FirstName |
        Add-Member -PassThru NoteProperty LastName $Users[$_.SamAccountName].LastName  |
        Add-Member -PassThru NoteProperty FirstNameNonLatin $firstNameNonLatin |
        Add-Member -PassThru NoteProperty LastNameNonLatin $lastnameNonLatin |
        Add-Member -PassThru NoteProperty EmailAddressPolicyEnabled $_.EmailAddressPolicyEnabled | 
        Add-Member -PassThru NoteProperty PrimarySmtpAddress $_.PrimarySmtpAddress |
        Add-Member -PassThru NoteProperty PrimarySmtpDomain $PrimarySmtpDomain[1]          |
        Add-Member -PassThru NoteProperty EmailAddresses $_.EmailAddresses |   
        Add-Member -PassThru NoteProperty RecipientType $_.RecipientType |
        Add-Member -PassThru NoteProperty RecipientTypeDetails $_.RecipientTypeDetails | 
        Add-Member -PassThru NoteProperty IsResource $_.IsResource |
        Add-Member -PassThru NoteProperty IsShared $_.IsShared | 
        Add-Member -PassThru NoteProperty IsLinked $_.IsLinked | 
        Add-Member -PassThru NoteProperty ExchangeUserAccountControl $_.ExchangeUserAccountControl | 
        Add-Member -PassThru NoteProperty AddressBookPolicy $_.AddressBookPolicy | 
        Add-Member -PassThru NoteProperty Identity $_.Identity | 
        Add-Member -PassThru NoteProperty DistinguishedName $_.DistinguishedName |           
        Add-Member -PassThru NoteProperty OrganizationalUnit $_.OrganizationalUnit | 
        Add-Member -PassThru NoteProperty AddressListMembership $_.AddressListMembership | 
        Add-Member -PassThru NoteProperty RulesQuota $_.RulesQuota |
        Add-Member -PassThru NoteProperty UserPrincipalName $_.UserPrincipalName | 
        Add-Member -PassThru NoteProperty SamAccountName $_.SamAccountName |
        Add-Member -PassThru NoteProperty UseDatabaseQuotaDefaults $_.UseDatabaseQuotaDefaults 

    }

    foreach ($Mailbox in $AllMailboxes) {
       $skip = $false;
       foreach ($Excluded in $ExcludeUsers) {
            if ($($Mailbox.UserPrincipalName) -eq $Excluded) {
               $skip = $true;              
            }
       }
       if ($skip) { continue } 
       $FirstName = $Mailbox.FirstnameNonLatin
       $LastName = $Mailbox.LastNameNonLatin
       $Alias = $Mailbox.Alias
       $DisplayName = $Mailbox.DisplayName
       $FullEmail = $EmailTemplate
       $FullEmail = [regex]::Replace($FullEmail, '%(\d*)g', {param($m) Get-NameSection $FirstName $m.Groups[1].Value })
       $FullEmail = [regex]::Replace($FullEmail, '%(\d*)s', {param($m) Get-NameSection $LastName $m.Groups[1].Value })
       $Mailbox = ProcessEmail ($Mailbox) ($FullEmail)
       $collection += $Mailbox
    }
    if ($ShowSummaryTable) {
        $collection | ft UserPrincipalName, DisplayName, NewEmailToAdd, NewEmailRequiresAdding
    }
    if ($ShowSummaryLines) {
        foreach ($mail in $collection) {
            if ($($mail.NewEmailRequiresAdding) -eq $true) {
                Write-Color "Following e-mail will be added ", "$($mail.NewEmailToAdd)", " to user (UPN): ", "$($mail.UserPrincipalName)" -Color White,Green,White,Green
            } else {
                Write-Color "Following e-mail will be skipped ", "$($mail.NewEmailToAdd)", " to user (UPN): ", "$($mail.UserPrincipalName)" -Color White,Yellow,White,Yellow
            }
        }
    }

    if ($Commit) {
        Write-Color -LinesBefore 1
        foreach ($mail in $collection) {
            if ($($mail.NewEmailRequiresAdding) -eq $true) {
                Write-Color "Adding e-mail ", "$($mail.NewEmailToAdd)", " to user (UPN): ", "$($mail.UserPrincipalName)" -Color White,Green,White,Green
                Add-EmailAddress -Identity $($mail.UserPrincipalName) -EmailAddress $($mail.NewEmailToAdd)
            }
        }
    } else {
        Write-Color -Text "Changes not committed, re-run the script with the -Commit switch when you're ready to apply the changes." -LinesBefore 1
        Write-Color -Text "No changes made due to -Commit switch not being specified." -Color Magenta
    }

}

💡 Notes