Change UserPrincipalName + Suffix With a scheduled Windows task

You have completed the domain work in relation to your O365 tenancy, however the usernames replicated to AAD are in “username@old suffix.com” format.

Most organizations have inherited a “username@old-domain.local” suffix. Time goes on and the organization changes its name, amalgamates, etc, for whatever reason, effectively changes its name.

The target of the script is the group a on-prem active directory group.

The following script will serve to allow you to run the script wrapped in a scheduled task in order to change the full upn for the usernames in the group with from “username@old-company.local”(no joke intended), through to “firstname.lastname@new-domain.com” format.

I have used the script on a nightly basis but do not see the reason to run it more frequently.

Import-Module ActiveDirectory
$group = "O365 Users"
$newsuffix = "new-suffix.com"
$users = get-adgroupmember -Identity $group |Get-ADUser |sort userprincipalname
$from = "servername@domain.com"
$to = "admin-user@domain.com"
$tofailedrecipient = "poor.sysadmin@domain.com"

foreach ($user in $users){
    $upn = (($user.givenname)+"."+($user.surname)+"@"+$newsuffix).tostring()
    $upnfromad = ($user.userprincipalname).tostring()
    if ($upn -notmatch $upnfromad) {
        Try{
            $emailbody = "<HTML><HEAD><META http-equiv=""Content-Type"" content=""text/html; charset=iso-8859-1"" /><TITLE></TITLE></HEAD>"
            $emailbody = "<BODY bgcolor=""#FFFFFF"" style=""font-size: Small; font-family: Arial; color: #000000""><P>"
            $emailbody += "<p>The following UPNs have been updated:</p>"
            Set-ADUser -Identity $user.SamAccountName -UserPrincipalName "$($user.GivenName).$($user.Surname)@$newsuffix"
            $emailbody += $upn
            $emailbody += "<br>"
            $successMessageParameters = @{
                        Subject = "The following addresses have been updated - $((Get-Date).ToShortDateString())"
                        Body = $emailbody
                        From = $from
                        To = $to
                        SmtpServer = "x.x.x.x"
                        BodyAsHTML = $true
                        }
            $emailbody += "<p>Regards,</p>"
            $emailbody += "<p>Your friendly Office 365 team</p>"
            Send-MailMessage @successMessageParameters
            }
        Catch [Exception] {
            $ErrorMessage = $_.Exception.Message
            $failedMessageParameters = @{
                        Subject = "The following error was encountered when attempting to update the UPN's"
                        Body = ("'$ErrorMessage'") | Out-String
                        From = $from
                        To = $tofailedrecipient
                        SmtpServer = "x.x.x.x"
                        }
            Send-MailMessage @failedMessageParameters -BodyAsHtml
        }
    }
}



Change Suffix To AD Security group

You have completed the domain work in relation to your O365 tenancy, however the usernames replicated to AAD are in “username@old suffix.com” format.

Most organizations have inherited a “username@old-domain.local” suffix. Time goes on and the organization changes its name, amalgamates, etc, for whatever reason, effectively changes its name.

The following script will serve to allow you to run the script ad hoc in order to change the full upn for the usernames in the group with from “username@old-company.local”(no joke intended), through to “firstname.lastname@new-domain.com” format:

param([string] $group)
cls

Import-Module ActiveDirectory
$newsuffix = "new-suffix.com"
$users = get-adgroupmember -Identity $group |Get-ADUser


write-host "The following users are going to be renamed, would you like to procees?"
$users.userprincipalname 
write-host "The above UPNs are affected." -ForegroundColor Red
$confirmation = Read-Host "Are you Sure You Want To Proceed (y) to firstname.lastname@new-domain.com format" 
if ($confirmation -eq 'y') {
    foreach ($user in $users)
        {
            Set-ADUser -Identity $user.SamAccountName -UserPrincipalName "$($user.GivenName).$($user.Surname)@$newsuffix"
        }
}
Sleep 5

Get-ADGroupMember $group | get-aduser | select userprincipalname

Backup permissions for Sharepoint Online

So you have started down the road of creating your Sharepoint site in Office 365 but you do not have permissions permissions as these require to be manually added.

We are a little lazier than that, this will be the basis of a scheduled job.

Please use the script below along with my small recommendations:

#one-time generation of an a file to hold password so that we may schedule the job. 
#read-Host “Enter Password” -AsSecureString |  ConvertFrom-SecureString | Out-File “C:\Scripts\o365_spo.txt”


Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking

#Setup our variables
$date = (get-date -format "dd-MM-yy")
$contents = $null
$emailbody = $null
$AdminURL = "https://tenancy-admin.sharepoint.com"
$AdminName = "O365Backup@domain.com"
#admin account we will be adding
$AdminNames = "backup.account@domain.com","sharepoint.admin@domain.com"
$TenantPass = cat “C:\Scripts\o365_spo.txt” | ConvertTo-SecureString
$TenantCredentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $adminname, $TenantPass
$cred = [System.Net.CredentialCache]::DefaultCredentials
[System.Net.WebRequest]::DefaultWebProxy.Credentials = $TenantCredentials

Connect-SPOService -Url $adminurl -credential $TenantCredentials

#Build site list
$Sites = Get-SPOSite -Limit ALL

$emailbody = "<HTML><HEAD><META http-equiv=""Content-Type"" content=""text/html; charset=iso-8859-1"" /><TITLE></TITLE></HEAD>"
$emailbody = "<BODY bgcolor=""#FFFFFF"" style=""font-size: Small; font-family: Arial; color: #000000""><P>"
$emailbody += "<p>Please be aware that the following permissions have been added to sharepoint sites by $AdminName :</p>"
$emailbody += "<p>Tenancy: $AdminURL</p>"


Foreach ($admin in $adminnames){

    Foreach ($Site in $Sites){
        Set-SPOUser -site $Site.Url -LoginName $Admin -IsSiteCollectionAdmin $True
        $displayname = Get-SPOUser -site $Site.Url -LoginName $admin
        $displayname = $displayname.displayname | out-string
        $url = $site.url | Out-String
        $isadmin = get-spouser $site.url -loginname $Admin | select issiteadmin
        $emailbody += "$displayname confirmed as $isadmin for: $url</br>"
        write-host $emailbody
        }
}
$emailbody += "<p>Regards,</p>"
$emailbody += "<p>Your friendly Office 365 team</p>"
$successMessageParameters = @{
Subject = "Site Collection Admin Added to SharePoint Sites - $((Get-Date).ToShortDateString())"
Body = $emailbody
From = "servername@domain.com"
To = "backupadmins@domain.com","digitalplatformteam@domain.com"
#To = "testing.user@pegasus.org.nz"
SmtpServer = "x.x.x.x"
BodyAsHTML = $true
}
Send-MailMessage @successMessageParameters

Exchange 2019 failure: Error 4027 and error id 6

Background

Organizations are preparing site to move their mailboxes up to O365 and would like to have a management console in-house that is the latest and greatest, namely Exchange 2019 (CU1) on server 2019 and running ONLY the Edge Transport roles only.  This then allows them to decommission their old 2013, or 2016 Exchange servers.

During the installation, the installation window completely disappears in the final step, 95%, step 7/8 . The Exchange installation is now marked as complete as opposed to the reality, the installation wizard crashing.

What we are seeing in the installation log:

[05/16/2019 04:18:19.0456] [2] Ending processing Set-ServerComponentState
[05/16/2019 04:18:19.0456] [2] Beginning processing Write-ExchangeSetupLog
[05/16/2019 04:18:19.0456] [2] Install is complete. Monitoring has been set to Active.
[05/16/2019 04:18:19.0456] [2] Ending processing Write-ExchangeSetupLog
[05/16/2019 04:18:19.0456] [2] Active Directory session settings for ‘Get-ServerComponentState’ are: View Entire Forest: ‘True’,
[05/16/2019 04:18:19.0456] [2] User specified parameters: -Component:’RecoveryActionsEnabled’ -ErrorAction:’SilentlyContinue’ -Identity:’ExcSRV’
[05/16/2019 04:18:19.0456] [2] Beginning processing Get-ServerComponentState
[05/16/2019 04:18:26.0300] [2] Calling ADSession.GetSharedConfigDC()
Failed to determine hostname for the DC. Hostname: null
[05/16/2019 04:18:26.0300] [2] Machine:ExcSrv and Process:ExSetupUI is executing the request

What we are seeing in the event log at the very start of the installation:

Both event ID 4027 and 6 are very vague.

Root cause: 

The missing service: 

The Microsoft Exchange Active Directory Topology service is not installed as a part of the installation. Since this is not installed, the new Exchange server is unable to query with Active Directory and ask ‘where are the other Exchange server?’

Without the topology service present, Exchange cannot root itself into active directory.

Running the same as the above in the existing Exchange DAG farm, does not show the new server:

Running the same on the new server only outputed itself.

Resolution: 

Install Exchange with 2019 and include the mailbox role together. It appears the installation is flawed by-design. The outcome was from spending 2 hours with a Microsoft Exchange engineer and countless other attempts. The result in not doing do is a simple member server which thinks it is an Exchange server, but it  is not rooted in the Active Directory.  None of the OWA, ECP, EWS, AD attributes are carried through.

Appendix:

Event ID 4027

System.ServiceModel.EndpointNotFoundException: Could not connect to net.tcp://localhost:890/Microsoft.Exchange.Directory.TopologyService. The connection attempt lasted for a time span of 00:00:02.0280035. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:890.  —> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:890
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at System.ServiceModel.Channels.SocketConnectionInitiator.Connect(Uri uri, TimeSpan timeout)
— End of inner exception stack trace —

Log Name:      Application
Source:        MSExchange ADAccess
Date:          1/13/2014 8:57:01 PM
Event ID:      4027
Task Category: General
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      ExcSrv.contoso.com
Description:
Error Details
System.ServiceModel.EndpointNotFoundException: Could not connect to  net.tcp://localhost:890/Microsoft.Exchange.Directory.TopologyService. The connection attempt lasted for a time span of 00:00:02.0280035. TCP error code 10061: No connection could be made because the target machine actively refused it 127.0.0.1:890.  —> System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 127.0.0.1:890
at System.Net.Sockets.Socket.DoConnect(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Connect(EndPoint remoteEP)
at System.ServiceModel.Channels.SocketConnectionInitiator.Connect(Uri uri, TimeSpan timeout)
— End of inner exception stack trace —

Event id 6

Get-ExchangeServer

-ErrorAction “SilentlyContinue” -DomainController $null -Identity “PEGEXC05”

contoso.com/Users/Administrators/Admin

Local-Exchange Management Console-Unknown

7900 ExSetupUI.exe

53

00:00:00

View Entire Forest: ‘True’, Configuration Domain Controller: ‘ExcSRV.contoso.com’, Preferred Global Catalog: ‘ExcSRV.contoso.com’, Preferred Domain Controllers: ‘{ ExcSRV.contoso.com}’

Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException: The operation couldn’t be performed because object ‘PEGEXC05’ couldn’t be found on ‘ExcSRV.contoso.com’.

at Microsoft.Exchange.Configuration.Tasks.Task.ThrowError(Exception exception, ErrorCategory errorCategory, Object target, String helpUrl)

at Microsoft.Exchange.Configuration.Tasks.GetObjectWithIdentityTaskBase`2.InternalProcessRecord()

at Microsoft.Exchange.Configuration.Tasks.Task.<ProcessRecord>b__91_1()

at Microsoft.Exchange.Configuration.Tasks.Task.InvokeRetryableFunc(String funcName, Action func, Boolean terminatePipelineIfFailed)

Context

Ex6F9304

False

0 objects execution has been proxied to remote server.

0

ActivityId: 23f63a8c-7481-40ec-9d87-9950120d0c35

en-US

The message resource is present but the message was not found in the message table