Using Powershell to match up AD and 365 accounts for AD Connect

Today I have decided that I was going to start writing again! I apologize in advanced if I am not good at it but I am sure I will learn as life moves forward. Today's topic is AD Connect. I work for a technical company that provides IT services to other companies. We had a situation last week were our client's Small Business Server was down and Microsoft had been on the server for about 5 hours and they had no clue why it was not working. Our client really needed email so we migrated them to Office 365 in a relatively short amount of time. Now that the email is working, we have decided to go back and setup AD Connect so that they can have a Single Sign-On experience.
If you have ever setup AD Connect for an existing domain and existing Office 365 tenant, you will learn that there are some things you need to do to prep. Here are the steps that we determined that we needed to perform before setting up AD Connect to sync the accounts.

  1. Add the proxy addresses in Office 365 into AD equal and ensure to keep case sensitivity.
  2. Set Display Name in Office 365 equal to the Display Name in AD and ensure to keep case sensitivity.
  3. Set the User Principle Name in AD equal to the primary email address in Office 365 and ensure to keep case sensitivity.
  4. Set the Mail property in AD equal to the the default SMTP address.

Let's go through the script! Please keep in mind that since my company has multiple clients, I need to ensure that this script will work across different environments as much as possible which guides some of my design choices.
This script should be ran from the console of an AD Domain Controller.

In this section, I am loading the Powershell modules needed for the script. The activedirectory module is needed to connect to your Active Directory and will provide cmdlets necessary for managing object within it. The msonline module is the similar module for Office 365 objects.
Import-Module activedirectory
Install-Module MSOnline

In this section, I am setting the verbose preference to continue because by default it is set to silentlycontinue. This is a requirement if you would like to use the Write-Verbose cmdlet instead of Write-Host. I do this because in a lot of my scripts, I run them from a RMM tool such as Kaseya, Labtech or Continuum and I use the Start-Transcript and Stop-Transcript cmdlets to output the console to file for troubleshooting if needed.
Next I have variables needed to hold the username and password of the Office 365 admin account. Use the Get-Credential cmdlet instead if you plan on storing the script so that you are not leaving plain text passwords in files.

$VerbosePreference = 'Continue'
$username = <>
$passwd = <>

In this section, I am connecting to the Azure AD to get a list of users that are in the tenant. I convert the password to a secure password and then create an object that holds the credentials to log in. Then I create the session to Azure AD and add that session to the Powershell console. The final line in this section will create an array of user objects imported from the Azure AD.

# connect to office 365
$password = ConvertTo-SecureString $passwd -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$username", $password)
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $creds -Authentication Basic –AllowRedirection
Import-PSSession $Session -AllowClobber
# Get user from office 365
$365users = Get-Mailbox -ResultSize Unlimited

In this section, I am looping through the array of users one at a time and placing them in the $365user variable to be manipulated.

# find user in AD
foreach ($365user in $365users){

In this section, I am grabbing the UserPrincipalName property and then writing it to the screen. This is done for one purpose and that is because it outputs it in white which makes a good divider between the users in the console when running the script.

$upn =  $365user.UserPrincipalName
Write-Host $upn

In this section, I am using the UserPrincipalName property to find the user in Active Directory. The UserPrincipalName field in Active Directory is not likely to be set to the same as their default email since in my experience many IT departments out there build domains with .local. I am finding the email address among the proxyAddress property.

$filter = "proxyAddresses -like `"*SMTP:$upn*`""
$aduser = get-aduser -filter $filter -Properties *

In this section, I first use an if statement to see if the AD user was found and then I use the Write-Verbose cmdlet to output to the console which user I am working on. I then use the Write-Verbose cmdlet to output to the console which step I am performing on that user. I get all of the proxy addresses in Office 365 for the user and loop through them one at a time to see if they are in the proxyAddresses property of the user in AD. If they are not then I am adding the email address to the property. In the end, this will end up where you have a user account in AD that has all email addresses in Office 365 added to the existing proxy addresses in AD.

if ($aduser){
    Write-Verbose "**** Setting properties for user $($365user.DisplayName)"
    # set proxy in AD equal to proxy 365 case sensitive
    Write-Verbose "set proxy in AD equal to proxy 365 case sensitive"
    $proxies = $365user.EmailAddresses
    foreach ($proxy in $proxies){
        if ($aduser.proxyAddresses -cmatch $proxy){
            Write-Verbose "The proxy address matched for $proxy"
        } else {
            Write-Verbose "Adding proxy $proxy to AD"
            Set-ADUser $aduser -Add @{ProxyAddresses = $proxy}
        }
    }

In this section, I am setting the DisplayName property of the Office 365 account equal to that of the AD account. The one thing that you will want to pay attention to here is the use of -ceq instead of -eq. This is done because for AD sync to work properly some fields have to be exactly the same even down to capital letters. Powershell is case insensitive so saying "Carlos -eq carlos" would be a true statement. In order to make it case sensitive you add a "c" at the beginning of the operator. In order to set the DisplayName property on Office 365 I need to connect using the MSonline module and not Azure. To do this I am using the Connect-MsolService cmdlet with the credentials created earlier.

# set Display name in 365 equal to AD with case sensitive
    Write-Verbose "**** set Display name in 365 equal to AD with case sensitive"
    if ($aduser.DisplayName -ceq $365user.DisplayName){
        Write-Verbose "Display names match for $($aduser.displayname)"
    }else{
        Write-Verbose "Setting the Display Name from $($365user.DisplayName) -ceq $($aduser.DisplayName)"
        Connect-MsolService -Credential $creds
        Set-MsolUser -UserPrincipalName $upn -DisplayName $($aduser.displayname)
    }

In this section, I am setting the UserPrincipalName property in AD to equal that of their UserPrincipalName property in Office 365. This section is required or it will create a new account in Office 365 with the .onmicrosoft.com domain and the accounts will be linked to that one.

# set upn in AD equal to 365 with case sensitivity
    Write-Verbose "**** set upn in AD equal to 365 with case sensitivity"
    if ($aduser.UserPrincipalName -ceq $365user.UserPrincipalName){
        Write-Verbose "UserPrincipalName match for $($aduser.UserPrincipalName)"
    }else{
        Write-Verbose "Setting the UPN $($aduser.UserPrincipalName) -ceq $($365user.UserPrincipalName)"
        Set-ADuser -Identity $aduser -UserPrincipalName $upn
    }

In this section, I am ensuring that the Mail attribute in Active Directory is set to their primary email account. The primary email account in Active Directory is denoted in the proxyAddresses property by a capital SMTP: before the email. I am looping through all of the properties looking for the capital SMTP: and then setting the mail attribute to that email

# set mail property in AD equalt to the the default SMTP
    Write-Verbose "**** set mail property in AD equalt to the the default SMTP"
    foreach ($proxy in $proxies){
    if ($proxy -cmatch 'SMTP:'){
       $email = $proxy.split(":")[1]
       Write-Verbose "The default SMTP is $email"
       Set-ADuser -Identity $aduser -EmailAddress $email
    }
}

 

In this section, I am sending output to the console in the event that the AD account was not found for the user.

} else {
    Write-Verbose "User $($365user.DisplayName) was not found"
    }
}

Here is the entire script in case you would like to use it.

Import-Module activedirectory
Install-Module MSOnline 

$VerbosePreference = 'Continue'
$username = 'INPUT_OFFICE365_USERNAME_HERE'
$passwd = 'INPUT_OFFICE365_PASSWORD_HERE'

# connect to office 365
$password = ConvertTo-SecureString $passwd -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ("$username", $password)
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell/ -Credential $creds -Authentication Basic –AllowRedirection
Import-PSSession $Session -AllowClobber

# Get user from office 365
$365users = Get-Mailbox -ResultSize Unlimited #| Select-Object DisplayName,PrimarySmtpAddress,EmailAddresses,Alias,UserPrincipalName

# find user in AD
foreach ($365user in $365users){
    $upn =  $365user.UserPrincipalName
    Write-Host $upn
    $filter = "proxyAddresses -like `"*SMTP:$upn*`""
    $aduser = get-aduser -filter $filter -Properties *
    
    if ($aduser){
        Write-Verbose "**** Setting properties for user $($365user.DisplayName)"
        # set proxy in AD equal to proxy 365 case sensitive
        Write-Verbose "set proxy in AD equal to proxy 365 case sensitive"
        $proxies = $365user.EmailAddresses
        foreach ($proxy in $proxies){
            if ($aduser.proxyAddresses -cmatch $proxy){
                Write-Verbose "The proxy address matched for $proxy"
            } else {
                Write-Verbose "Adding proxy $proxy to AD"
                Set-ADUser $aduser -Add @{ProxyAddresses = $proxy}
            }
        }

        # set Display name in 365 equal to AD with case sensitive
        Write-Verbose "**** set Display name in 365 equal to AD with case sensitive"
        if ($aduser.DisplayName -ceq $365user.DisplayName){
            Write-Verbose "Display names match for $($aduser.displayname)"
        }else{
            Write-Verbose "Setting the Display Name from $($365user.DisplayName) -ceq $($aduser.DisplayName)"
            Connect-MsolService -Credential $creds
            Set-MsolUser -UserPrincipalName $upn -DisplayName $($aduser.displayname)
        }

        # set upn in AD equal to 365 with case sensitivity
        Write-Verbose "**** set upn in AD equal to 365 with case sensitivity"
        if ($aduser.UserPrincipalName -ceq $365user.UserPrincipalName){
            Write-Verbose "UserPrincipalName match for $($aduser.UserPrincipalName)"
        }else{
            Write-Verbose "Setting the UPN $($aduser.UserPrincipalName) -ceq $($365user.UserPrincipalName)"
            Set-ADuser -Identity $aduser -UserPrincipalName $upn
        }

        # set mail property in AD equalt to the the default SMTP
        Write-Verbose "**** set mail property in AD equalt to the the default SMTP"
        foreach ($proxy in $proxies){
            if ($proxy -cmatch 'SMTP:'){
                 $email = $proxy.split(":")[1]
                 Write-Verbose "The default SMTP is $email"
                 Set-ADuser -Identity $aduser -EmailAddress $email
            }
        }

    } else {
        Write-Verbose "User $($365user.DisplayName) was not found"
    }
}

I hope that you enjoyed this article and please let me know if you expand upon it or find any errors!

Thanks
Carlos

Leave a Reply

Your email address will not be published.