Categories
Active Directory Microsoft 365

Restart failed Microsoft 365 mailbox migration

We’ve had a client where a single mailbox keeps failing, no matter what we do.

I was tired of having to restart this migration manually, so I whipped up a tiny script that would do it for me.

$username = "exchange365admin@tenant.onmicrosoft.com"
$password = ConvertTo-SecureString 'password' -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

Connect-ExchangeOnline -credential $credential 

$migrationusers = Get-MigrationUser -ResultSize Unlimited | Where-Object {$_.Status -eq "Failed"}

$migrationusers | Start-MigrationUser

Disconnect-ExchangeOnline -Confirm:$false

I set up a scheduled task to run it regularly (every 15 minutes in my case) and a few days later, the mailbox was up and running in Microsoft 365.

Categories
Active Directory Azure AD Microsoft 365 Windows Server

Import Microsoft 365 user to on premise Active Directory

This solves a frequent situation: you have an existing Microsoft 365 tenant with users that already are using it, and you want to set-up an Azure AD connect synchronization with an on premise Active directory.

This script will read the AAD user’s attributes, create a local user with matching attributes and set up a mS-DS-ConsistencyGuid attribute that allows Azure AD connect to synchronize the two users.

It is important to note that since we can’t know the AAD user’s password, a new one will need to be set through the script and will replicate to AAD.

<# 
.SYNOPSIS
    Script synchronizes a remote AzureAd users with a local Activedirectory users that it creates

.DESCRIPTION 
    This script will get all attributes from office 365 and add them to a locally created user.
	It will then generate and add an mS-DS-ConsistencyGuid that will allow AzureADConnect to sync the remote and local user.
	User should be created in a non-synced OU and then copied over to a synchronized OU to allow for checks.
	
 
.NOTES 
    A global admin is probably not required but at this stage I'm not sure what permission are actually needed.

.COMPONENT 
    Requires module AzureAD and MSOnline


.LINK 
    https://blog.edzilla.info/?p=88
 
.Parameter usersContainer 
    OU where the user will be created

.Parameter userUPN 
    The UPN of the users we want to import

.Parameter userPassword 
    The password we want to user: password cannot be imported from AAD, so a new one must be set on import.
#>
Param (
	[string] $usersContainer = "OU=Isotoit,OU=Building,OU=No-365,OU=Users,OU=XLG,DC=XLG,DC=grp",
	[string] $userUPN = "toto@toto.com",
	[string] $userPassword = 'Qow65345@@'

)

$UserCredential = Get-Credential
 

#Install Module
Install-Module -Name AzureAD
Install-Module MSOnline
 
#Connect to Office 365 Azure AD
Connect-AzureAD -Credential $UserCredential
Connect-MsolService -Credential $UserCredential

#Add UPN suffixes to local ad
$azureDomains = Get-AzureADDomain
$localForest = (get-addomain).Forest
 
ForEach($domain in $azureDomains)
{
    if($domain.Name -notlike "*onmicrosoft.com")
    {
        Set-ADForest -Identity $localForest -UPNSuffixes @{Add=($domain).Name}
    }
}
 

 
function convert-guid {
  param(
    [Parameter(Mandatory=$true)]
    [GUID]$guidtoconvert
  )

  $bytearray = $guidtoconvert.ToByteArray()
  $immutableID = [System.Convert]::ToBase64String($bytearray)
  $immutableID
}


 
function Add-LocalADObject
{
    ForEach($object in $input)
    {
        write-host $object.ObjectType $object.DisplayName
 
        if($object.UserPrincipalName -like "*onmicrosoft.com")
        {
            write-host "Skipping object " $object.DisplayName "because it does not have a custom logon domain"
            continue
        }
 
        if($object.ObjectType -eq "User")
        {
            $userName, $upn = $object.UserPrincipalName.split('@')
            $upn = "@"+$upn
            New-ADUser -SamAccountName $userName -UserPrincipalName $object.UserPrincipalName -Name $object.DisplayName -DisplayName $object.DisplayName -Path $usersContainer -AccountPassword (ConvertTo-SecureString $userPassword -AsPlainText -Force) -Enabled $True -PassThru
            $filter = "CN=" + $object.DisplayName
        }
 
        $localADObject = Get-ADObject -Filter "UserPrincipalName -eq '$($userUPN)'"
        #Update attributes based on Azure contact
        if($object.GivenName -ne $null){ Set-ADObject $localADObject -Add @{givenName=$object.GivenName}  }
        if($object.Surname -ne $null){ Set-ADObject $localADObject -Add @{sn=$object.Surname}  }
        if($object.Mail -ne $null){ Set-ADObject $localADObject -Add @{mail=$object.Mail}  }
        if($object.StreetAddress -ne $null){ Set-ADObject $localADObject -Add @{streetAddress=$object.StreetAddress} }
        if($object.PostalCode -ne $null){ Set-ADObject $localADObject -Add @{postalCode=$object.PostalCode} }
        if($object.City -ne $null){ Set-ADObject $localADObject -Add @{l=$object.City} }
        if($object.State -ne $null){ Set-ADObject $localADObject -Add @{st=$object.State} }
        if($object.PhysicalDeliveryOfficeName -ne $null){ Set-ADObject $localADObject -Add @{physicalDeliveryOfficeName=$object.PhysicalDeliveryOfficeName} }
        if($object.TelephoneNumber -ne $null){ Set-ADObject $localADObject -Add @{telephoneNumber=$object.TelephoneNumber} }
        if($object.FacsimilieTelephoneNumber -ne $null){ Set-ADObject $localADObject -Add @{facsimileTelephoneNumber=$object.FacsimilieTelephoneNumber} }
        if($object.Mobile -ne $null){ Set-ADObject $localADObject -Add @{mobile=$object.Mobile} }
        if($object.JobTitle -ne $null){ Set-ADObject $localADObject -Add @{title=$object.JobTitle} }
        if($object.Department -ne $null){ Set-ADObject $localADObject -Add @{department=$object.Department} }
        if($object.CompanyName -ne $null){ Set-ADObject $localADObject -Add @{company=$object.CompanyName} }
		if($object.Country -ne $null){ Set-ADObject $localADObject -Add @{c="BE"} }
		if($object.MailNickName -ne $null){ Set-ADObject $localADObject -Add @{MailNickName=$object.MailNickName} }
		if($object.PreferredLanguage -ne $null){ Set-ADObject $localADObject -Add @{PreferredLanguage=$object.PreferredLanguage} }
		Set-ADObject $localADObject -Add @{msExchPoliciesExcluded="{26491cfc-9e50-4857-861b-0cb8df22b5d7}"}
 
        #get proxy addresses
        if($object.ProxyAddresses -ne $null)
        {
            ForEach($proxyAddress in $object.ProxyAddresses)
            {
                Set-ADObject -identity $localADObject -Add @{ProxyAddresses=$proxyAddress}
            }
        }
     }
}
 
#Add users to local AD
if (!(Get-ADUser -Filter "userPrincipalName -like '$userUPN'"))
{
	Get-AzureADUser -ObjectId $userUPN | Add-LocalADObject
}
$user = Get-ADUser -Filter "userPrincipalName -like '$userUPN'"
$immutableID = convert-guid -guidtoconvert $user.objectguid
Set-MsolUser -UserPrincipalName $userUPN -ImmutableId $immutableID
$decodedII = [system.convert]::frombase64string($ImmutableId)
$decode = [GUID]$decodedii
$ImmutableId = $decode
$user | Set-ADUser -Replace @{'mS-DS-ConsistencyGuid'=$($ImmutableId)}

Disconnect-AzureAD