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 } }