Gather all the Essential Information from Source Tenants

Assessing the essential information about tenants to for an Office 365 migration plan has never been easy. The complexity involved in tenant-to-tenant migrations has grown considerably as Microsoft adds more features and services to the mix used by tenants. For example, the addition of Shared Channels to Teams adds a new layer of consideration for anyone trying to understand how external people access Teams in an organization.

In addition to Teams, there are many moving parts to track including SharePoint sites, OneDrive for Business, Exchange Online Mailboxes, Azure AD applications, etc. The amount of data that must be migrated impacts the planning of an Office 365 tenant-to-tenant migration, what tools and licensing are needed, and if coexistence is required.

To generate an initial assessment for a tenant, I created two PowerShell scripts and an Excel template (available on GitHub). The first script creates an Azure AD app and certificate; the second script fetches different types of tenant data to create the Office 365 migration plan report. Together, the aim is to capture and document the most important information that influences migration planning. The report helps those working on an Office 365 migration plan to make decisions based on hard data.

What does the Office 365 Migration Report Tell Me?

The script uses cmdlets from the ImportExcel module to create and update an Excel workbook with 19 tabs (worksheets) containing information relevant to migration scenarios. A high-level breakdown of the contents of each tab is detailed here.

Tab 1: High-Level

 The High-Level tab works as a cover page and is imported from the template file TenantAssessment-Template.xlsx (available on GitHub). The template file must be present in the directory you run the script from. You can customize the tab by modifying the template (Figure 1), including the use of lookups and formulas to help pull together the high-level information you need into a single place.

Office 365 migration plan assessment template
Figure 1: Modify the template file to customize the High-Level tab

When the Office 365 migration plan report runs, this tab will be added, and any lookups will pull information from the report as shown in Figure 2.

Office 365 migration plan assessment template
Figure 2: Formulas from the template file will be applied to the report

This might take some effort to get the format to be just how you like it. The report could even be adapted into a runbook-style sheet that captures common planning tasks and stakeholders.

Preparing for a migration? Learn more in this whitepaper: Top Five Ways to Prepare for Your Next Office 365 Tenant Migration.

Tab 2: User Accounts

The User Accounts tab lists all tenant users and important details of each such as Mailbox/Archive sizes, OneDrive storage consumption, proxy addresses, licenses, and disabled plans. This tab can be used to define the user migration scope. I like to use the Migrate column to record if the user is in scope or not. You can also use the targetObjectID, targetUPN and targetMail columns to help create mapping files for accounts to be used with migration tools (Or remove them from the script if you like!).

Tab 3/4: Shared Mailboxes and Resource Mailboxes

The Shared Mailboxes and Resource Mailboxes tabs give similar information to the User Accounts tab but for Shared, Room, and Equipment Mailboxes.

Tab 5: SharePoint Sites

The SharePoint Sites tab in this Office 365 migration plan lists details and sizes of all SharePoint Online sites that are not connected to Teams.

Tab 6: Teams

The Teams tab provides details of all Teams including a breakdown of Standard/Private/Shared channels and their associated sizes (Figure 3). Shared channels are broken down into standard SharedChannels and IncomingSharedChannels. The difference between these two values is that IncomingSharedChannels are not homed in this Team (created in another Team or even tenant), so don’t count towards data size. This information is useful when assessing the impact of a migration on external access.

Office 365 migration plan assessment doc
Figure 3: The Teams tab gives channel type breakdowns including incoming Shared Channels

Tab 7: Guest Accounts

Guest Accounts are often forgotten when performing an Office 365 migration plan but deserve some consideration. This tab lists all Guest accounts in the tenant. The data can be used with this script to create custom B2B Guest User invitations to recreate guest accounts in the target tenant.

Tab 8: AAD Apps

This tab lists applications which have been registered in the Azure AD tenant (where Microsoft is not the publisher). This information is useful when assessing line of business apps that interact with Office 365.

Tab 9: Conditional Access

The Conditional Access tab lists all Conditional Access policies and settings (Figure 4). The script uses the information gathered about users, groups, and apps to translate Object ID references into easy-to-read names.

Office 365 migration plan assessment doc
Figure 4: The Conditional Access tab gives a detailed breakdown of policies applied in the tenant

Tab 10: M365 Apps Usage

The M365 Apps Usage tab provides a report of the user activity across the different apps over the past 30 days. This report is downloaded from the standard reports available in the Admin Center.

Tab 11: Unified Groups

The Unified Groups tab contains details of all Microsoft 365 Group excluding those linked to Teams.

Tab 12: Standard Groups

This tab contains details of all distribution, security, and mail-enabled security groups in the tenant.

Tab 13: Mail Contacts

This tab contains any mail contacts in the tenant.

Tab 14: MX Records

The MX Records tab details the MX Records for each of the accepted domains known to Exchange Online.

Tab 15: Verified Domains

The Verified Domains tab lists all domains registered in the tenant and the enabled services for each.

Tab 16: Transport Rules

The Transport Rules tab lists all Transport Rules present in Exchange Online and details of each rule’s configuration.

Tab 17/18: Mail Connectors

Tabs 17 and 18 capture the names and settings of each receive and send connector in Exchange Online respectively.

Tab 19: OneDrive Sites

The OneDrive Sites tab contains additional details for each OneDrive for Business account in the tenant, including the URL and recently active file counts.

Office 365 migration plan Powershell assessment prerequisites

The Office 365 migration plan report requires four PowerShell Modules:

  • MASL.PS authenticates with the Microsoft Graph API and obtains an Access Token
  • ImportExcel exports the report to Excel
  • ExchangeOnlineManagement connects to Exchange Online to get information that is not exposed via the Graph API
  • AzureAD (or AzureADPreview) sets up the required app registration and permissions

If you don’t already have the required modules installed on a workstation, you can install them from the PS Gallery by running these commands:

Install-Module MSAL.PS

Install-Module ImportExcel

Install-Module ExchangeOnlineManagement

Install-Module AzureAD

In addition to the required modules, the script also requires that the current user account has permission to create and export to the folder c:\temp, and to add certificates to the local user certificate store.

Preparing the environment for an assessment with the Office 365 migration plan

An Azure AD App Registration using Certificate-based authentication authenticate access for the report script to run Microsoft Graph queries. The script Prepare-TenantAssessment.ps1, which is available in the same folder on GitHub, registers the app in Azure AD, assigns the necessary permissions, and creates a Self-Signed Certificate in the current user’s certificate store.

When the preparation script runs, it prompts for Global Admin credentials – these are necessary to create the required objects in the tenant. When the script finishes, a new browser window opens to authenticate with Global Admin credentials. Signing into this page prompts you to grant consent to the new registered app as shown in Figure 5. Once consent is granted, this page redirects to https://localhost and can be closed.

permissions
Figure 5: Grant consent to the new app registration

The script displays values for the Tenant ID, Client ID and Certificate Thumbprint (Figure 6). Capture these values for later use (you can always get the information from the app registration overview in Azure AD). Pressing enter clears the display.

PowerShell script
Figure 6: Take note of the output values

The script creates an app registration called Tenant Assessment Tool in Azure AD. The app has all the required read-only roles assigned (Figure 7). If you didn’t grant consent for the app to use these permissions, you should do so here as otherwise, the app won’t be able to access the data it needs to process.

Office 365 migration plan assessment script
Figure 7: The new app registration will have all required roles assigned and consented

The Service Principal of the app is granted the Global Reader administrative (RBAC) role. This role allows access to information like mail connectors and transport rules that can’t be fetched using a Graph query.

Read More: For more information on why you should use Certificate Authentication for App Registrations, check out this article

Disable concealed names in reports

As the report uses the Graph usage API, I recommend that you disable the option to Display concealed user, group and site names in all reports in the Reports section of the Microsoft 365 admin center (Figure 8). If this setting is not disabled, the Graph generates obfuscated names when it creates usage data for users, groups, and sites, meaning that the script cannot cross-reference objects.

Reports
Figure 8: Disable the option to conceal user, group and site names in all reports

Running the script for your Office 365 migration plan

The Perform-TenantAssessment.ps1 script requires the ClientID, TenantID and CertificateThumbprint reported by the preparation script to be passed as parameters as shown in Figure 9.

Office 365 migration plan script
Figure 9: Run the report with the details output from the preparation script passed as parameters

Depending on the number of objects in the tenant the script will take some time to run, particularly in larger environments. When it finishes, the report will be exported to C:\temp\TenantAssessment-<DateTime>.xlsx where <DateTime> is the date/time that the script was run.

This is just the beginning for Office 365 migration plan assessment scripts

A great benefit of sharing scripts like this online is that there is always someone to take a fresh view and suggest or make improvements. This report focuses on the most common characteristics relevant to an Office 365 tenant-to-tenant migration but there’s always more information that can be added. Let us know if you have any good ideas to improve the script by commenting on this article!

Preparing for a migration? Learn more in this whitepaper: Top Five Ways to Prepare for Your Next Office 365 Tenant Migration.

About the Author

Sean McAvinue

Sean McAvinue is a Microsoft MVP in Office Development and has been working with Microsoft Technologies for more than 10 years. As Modern Workplace Practice Lead at Ergo Group, he helps customers with planning, deploying and maximizing the many benefits of Microsoft 365 with a focus on security and automation. With a passion for creative problem solving, he enjoys developing solutions for business requirements by leveraging new technologies or by extending the built-in functionality with automation. Blogs frequently at https://seanmcavinue.net and loves sharing and collaborating with the community. To reach out to Sean, you can find him on Twitter at @sean_mcavinue

Comments

  1. Dean Niquette

    Extremely helpful tools here, saved me a bunch of time. Thanks!

  2. Paul Gordon

    Hi Sean – I’m struggling with the prepare script…. – NB I’m running it in a test tenant… I have no 365 subscription, only an AAD directory, if that’s relevant…
    I’m getting the error:
    New-AzureADApplication : Error occurred while executing NewApplication
    Code: Request_BadRequest
    Message: Cannot convert a primitive value to the expected type ‘Edm.Guid’. See the inner exception for more details.
    which is thrown by the New-AzureADApplication cmdlet on line 154… I’m assuming it’s a problem with one of the parameters, checking, I see that all the params have a value, the types are:
    $appname = String
    $appuri = system.array
    $permissions = RequiredResourceAccess
    $EXOapiPermission = RequiredResourceAccess
    Which on the face of it seems all good…
    I’m using an account which has Global Admin role (and ONLY that role)
    Any thoughts on where I might look to try to get past this?
    TIA

    1. Sean McAvinue

      Hey Paul,
      The problem is likely the Exchange Online permissions with no Office 365 tenant associated. I don’t believe this will work without that

  3. Nanyo Nanev

    Hi Sean,

    Great job. You are a star!!! Highly appreciate what you did and shared.

    I have played with your T2T scripts, which work excellent for 3 tenants. I ran it against a fourth one and it created the excel report w/o any errors. The report is missing the MailboxItemCount and MailboxSizeGB data only (empty cells). If I run Get-MailboxStatistics [user] | ft DisplayName, TotalItemSize, I have the correct information for the particular user. All four tests are performed from the same laptop, software/modules versions. Are you aware if there is any variety of O365 tenants? I cannot explain to myself where is the difference and how the same script can fetch the data from three tenants but fails on the fourth. Did you have such an experience?

    1. Sean McAvinue

      The item count and sizes come from the reports in 365 so I would check if your fourth tenant has obfuscated names in reports enabled.

  4. Agile Admin

    I cannot get this to work versus a GCCH environment. What must I do to get this to authenticate properly? I will take a stab at it shortly, but I assume I am missing something easy

  5. Brian

    Sean, thank you for all of your work. I am running into an issue with the acquiring an access token:
    “Unable to acquire access token, check the parameters are correct
    The property ‘Authority’ cannot be found on this object. Verify that the property exists.”

    Any ideas?

  6. Ahmed

    Hi Team,

    We have migrated few mailboxes using tenant to tenant approach and we are facing a challenge with AIP encrypted emails. After the mailboxes migrated to the target tenant the recipients can no longer view AIP encrypted emails. We have tried multiple ways and also contacted Microsoft but there is no easy solution. We are also trying to leverage using of MIP API’s to see if the protection labels can be removed before migration but this is not an ideal solution. Have you come across this solution and is there a way we can achieve and help customers who can read the AIP protected emails post migration.

    1. Sean McAvinue

      Hi Ahmed,
      Without modifying the protection or removing encryption this isn’t possible as far as I know.

  7. Dean Gilau

    Thanks so much for the Sean. I noticed the Export goes through fine but not updating the template… Any Advice?

    1. Sean McAvinue

      Hi Dean,
      The script outputs to C:\temp\ with a new file. It doesn’t update the template. Make sure the folder exists before running.

    2. Sean McAvinue

      Hi Dean,
      The template file does not get updated, a second file is created with the output

  8. Eduardo

    Hi!
    We have a large environment with many objects and the script is failing with the following error: “Invoke-RestMethod : The remote server returned an error: (401) Unauthorized”. The error starts after an hour of script execution, possibly the Access Token is expiring. Could help?

    1. Sean McAvinue

      Hi Eduardo,
      Yes I imagine if this occurs after an hour then the token is expiring. It must be quite a large environment! You can always add in the Get-Msaltoken command from line 116 where you believe the token is expiring. I’ve run in environments with multiple thousand users without it taking that long but I didn’t come across one of that size.

  9. Shekhar Mistry

    I am attemtping to run throuh this process to prepare for a tenant to tenant migration. It doesnt look like I am receiving a Certificate Thumbprint from the Prepare Assessement script. Am I doing something incorrectly?

    1. Sean McAvinue

      Hi Shekar,
      Do you get a certificate created in your personal store? If not you may need to run PowerShell as admin

      1. Shekhar Mistry

        Thanks for the reply. Checking to see. I did make sure to run Powershell as Admin. I then get this message when I try to run the Perfom Assessement: “Unable to acquire access token, check the parameters are correct
        Cannot process argument transformation on parameter ‘ClientCertificate’. Cannot convert the “System.Security.Cryptograph
        y.X509Certificates.X509Store” value of type “System.Security.Cryptography.X509Certificates.X509Store” to type “System.Se
        curity.Cryptography.X509Certificates.X509Certificate2″.”

        1. Shekhar Mistry

          This is the error I receive when running as local admin as well as Azure Global admin:

          New-AadApplicationCertificate : ERROR. Probably need to run as Administrator.
          At C:\Users\user\Desktop\PS\Prepare_Assesement.ps1:196 char:15
          + … humbprint = New-AadApplicationCertificate -ClientId $appReg.AppId -Ce …
          + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
          + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,New-AadApplicationCertificate

          1. Shekhar Mistry

            Was finally able to get it to work. Getting a new error during the Perform. Anyone else see this?

            Method invocation failed because [System.Object[]] does not contain a method named ‘op_Division’.
            At C:\Users\\desktop\PS\Perform_Assesement.ps1:807 char:9
            + $user.OneDriveSizeGB = (((($OneDrive | ? { $_.’Owner Principa …
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : InvalidOperation: (op_Division:String) [], RuntimeException
            + FullyQualifiedErrorId : MethodNotFound

        2. Sean McAvinue

          Hi Shekhar,
          I have seen that error and will update to account for it. It occurs when one of the values in the Microsoft usage report is blank. You should still have the output file though

          1. Shekhar Mistry

            Thank you! This has been a huge help! Quick question is the One Drive Data Size in GB as well?

          2. Sean McAvinue

            Yes it’s in GB

  10. Henrique

    I tested the script in a small test environment and it worked perfectly, now I am testing it in a real environment with many teams and users and I am getting an error as the script takes hours to run.

    Error: Invoke-RestMethod : The remote server returned an error: (401) Not authorized.

  11. Clement B.

    Hello,
    Thanks for the scripts.
    I have an issue with the perform script.
    It stops when it tries to connect to Exchange and says there is a new update for the ExchangeOnlineManagement module. I had to install PowerShell 7.2.4 to be able to install the preview5 2.0.5

    Now the message is just “Error connecting to Exchange Online…Exiting…”
    I think Microsoft probably changed some commands with this new version of the module

    I also tested with the latest preview6 2.0.6 with same result.

    If you have an idea.

    Thanks

  12. Clement B

    Hello,
    First, thanks for your scripts.
    I’ve an issue running “Prepare-TenantAssessment.ps1”
    I have the following message :
    “App already exists – Please delete the existing ‘Tenant Assessment Tool’ app from Azure AD and rerun the preparation script to recreate, exiting”

    Unfortunately I can’t find the application when I go manually on the Azure web page with the same logins.

    If you have an idea ?

    Thanks

    1. Sean McAvinue

      Make sure you search “All Applications” under “App Registrations” in Azure AD

    2. Clement B

      You can delete this one, I didn’t saw there was a filter activated to only show Enterprise Apps..

  13. Gustavo Rodrigo

    Hi,

    When I try to Execute the prepare assessment script it give those errors:

    En C:\Tenant Assesment\Prepare-TenanatAssessment.ps1: 214 Carácter: 140
    + … rimary text-bold py-2″ data-analytics-event=”{"category":&q …
    + ~
    No se permite usar el carácter de Y comercial (&). El operador & está reservado para un uso futuro; encierre un símbolo de Y comercial entre comillas dobles (“&”) para pasarlo como
    parte de una cadena.

    Token ‘&’ inesperado en la expresión o la instrucción.
    No se notificaron todos los errores de análisis. Corrija los errores notificados e inténtelo de nuevo.
    + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : AmpersandNotAllowed

    1. Sean McAvinue

      Did you copy and paste the script or modify it? The GitHub script does not have a line 214?

  14. Ashley

    Hi, I get lots of errors when running the prepare script, some examples as below:

    ExpandArchiveHelper : Failed to create file ‘C:\temp\microsoft.identitymodel.clients.activedirectory.3.19.8\_rels\.rels’ while expanding the archive file ‘C:\temp\microsoft.identitymodel.clients.activedirectory.3.19.8.zip’ contents as the file ‘C:\temp\microsoft.identitymodel.clients.activedirectory.3.19.8\_rels\.rels’ already exists. Use the -Force parameter if you want to overwrite the existing directory ‘C:\temp\microsoft.identitymodel.clients.activedirectory.3.19.8\_rels\.rels’ contents when expanding the archive file. At C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Archive\Microsoft.PowerShell.Archive.psm1:397 char:17

    1. Sean McAvinue

      Looks like this may be related to the AzureAD PowerShell Module you are using. Check that it’s up to date and that it is loading correctly.

      1. Ashley

        Thanks, I upgraded to the latest version and unfortunately still get the issue. I also tried manually connecting via the Azure AD module and that works fine.

          1. Ashley

            Thanks, this has now ran OK.

  15. Glynn

    Thanks for the tips. There were no errors with -Verbose and I was able to run Connect-AzureAD no problem.

    Took me a couple of tries but now it’s working great.

    Sequence of events:

    1- Removed the requires statement to fix the error I posted.
    2- All was good until I got this message:

    “Unable to acquire access token, check the parameters are correct
    The property ‘Authority’ cannot be found on this object. Verify that the property exists.”

    3- Switched to a different workstation and started again. This time no errors encountered, not even the original one, and even though I had put the “Requires” statement back in as a test.

    4- Script appeared to be executing correctly until it detected that WinRM basic auth was disabled on workstation.

    5- No quick way to fix on that workstation so switched to a 3rd WS where basic auth was already enabled.

    6- Script executed flawlessly from beginning to end of your whole documented process and produced a detailed inventory of my tenant.

    So, had to jump through a couple of hoops but well worth it for the learning and the end result, which is a fantastic piece of documentation.

    Once again, thanks for releasing this to the community.

  16. Glynn

    Good day and thank you for all the hard work here.

    Look forward to using the script but I seem to have fallen at the first hurdle:

    PS C:\temp\PowerShell> Install-Module AzureAD
    PS C:\temp\PowerShell> Install-Module ExchangeOnlineManagement
    PS C:\temp\PowerShell> Install-Module ImportExcel
    PS C:\temp\PowerShell> Install-Module MSAL.PS

    PS C:\temp\PowerShell> .\Prepare-TenantAssessment.ps1

    .\Prepare-TenantAssessment.ps1 : The script ‘Prepare-TenantAssessment.ps1’ cannot be run because the following modules
    that are specified by the “#requires” statements of the script are missing: AzureAD.
    At line:1 char:1
    + .\Prepare-TenantAssessment.ps1
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ResourceUnavailable: (Prepare-TenantAssessment.ps1:String) [], ScriptRequiresException
    + FullyQualifiedErrorId : ScriptRequiresMissingModules

    All required modules appeared to install without error.

    Any help to understand what I have missed is appreciated.

    Thanks!

    1. Sean McAvinue

      The requires statement checks if the AzureAD module is installed but seems to not see the module on your machine.

      Do you get any errors running: “Import-Module AzureAD -Verbose”

      You could also check if you can run Connect-AzureAD to test the module if so then you can try run it after removing the Requires statement (line 22).

      1. Glynn

        Thanks for the tips. There were no errors with -Verbose and I was able to run Connect-AzureAD no problem.

        Took me a couple of tries but now it’s working great.

        Sequence of events:

        1- Removed the requires statement to fix the error I posted.
        2- All was good until I got this message:

        “Unable to acquire access token, check the parameters are correct
        The property ‘Authority’ cannot be found on this object. Verify that the property exists.”

        3- Switched to a different workstation and started again. This time no errors encountered, not even the original one, and even though I had put the “Requires” statement back in as a test.

        4- Script appeared to be executing correctly until it detected that WinRM basic auth was disabled on workstation.

        5- No quick way to fix on that workstation so switched to a 3rd WS where basic auth was already enabled.

        6- Script executed flawlessly from beginning to end of your whole documented process and produced a detailed inventory of my tenant.

        So, had to jump through a couple of hoops but well worth it for the learning and the end result, which is a fantastic piece of documentation.

        Once again, thanks for releasing this to the community.

  17. Andy Wilson

    This looks excellent, thanks Sean! Looking forward to giving it a test drive.

  18. Łukasz

    Sean, great work, I appreciate the hard work you’ve done to make this happen!

  19. Julien

    Thanks for your huge work !

    First feedbacks:
    – User accounts => Missing data from the sizes while I have a mailbox at least (values are ok into shared mailboxes sheet)
    – Conditional Access => I think the construction of the table has an issue. I see PolicyName and the name of my policy as column.
    – M365 Apps Usage => Special caracter in the name of the first column

    1. Sean McAvinue

      Thanks Julian,
      On the user accounts data, I have not seen this but as we use the Mailbox usage reports can you check if the user is missing from there also?

      Conditional Access – If I understand correctly you have a column for each policy and settings along the left (first) column. This is by design to try make it easier to read

      Apps usage – I did notice that also but it is a default Microsoft report It could be tidied up by trimming the variable

  20. Kannan

    When I try to Execute the prepare assessment script it give me an error:-

    Waiting for app to provision…
    Error creating new app reg:
    Cannot bind argument to parameter ‘ObjectId’ because it is null.
    Exiting…

    1. Sean McAvinue

      Hi Kannan,
      Seems like it’s failing as the global reader role is not provisioned in your tenant. You should be able to assign the global reader role to any user then rerun. I may update with a check for this soon.

      1. Sean McAvinue

        Script is now updated to provision the role if it’s missing so if you redownload it it should work even when you don’t already have the role provisioned.

        1. Kannan R

          Thank you so much Sean, Cheers!

Leave a Reply