New-SharedMailboxV5.ps1

Function New-SharedMailbox { 
    <#PSScriptInfo
            .DESCRIPTION
            Create a shared mailbox in a hybrid exchange environment.
            .VERSION 1.1
 
            .GUID 5456f964-3a57-4dd2-9a55-fd9649e1c69d
 
            .AUTHOR Alex Curley
 
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $Alias,
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $DisplayName,
        [parameter(mandatory=$false)]
        [system.string[]]
        $Users,
        [System.Management.Automation.CredentialAttribute()]
        $o365AdminCredential,
        [System.Management.Automation.CredentialAttribute()]
        $ADAdminCredential
    )# param end
    begin {
              
        #Functions
        #Connect Exchange Online
        Function Connect-ExchangeOnline ($o365AdminCredential) {            
            $EOSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri 'https://outlook.office365.com/powershell-liveid/?proxymethod=rps' -Credential $o365AdminCredentials -Authentication Basic -AllowRedirection
            Write-Host -ForegroundColor Magenta "Attempting to connect to Exchange Online" -NoNewline
            try { 
                Import-PSSession $EOSession -AllowClobber -DisableNameChecking | Out-Null
                Write-Host -ForegroundColor Cyan ' OK'
            }
            catch {
                Write-Host -ForegroundColor Red 'Could not connect to Exchange. Exiting.'
                Write-Host ''
                Write-Error -Message "$_" -ErrorAction Stop
                return;
            }
        } 
        #Connect Exchange On Premise
        Function Connect-ExchangeOnprem ($Server, $ADAdminCredential) {
            Write-Host -ForegroundColor Magenta "Attempting to connect to Exchange On Premise." -NoNewline
            $so = New-PSSessionOption -SkipCACheck:$true -SkipCNCheck:$true -SkipRevocationCheck:$true
            $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "http://$server/powershell/" -Credential $ADAdminCredential -SessionOption $so
            try {       
                Import-PSSession $Session -AllowClobber -DisableNameChecking | Out-Null
                Write-Host -ForegroundColor Cyan " OK"
            }
            catch {
                Write-Host -ForegroundColor Red 'Could not connect to Exchange. Exiting.'
                Write-Host ''
                Write-Error -Message "$_" -ErrorAction Stop
                return;
            }
        }
        #Replicate Active Directory
        Function Replicate-AD ([string[]]$Controllers,$ADAdminCredential) { 
            Write-Host -ForegroundColor Magenta "Replicating Active Directory." -NoNewline           
            foreach ($controller in $Controllers) { 
                $controller
                $x=0
                start-sleep -Seconds 3
                while ($x -lt 3) { 
                    $x = $x+1
                    # write-host -ForegroundColor red $x
                    Invoke-Command -ComputerName $controller -Credential $ADAdminCredential -ScriptBlock { cmd /c "repadmin /syncall /AdeP" }
                    start-sleep -Seconds 1
                }
            } Write-Host -ForegroundColor Cyan ' OK'
        }
        #Sync Azure AD
        Function Sync-AD ($Server,$ADAdminCredential) {
            Write-Host -ForegroundColor Magenta "Performing Delta AADSync." -NoNewLine
            Invoke-Command -ComputerName $Server -Credential $ADAdminCredential -ScriptBlock { Start-ADSyncSyncCycle -PolicyType Delta } 
            Write-Host -ForegroundColor Cyan ' OK'
        }
        #Connect to MsolService
        Function Connect-Msol {
            Write-Host -ForegroundColor Magenta 'Attempting to connecto to MsolService.' -NoNewline
            try { 
                Connect-MsolService -Credential $o365adminCredentials -ErrorAction Stop
                Write-Host -ForegroundColor Cyan ' OK'
            }
            catch {
                Write-Host -ForegroundColor Red 'Could not connect to MsolService. Exiting.'
                Write-Error -Message "$_" -ErrorAction Stop
                return;
            }
        }
        
        #Modules
        
        #Variables
        $domain = 'YOURDOMAIN'
        [regex]$rgx = "'.+'.couldn't.be.found.on"
        $transcript = 'C:\temp\Transcript.txt'
        $upn_smtp = "$alias@$domain.org"
        $proxy = "$alias@$domain.mail.onmicrosoft.com"
        $target = $proxy.Split('@')[1]
        $DC = 'YOURDC'
        $controllers = 'YOURDC','YOURDC'
        $ExchangeServer = 'YOUREXCHANGESERVER'
        $ExchangeFQDN = 'YOUREXCHANGESERVER'
        $AADServer = 'YOURAADSYNCSERVER'
        $OU = 'YOUROU'
        #Confirm $users exist in AD
        if ($Users){
            Write-Host -ForegroundColor Magenta "Checking if users exist in AD"
            foreach ($user in $users){
                try{
                    get-aduser -Identity $user | out-null
                    Write-Host -ForegroundColor Cyan "Found" -NoNewLine
                    Write-Host -ForegroundColor White " $User"
                }
                catch {
                    Write-Host -ForegroundColor Red "Could not find $user. Exiting"
                    Write-Host ''
                    Write-Error -Message $_ -ErrorAction Stop
                    return;
                }
            }
        }
        
    }
    process {        
        Write-Host ''
        #Create user mailbox onprem, then add mail.onmicrosoft.com proxy address for migration
        Connect-ExchangeOnprem -Server $ExchangeServer -ADAdminCredential $ADAdminCredential  
        Write-Host -ForegroundColor Magenta "Creating mailbox." -NoNewline     
        try {
            $Global:ErrorActionPreference = 'Stop'
            New-Mailbox -Password (ConvertTo-SecureString -AsPlainText 'USIP@1984' -Force) -DisplayName $displayname -Name $displayName -Alias $alias -SamAccountName $alias -UserPrincipalName $upn_smtp -PrimarySmtpAddress $upn_smtp -OrganizationalUnit $OU -DomainController $DC -Confirm:$false -ErrorAction Stop | Out-Null
            Write-Host -ForegroundColor Cyan " OK"       
        }
        catch { 
            Write-Host -ForegroundColor Red "Failed to create mailbox. Exiting."
            Write-Host ''
            write-error -Message $_ -ErrorAction Stop
            return;
        }     
        Write-Host -ForegroundColor Magenta "Adding proxy address for migration."  
        #Give exchange and DC little time to catch up with itself
        Start-Sleep -Seconds 5        
        #Add the proxy address user@DOMAIN.mail.onmicrosoft.com
        try {
            Set-Mailbox -Identity $Alias -EmailAddresses @{add="$proxy"} -DomainController $DC -ErrorAction 'Stop' | Out-Null
            Write-Host -ForegroundColor Cyan "Added " -NoNewline
            Write-Host -ForegroundColor White $proxy -NoNewline
            Write-Host -ForegroundColor Cyan " as a proxy address."
        }
        catch { 
            Write-Host -ForegroundColor Red "Failed to add SMTP proxy address. Cannot migrate without it. Exiting"
            Write-Host ''
            Write-Error -Message $_
            return;
        }
        #Close connection to Exchange on premise
        Get-PSSession | Remove-PSSession
        
        #Replicate Active Directory and Sync to Azure
        Write-Host ''
        Replicate-AD -Controllers $controllers -ADAdminCredential $ADAdminCredential | out-null
        Sync-AD -Server $AADServer -ADAdminCredential $ADAdminCredential  | out-null
        Write-Host -ForegroundColor Magenta "Sleeping 30s." -NoNewline
        Start-Sleep -Seconds 30
        Write-Host -ForegroundColor Cyan ' OK'
        Write-Host ''
        
        #Connect to MsolService and wait for user to sync
        Connect-Msol        
        Write-Host -ForegroundColor Magenta 'Waiting for Get-MsolUser to return something...'  -NoNewline      
        do {
            Get-MsolUser -UserPrincipalName $upn_smtp -ErrorAction SilentlyContinue | Out-Null
        }
        while (!(Get-MsolUser -UserPrincipalName $upn_smtp -ErrorAction SilentlyContinue))
        Write-Host -ForegroundColor Cyan ' OK'
        
        #Connect to Exchange Online, wait for mailbox to become available, and migrate
        #I didn't know how to test whether or not the mailbox was available to migrate, so I used transcript and regex.
        #Start-transcript will not include the successful whatif below, which I would prefer to match. Apparently it's a bug, so i'm matching the most common error (can't find mailbox)
        Connect-ExchangeOnline 
        Write-Host ''
        Write-Host -ForegroundColor Magenta 'Waiting for New-MoveRequest -WhatIf to stop returning errors.'
        do { 
            $ErrorActionPreference = 'Continue'
            $Global:ErrorActionPreference = 'Continue'
            Start-Transcript -LiteralPath $transcript | out-null
            New-MoveRequest -BatchName $Alias -Identity $Alias -Remote -RemoteHostName $ExchangeFQDN -TargetDeliveryDomain $target -RemoteCredential $ADAdminCredential -BadItemLimit 0 -LargeItemLimit 0 -WhatIf 
            Stop-Transcript | out-null            
            Start-Sleep -Seconds 20
        }
        while ((gc $transcript) -match $rgx)
        Write-Host -ForegroundColor Cyan ' OK'
        #Migrate
        Write-Host -ForegroundColor Magenta 'Starting migration.' -NoNewline
        New-MoveRequest -BatchName $Alias -Identity $Alias -Remote -RemoteHostName $ExchangeFQDN -TargetDeliveryDomain $target -RemoteCredential $ADAdminCredential -BadItemLimit 0 -LargeItemLimit 0 | Out-Null
        Write-Host -ForegroundColor Cyan ' OK'
        Write-Host -ForegroundColor Magenta 'Waiting for the status below to show "Completed"'
        #Wait for migration to complete
        do {
            Get-MoveRequest | ? alias -eq $Alias | % { '{0,1} {1,20}' -f $_.batchname,$_.status }
            start-sleep -Seconds 20
        }
        while ((Get-MoveRequest | ? alias -eq $Alias).Status -ne 'Completed')
        Get-MoveRequest | ? alias -eq $Alias | Format-Table batchname,status
        
        #Set mailbox to shared, enable messagecopyforsendas, set ad attributes for hybrid shared mbox, and disable ad user
        Write-Host ''
        Write-Host -ForegroundColor Cyan 'Setting mailbox type to' -NoNewline
        Write-Host " Shared" -NoNewline
        Write-Host -ForegroundColor Magenta "and enabling"  -NoNewline             
        Write-Host  ' MessageCopyForSendOnBehalfEnabled.'
        try {
            $Global:ErrorActionPreference = 'Stop'
            Set-Mailbox -Identity $upn_smtp -Type Shared -MessageCopyForSendOnBehalfEnabled $true | Out-Null          
            Write-Host -ForegroundColor Cyan " OK"       
        }
        catch { 
            Write-Host -ForegroundColor Red "Failed to set mailbox. Exiting."
            Write-Host ''
            write-error -Message $_ -ErrorAction Stop
            return;
        } 
        #Set AD attributes
        Write-Host -ForegroundColor Magenta "Setting msExchRemoteRecipientType to" -NoNewline
        Write-Host " 100" 
        Write-Host -ForegroundColor Magenta "Setting msExchRecipientTypeDetails to" -NoNewline
        Write-Host " 34359738368." 
        try {         
            Set-ADUser $Alias -Replace @{msExchRemoteRecipientType=100; msExchRecipientTypeDetails=34359738368} -Server $DC -Credential $ADAdminCredential | Out-Null
            Write-Host -ForegroundColor Cyan ' OK'
        }
        catch {
            Write-Host -ForegroundColor Red "Failed to set AD attributes. Exiting."
            Write-Host ''
            write-error -Message $_ -ErrorAction Stop
            return;
        }
        #Disable AD object
        Write-Host -ForegroundColor Magenta 'Setting AD Object to' -NoNewline
        Write-Host ' Disabled'        
        try {    
            Disable-ADAccount -Identity $Alias -Server $DC -Credential $ADAdminCredential | Out-Null
            Write-Host -ForegroundColor Cyan 'OK'
        }
        catch {
            Write-Host -ForegroundColor Red "Failed to disable object. Exiting."
            Write-Host ''
            write-error -Message $_ -ErrorAction Stop
            return;
        }
        
        #Add FullAccess and SendAs permissions
        #Try/Catch doesn't work well with PS remoting
        if ($Users){
            foreach ($User in $Users){
                Write-Host -ForegroundColor Magenta 'Assigning' -NoNewLine
                Write-Host -ForegroundColor White " $user" -NoNewline
                Write-Host -ForegroundColor Magenta 'FullAccess and SendAs rights on' -NoNewline
                Write-Host -ForegroundColor White " $Alias" -NoNewline
                Add-MailboxPermission -Identity $upn_smtp -AccessRights FullAccess -User $user -Confirm:$false | Out-Null
                Add-RecipientPermission -Identity $upn_smtp -AccessRights SendAs -Trustee $user -Confirm:$false | Out-Null                              
                Write-Host ' OK'
            }
        }
        
    }
    end {
        $trustees = $users -join ', '
        $mailbox = Get-Mailbox -Identity $Alias | select primarysmtpaddress,name,recipienttypedetails
        $permiss = Get-MailboxPermission -Identity $Alias | Where-Object {$_.user -in $Users} | select user,accessrights
        $sendas  = Get-RecipientPermission -Identity $Alias | Where-Object {$_.Trustee -in $Users} | select trustee,accessrights
        Write-Host "Shared mailbox $Alias created and configured"
        Write-Host "Email address: $($mailbox.primarysmtpaddress)"
        Write-Host "Displayname: $($mailbox.Name)"
        Write-Host "$trustees have full access to $Alias and the ability to send as that address"
        Get-PSSession | Remove-PSSession
        #output text to put in ticket resolution: Email address, display name, access rights, sendonbehalf, type
    }
}