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


