Office365MailAliases.psm1

<#
.SYNOPSIS
  This module contains functions to create mail aliases in Office 365

.DESCRIPTION
  These mail aliases are created per domain name or organization. This is to make sure
  that organizations get unique email addresses. You need at least "User administrator"
  permissions and the appropriate Exchange permissions to run the "New-MailAlias" command.

.INPUTS
  None

.OUTPUTS
  None

.NOTES
  Author: Jean-Paul van Ravensberg, Cloudenius.com

.EXAMPLE
  Select-MailAlias -DomainName Google.com -ExportAliasesToMailDraft -Verbose

  Create a mail alias for Google.com and provide Verbose output. After selecting the mail alias,
  create a draft mail in the mailbox of the user that contains all the used mail aliases.

.EXAMPLE
  New-MailAlias -NumberOfAliases 9 -Verbose

  Warm up aliases for later use and provide Verbose output.
#>


Function New-MailAlias {
    #Requires -Modules ExchangeOnlineManagement
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '*', Scope = 'Function', Target = '*', Justification = 'Does not change system state')]
    param(
        [parameter(Mandatory = $true, HelpMessage = "Specify the amount of aliases required")]
        [ValidateNotNullOrEmpty()]
        [int]$NumberOfAliases,

        [parameter(Mandatory = $true, HelpMessage = "Specify the domain name that is used for the email address. E.g. johndoe.com")]
        [ValidateNotNullOrEmpty()]
        [string]$EmailDomain,

        [parameter(Mandatory = $true, HelpMessage = "Specify the owner of the alias. E.g. john@johndoe.com")]
        [ValidateNotNullOrEmpty()]
        [string]$Owner,

        [parameter(Mandatory = $true, HelpMessage = "Specify the prefix that will be used to create the alias. E.g. JD")]
        [ValidateNotNullOrEmpty()]
        [string]$GroupNamePrefix,

        [parameter(Mandatory = $false, HelpMessage = "Keep the Exchange Online session alive for further use")]
        [switch]$KeepAlive
    )

    ## Login to Office 365
    If (!(Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" })) {
        Connect-ExchangeOnline -ShowBanner:$false
    }

    Write-Verbose "Creating $NumberOfAliases aliases"

    Foreach ($i in 1..$NumberOfAliases) {
        $Random = Get-Random -Minimum 10000 -Maximum 99999
        $GroupName = $GroupNamePrefix + $Random
        $GroupEmail = ($GroupName + "@" + $EmailDomain)

        Write-Verbose "Creating alias $i with name $GroupName"

        If (Get-DistributionGroup | Where-Object { $_.Name -like "*$GroupName*" }) {
            Write-Verbose "Distribution Group name is not unique. Will skip name $GroupName"
        }

        Else {
            # Create the new Distribution Group
            Try {
                New-DistributionGroup -Name $GroupName -Type "Security" -ManagedBy $Owner -PrimarySmtpAddress $GroupEmail
            }

            Catch [Exception] {
                Write-Error "Distribution Group already exists or another error occurred"
                Break
            }

            # Allow external senders to mail to the address & set _CLAIMABLE suffix
            Set-DistributionGroup -Identity $GroupName -RequireSenderAuthenticationEnabled:$false -DisplayName $($GroupName + "_CLAIMABLE")

            # Modify the new Distribution Group with SendOnBehalf permissions
            Add-RecipientPermission -Identity $GroupName -AccessRights SendAs -Trustee $Owner -Confirm:$false

            # Add the owner to the Distribution Group
            Add-DistributionGroupMember -Identity $GroupName -Member $Owner

            Write-Verbose "Created group called $GroupName with owner $Owner"
        }
    }

    # Disconnect the session to make sure we don't run out of maximum concurrent connections
    If (!$KeepAlive) {
        If (Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }) {
            $Null = Disconnect-ExchangeOnline -Confirm:$false
        }
    }
}

Function Select-MailAlias {
    #Requires -Modules ExchangeOnlineManagement

    param(
        [parameter(Mandatory = $true, HelpMessage = "Specify the domain name of the website")]
        [ValidateNotNullOrEmpty()]
        [string]$DomainName,

        [parameter(Mandatory = $false, HelpMessage = "Create a draft mail in the mailbox of the user that contains all the used mail aliases")]
        [switch]$ExportAliasesToMailDraft,

        [parameter(Mandatory = $false, HelpMessage = "Keep the Exchange Online session alive for further use")]
        [switch]$KeepAlive
    )

    ## Login to Office 365
    If (!(Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" })) {
        Connect-ExchangeOnline -ShowBanner:$false
    }

    Write-Verbose "Claiming an alias for $DomainName"

    # Check if domain name already exists in Distribution Group
    $ExistingDistributionGroup = Get-DistributionGroup | Where-Object { $_.DisplayName -like "$DomainName - *" }

    If ($ExistingDistributionGroup) {
        Write-Output "Alias for domain name '$($DomainName)' already exists. Returning the alias already in use"

        $DistributionGroup = $ExistingDistributionGroup

        $EmailDomain = $DistributionGroup.PrimarySmtpAddress.Split('@')[1]
        $DisplayName = $DomainName + " - " + $EmailDomain
    }

    Else {
        # Search for unused alias and return the oldest one
        $ClaimableDistributionGroups = Get-DistributionGroup | Where-Object { $_.DisplayName -Like "*_CLAIMABLE" } | Sort-Object WhenCreatedUtc

        If (!($ClaimableDistributionGroups)) {
            Write-Error "No claimable Mail Aliases found. Please run New-MailAlias first."
            Break
        }

        While (!($ClaimableDistributionGroups = Get-DistributionGroup | Where-Object { $_.DisplayName -Like "*_CLAIMABLE" })) {
            Write-Output "Waiting for a new claimable Distribution Group. Pause 5 seconds..."
            Start-Sleep -Seconds 5
        }

        Write-Verbose "Found $($ClaimableDistributionGroups.GetType().Count) claimable Distribution Group(s)"

        # Rename unused alias & change description
        $DistributionGroup = $ClaimableDistributionGroups[0]

        Write-Verbose "Picking $DistributionGroup for the rename"

        If ($DistributionGroup.WhenCreated.AddHours(1) -gt (Get-Date)) {
            Write-Warning "Be aware that this alias is <60 minutes old and might not be active yet"
        }

        # Change the Display Name for the Distribution Group
        $EmailDomain = $DistributionGroup.PrimarySmtpAddress.Split('@')[1]
        $DisplayName = $DomainName + " - " + $EmailDomain

        Set-DistributionGroup -Identity $DistributionGroup.Name -DisplayName $DisplayName
    }

    # Create the draft mail in the mailbox of the user that contains all the used mail aliases
    If ($ExportAliasesToMailDraft) {
        $MailMessage = New-MailMessage -Body (Get-UsedMailAlias | Select-Object Name, DisplayName | Sort-Object DisplayName | Out-String) -Subject "Used Mailbox Aliases"

        if ($MailMessage) {
            Write-Output "Successfully created draft mail message with subject '$($MailMessage.Subject)' and object state '$($MailMessage.ObjectState)'"
        }

        Else {
            Write-Warning "Something went wrong with creating the draft mail message"
        }
    }

    # Disconnect the session to make sure we don't run out of maximum concurrent connections
    If (!$KeepAlive) {
        If (Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }) {
            $Null = Disconnect-ExchangeOnline -Confirm:$false
        }
    }

    # Return the new name of the alias
    return New-Object PSObject -Property ([ordered]@{"Name" = $DistributionGroup.Name; "DisplayName" = $DisplayName; "E-mail" = $DistributionGroup.PrimarySmtpAddress })
}

Function Get-UsedMailAlias {
    #Requires -Modules ExchangeOnlineManagement

    param(
        [parameter(Mandatory = $false, HelpMessage = "Name prefix that is used to identify the Mail Aliases")]
        [ValidateNotNullOrEmpty()]
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '$GroupNamePrefix', Justification = 'False positive')]
        [string]$GroupNamePrefix,

        [parameter(Mandatory = $false, HelpMessage = "Create a draft mail in the mailbox of the user that contains all the used mail aliases")]
        [switch]$ExportAliasesToMailDraft,

        [parameter(Mandatory = $false, HelpMessage = "Keep the Exchange Online session alive for further use")]
        [switch]$KeepAlive
    )

    ## Login to Office 365
    If (!(Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" })) {
        Connect-ExchangeOnline -ShowBanner:$false
    }

    # Check if domain name already exists in Distribution Group
    $ExistingDistributionGroup = Get-DistributionGroup | Where-Object `
    { $_.Name -like "$GroupNamePrefix*" -and $_.DisplayName -notlike "*_CLAIMABLE" }

    # Create the draft mail in the mailbox of the user that contains all the used mail aliases
    If ($ExistingDistributionGroup -and $ExportAliasesToMailDraft) {
        $MailMessage = New-MailMessage -Body ($ExistingDistributionGroup | Select-Object Name, DisplayName | Sort-Object DisplayName | Out-String) -Subject "Used Mailbox Aliases"

        if ($MailMessage) {
            Write-Output "Successfully created draft mail message with subject '$($MailMessage.Subject)' and object state '$($MailMessage.ObjectState)'"
        }

        Else {
            Write-Warning "Something went wrong with creating the draft mail message"
        }
    }

    # Disconnect the session to make sure we don't run out of maximum concurrent connections
    If (!$KeepAlive) {
        If (Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }) {
            $Null = Disconnect-ExchangeOnline -Confirm:$false
        }
    }

    # Return the new name of the alias(es)
    If ($ExistingDistributionGroup) {
        return $ExistingDistributionGroup | Select-Object Name, DisplayName, PrimarySmtpAddress | Sort-Object DisplayName
    }
    Else {
        return
    }
}

Function Get-UnusedMailAlias {
    #Requires -Modules ExchangeOnlineManagement

    param(
        [parameter(Mandatory = $false, HelpMessage = "Name prefix that is used to identify the Mail Aliases")]
        [ValidateNotNullOrEmpty()]
        [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '$GroupNamePrefix', Justification = 'False positive')]
        [string]$GroupNamePrefix,

        [parameter(Mandatory = $false, HelpMessage = "Keep the Exchange Online session alive for further use")]
        [switch]$KeepAlive
    )

    ## Login to Office 365
    If (!(Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" })) {
        Connect-ExchangeOnline -ShowBanner:$false
    }

    # Check if domain name already exists in Distribution Group
    $ExistingDistributionGroup = Get-DistributionGroup | Where-Object `
    { $_.Name -like "$GroupNamePrefix*" -and $_.DisplayName -like "*_CLAIMABLE" }

    # Disconnect the session to make sure we don't run out of maximum concurrent connections
    If (!$KeepAlive) {
        If (Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }) {
            $Null = Disconnect-ExchangeOnline -Confirm:$false
        }
    }

    # Return the names of the unused alias(es)
    If ($ExistingDistributionGroup) {
        return $ExistingDistributionGroup | Select-Object Name, DisplayName, PrimarySmtpAddress
    }
    Else {
        return
    }
}

Function Set-MailAliasToArchived {
    #Requires -Modules ExchangeOnlineManagement
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '*', Scope = 'Function', Target = '*', Justification = 'Does not change system state')]

    param(
        [parameter(Mandatory = $true, HelpMessage = "Specify the domain name of the website")]
        [ValidateNotNullOrEmpty()]
        [string]$DomainName,

        [parameter(Mandatory = $false, HelpMessage = "Keep the Exchange Online session alive for further use")]
        [switch]$KeepAlive
    )

    ## Login to Office 365
    If (!(Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" })) {
        Connect-ExchangeOnline -ShowBanner:$false
    }

    # Check if domain name already exists in Distribution Group
    $ExistingDistributionGroup = Get-DistributionGroup | Where-Object `
    { $_.DisplayName -like "$DomainName - *" }

    if (!$ExistingDistributionGroup) {
        throw "Cannot find an existing Distribution Group with domain name '$($DomainName)'"
    }

    else {
        Write-Output "Changing displayName from '$($ExistingDistributionGroup.DisplayName)' to '(Archived) $($ExistingDistributionGroup.DisplayName)'"
        Set-DistributionGroup -Identity $ExistingDistributionGroup.Identity -DisplayName "(Archived) $($ExistingDistributionGroup.DisplayName)"

        Write-Output "Removing members from distribution group"
        $ExistingDistributionGroup | Remove-DistributionGroupMember -Confirm:$false

        # Return the new name of the alias
        Write-Output "Done, new result:"
    }

    $output = Get-DistributionGroup -Identity $ExistingDistributionGroup.Identity | Select-Object Name, DisplayName, PrimarySmtpAddress

    # Disconnect the session to make sure we don't run out of maximum concurrent connections
    If (!$KeepAlive) {
        If (Get-PSSession | Where-Object { $_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }) {
            $Null = Disconnect-ExchangeOnline -Confirm:$false
        }
    }

    return $output
}