Skip to content

CrossTenantAccessPolicyCalendarValidation

Download the latest release: CrossTenantAccessPolicyCalendarValidation.ps1

Assesses whether Organization Relationships, Availability Address Spaces, and Sharing Policies can migrate to Microsoft 365 Cross-Tenant Access Policy (XTAP). It explains eligibility and can generate commands for migration, testing, rollback, and cleanup.

The script is read-only. Configuration changes occur only when you review and run the generated scripts.

Prerequisites

For live collection, connect to Exchange Online PowerShell with the Organization Management role:

Connect-ExchangeOnline

The generated Part 2 script requires the Microsoft Graph PowerShell SDK and Global Administrator. Offline analysis requires only a JSON bundle saved by this script.

Usage

Save the script in a writable folder, then change to that folder before running it. Replace the example path below with the folder where you saved the .ps1 file. By default, all output files are saved together in a timestamped results subfolder beside the script, even if you launch it from another working directory.

# Change to the folder containing the script
Set-Location -LiteralPath 'C:\Path\To\Script'

# Assessment only
.\CrossTenantAccessPolicyCalendarValidation.ps1 -RunName BeforeMigration

# Include migration commands and mailbox counts per Sharing Policy
.\CrossTenantAccessPolicyCalendarValidation.ps1 -RunName BeforeMigration -GenerateCommandScripts -ProcessUsers

# Generate commands from the same previously collected data
.\CrossTenantAccessPolicyCalendarValidation.ps1 -ImportData '.\BeforeMigration_CrossTenantAccessPolicyCalendarValidation_Data.json' -GenerateCommandScripts

# For MS Support to replay data:
# Copy this script and the JSON bundle into one folder, then run from that folder:
.\CrossTenantAccessPolicyCalendarValidation.ps1 -ImportData '.\BeforeMigration_CrossTenantAccessPolicyCalendarValidation_Data.json'

Parameters

Parameter Description
OutputPath Parent folder for the timestamped results folder. Defaults to the folder containing the .ps1 file. Specify another writable folder only when needed.
RunName Optional label such as BeforeMigration or MonthlyValidation, used in folder/file names, JSON metadata, and report headers. Defaults to the saved run name when importing. Each run also has an automatic timestamp.
ImportData Path to a previously saved JSON bundle. Replaces the separate CLIXML import parameters. Cannot be combined with ProcessUsers or Organization.
SkipTenantResolution Skips public OpenID Connect tenant lookups. Only domain names are sent. Import mode defaults to the saved setting and never makes network requests.
EntraLoginHost DNS host name for tenant lookup in sovereign clouds, without a scheme, port, path, or IP address. Defaults to login.microsoftonline.com in live mode; imports retain the saved host.
Organization Live mode only. Targets the specified tenant using cmdlets supporting Organization. The saved organization is also used in generated commands during replay. Unsupported collection cmdlets cause an error; an unsupported mailbox cmdlet skips mailbox counts with a warning.
GenerateCommandScripts Writes the optional XTAP Part 2, Part 3, rollback, and cleanup scripts for eligible items.
ProcessUsers Live mode only. Counts user and shared mailboxes per Sharing Policy using Get-EXOMailbox, or Get-Mailbox if unavailable. Mailboxes without an explicit assignment count toward the default policy. Off by default because it queries every mailbox. Counts are included in the JSON bundle for replay; policies confirmed unused are excluded from generated scripts.
Live runs check for script updates automatically. ImportData automatically skips the update check, as well as live
collection and tenant lookups, so replay can run without network access.

Results are displayed in the console and saved to files; the script does not return a PowerShell results object. Normal output uses the console's default text color so it follows your accessibility and contrast settings. Warnings and errors retain their diagnostic colors. For automation, read the saved JSON bundle:

# Replace the example timestamp with your results folder
$data = Get-Content -LiteralPath '.\BeforeMigration_XTAP_Calendar_20261021_14.27.42\BeforeMigration_CrossTenantAccessPolicyCalendarValidation_Data.json' -Raw -Encoding UTF8 | ConvertFrom-Json
$data.Analysis.OrganizationRelationship

Output and JSON replay

A new <RunName>_XTAP_Calendar_<yyyyMMdd_HH.mm.ss> folder contains the output. For example, -RunName Microsoft produces Microsoft_XTAP_Calendar_20261021_14.27.42. Without a run name, the folder is XTAP_Calendar_20261021_14.27.42. The timestamp uses local time, with dots separating hours, minutes, and seconds; milliseconds are omitted. If the folder name already exists, a numeric suffix such as _1 or _2 keeps each run separate without overwriting earlier output. Only the results folder name was shortened; the JSON and summary filenames inside the folder still use CrossTenantAccessPolicyCalendarValidation:

File Contents
*_CrossTenantAccessPolicyCalendarValidation_Data.json Versioned bundle with the summary near the top, invocation metadata, run errors, all collected configuration properties, optional mailbox counts, tenant resolution results (including failed lookups), analysis arrays, generated command text, and output file names.
*_CrossTenantAccessPolicyCalendarValidation_Summary.txt Readable results, warnings, next steps, and the ImportData replay command.
XTAP_Part2_CreatePolicies.ps1 Optional commands to create XTAP partner policies and capabilities. Saves a tenant-bound capability backup before creating capabilities; refuses to overwrite pre-existing capabilities.
XTAP_Part3_DisableForTesting.ps1, XTAP_Part3_Rollback.ps1 Optional commands to disable legacy settings and roll back this run's capability additions. Disabling requires explicit prerequisite confirmation. Availability Address Space backups use JSON.
XTAP_Part4_Cleanup.ps1 Review-only cleanup commands. All configuration-changing commands are commented out, so running the file unchanged makes no changes. Deliberately uncomment commands or copy and run them individually after successful testing.
XTAP_Part2_CapabilityBackup.json Created only when Part 2 runs, not during assessment. Records the connected tenant, relevant capability baselines, intended inbound scopes, and confirmed creation outcomes for rollback.

Empty configuration and analysis collections remain JSON arrays. SharingPolicyAssignment is null when counts were not collected, and an empty array when collection succeeded with no mailboxes. Exchange-specific values such as domains, sharing rules, enums, directory identities, and URIs are stored as plain strings; booleans and numbers retain their types. No XML or CSV data files are created. Existing CLIXML collections are not accepted by ImportData. JSON bundles are read as UTF-8, including in Windows PowerShell 5.1, so non-ASCII names survive replay.

Next steps list only the command scripts actually generated. An assessment with no eligible, used configuration can produce no command scripts. Availability Address Space-only assessments do not need a cleanup script. Policies containing both calendar sharing and unsupported rules can produce Part 2 and capability rollback without automatic legacy-disable commands; preserve unsupported functionality and plan manual testing before changing it. Capability and Availability Address Space backups are saved beside the generated scripts, regardless of the working directory. Keep all scripts and backups together in their assessment folder.

Summary immediately follows SchemaVersion. Metadata.CommandLine records the script path and resolved bound parameter values, including explicit switch values; Metadata.InvocationLine preserves the original invocation text when PowerShell provides it. Metadata.User identifies the local account executing PowerShell, not necessarily the Exchange Online account. Errors contains structured errors raised during this run, including recoverable lookup and mailbox-count failures, but excludes errors already present in the PowerShell session. It is an empty array when no errors were recorded. Existing warnings and fatal-stop behavior are unchanged; a fatal failure can stop before JSON is written. Replay records its own invocation, user, and errors rather than copying these fields from the input bundle.

Each TenantResolution entry also saves an Error string. Organization Relationship analysis includes a DomainTenantResolution array containing each domain, tenant ID, status, and error. Availability Address Space and Sharing Policy analysis expose TenantResolutionStatus or TenantResolution, plus TenantResolutionError. These diagnostics distinguish failed lookups, invalid configured tenant IDs, and missing saved lookup data. Offline replay retains the original lookup diagnostics without adding them to the replay run's Errors array. Older version 1 bundles without per-domain errors remain supported; their failed lookups report that no detailed error was saved.

Import mode recalculates analysis from saved configuration using saved tenant lookups, without querying Exchange or Microsoft Entra. RunName can be overridden. Older bundles using CustomerName are still imported as a run label; new bundles write RunName only. The folder timestamp, summary generation time, and JSON Metadata.GeneratedUtc identify when each run was performed, even when the same run label is reused. GenerateCommandScripts controls whether scripts are written on each run, regardless of whether the original collection included them. Missing or incompatible bundles cause an error.

These files contain organization configuration, external domains, command lines and paths, the executing user, and error details. Review them before sharing outside your organization.

Statuses

Status Meaning
Eligible Can migrate now. Commands can be generated.
Review Needs manual confirmation, such as an unresolved tenant ID.
NotEligible Cannot migrate now; notes explain why. Keep the existing configuration.
Disabled Not currently sharing. Potential capabilities if enabled are assessed separately; no migration commands are generated.
NothingToMigrate No supported Free/Busy or MailTips sharing enabled.
MailboxMoveOnly Used only for mailbox moves. Keep it.

Migration safeguards

  • On-premises, hybrid, non-Microsoft 365, and Google Workspace targets cannot migrate to XTAP.
  • An explicit FYDIBOHF25SPDLT on-premises indicator in an Availability Address Space takes precedence over a configured tenant ID. Invalid configured tenant IDs require review rather than being replaced by a lookup.
  • Only OrgWideFBToken Availability Address Spaces qualify.
  • ContactsSharing rules and Sharing Policy domains belonging to hybrid relationships remain in place.
  • Organization Relationships are checked for MailboxMove, IdentityMove, PeopleSearch, ArchiveAccess, DeliveryReport, and Photos functionality. MailboxMove and IdentityMove are detected from either their enabled flag or capability. Relationships with additional functionality are never disabled or removed by generated commands; only their Free/Busy and MailTips settings change, with rollback restoring those settings. Verify current XTAP support and preserve or split any functionality that is not covered before manually retiring a relationship.
  • With tenant resolution enabled, every Organization Relationship domain must resolve before the relationship is eligible. Partially resolved relationships require review and are excluded from migration, testing, and cleanup scripts. Missing domain names also require review, even when lookup is skipped. Skipping lookup retains guarded tenant-ID placeholders for otherwise eligible relationships.
  • Scoped sharing requires security groups in XTAP. Validate scope behavior to avoid over-sharing.
  • Generated Sharing Policy commands always require a security group containing only the mailboxes using that policy, including inherited default-policy assignments. Counts alone do not establish tenant-wide applicability, so even a single policy uses a guarded group placeholder. Policies confirmed to have no assigned user or shared mailboxes are excluded from migration, testing, rollback, and cleanup scripts. Policies targeting the same tenant and capability combine their security-group scopes into one creation request. Replace every group placeholder before running that request; existing capabilities and drift checks remain protected.
  • Wildcard and Anonymous Sharing Policy rules generate Part 2 commands for the default XTAP policy even when no partner tenant policy is needed.
  • Wildcard rules also apply to on-premises and hybrid recipients. Their Part 3 disable commands are commented out. Leave these policies enabled unless you confirm no on-premises or hybrid sharing dependencies remain.
  • Default calendar capabilities do not establish collaboration trust. For wildcard sharing, manually inspect Get-MgPolicyCrossTenantAccessPolicyDefault and each intended partner's Get-MgPolicyCrossTenantAccessPolicyPartner configuration. Verify effective m365CollaborationInbound access, user/group scopes, inheritance, and partner overrides for every intended Microsoft 365 partner. Generated commands never broadly enable default collaboration trust. An authored capability without effective trust does not prove sharing works.
  • Domains resolving to multiple tenants require separate partner policies.
  • XTAP is inbound; bidirectional sharing requires complementary external policies.
  • Existing Organization Relationships and Availability Address Spaces take precedence over XTAP and must be disabled or removed to test the replacement Free/Busy and MailTips configuration. Applicable XTAP calendar sharing capabilities take precedence over Sharing Policies; calendar sharing can be tested while those policies remain enabled. If no capability applies, the Sharing Policy is used.
  • Generated scripts contain guarded placeholders for unknown tenant IDs or security groups. Replace them before running.
  • Part 2 stops on Microsoft Graph authentication failures before any policy changes, rather than continuing with an existing Graph context. It also stops on trust creation, trust update, or capability creation failures. Resolve the error and confirm replacement policies are working before running Part 3 or disabling any legacy sharing.
  • Before running Part 3, manually verify effective collaboration trust and replacement policy functionality, including the intended users and partner configurations. Only then run .\XTAP_Part3_DisableForTesting.ps1 -ReplacementPoliciesVerified. This switch provides your explicit confirmation; it does not automatically validate or configure trust.
  • Part 2 records relevant capability baselines before creation and refuses to overwrite any pre-existing capability. Existing configurations require manual scope review and merging rather than an automatic replacement. Rerunning Part 2 preserves the original backup and skips this run's confirmed additions only if their inbound access still matches. Conflicting scopes stop the script; do not delete the backup or run Part 2 concurrently to bypass a conflict.
  • Rollback requires Microsoft Graph authentication as well as Exchange Online PowerShell. It verifies the backup's tenant and checks this run's new capabilities before making changes, restores legacy settings, then removes only confirmed additions whose inbound access still matches the recorded settings. Pre-existing and unrelated capabilities remain untouched. Re-enabling a Sharing Policy alone cannot override applicable XTAP calendar sharing. Missing backups, capability drift, and uncertain creation outcomes stop automatic rollback for manual review. Partner policy objects and collaboration trust created or updated in Part 2 are retained to avoid disrupting other workloads; review their cleanup separately. A completed rollback is recorded so repeated rollback is inert. Generate a fresh assessment and scripts before starting a new migration after rollback.
  • Availability Address Space backups record original configuration and removal/restoration progress. Rollback skips untouched spaces, including when Part 3 has not run or removal was declined, and safely resumes a partial restore. Missing backups for absent spaces or conflicting existing configuration require manual review; they are not ignored.
  • All Step 4 changes, including deletion and sharing-setting changes, are commented out until you deliberately enable them. Deletion is permanent. Retain the collected JSON bundle, Part 3 rollback script, and backups; Part 3 rollback does not recreate Organization Relationships or Sharing Policies deleted during Step 4.
  • Keep migrated Sharing Policies disabled until their mailbox assignments have been removed or reassigned. Before removing a default Sharing Policy, select an appropriate replacement default and verify its sharing restrictions. Disabling a policy does not clear its assignments or make it ready for removal.
  • Microsoft Graph cmdlet names can change; confirm them against the linked migration documentation.

Additional assessment details

Organization Relationship analysis includes AdditionalEnabledFunctionalities, AdditionalFunctionalitiesRecommendation, and SharingCapabilityWouldBeEligibleIfEnabled. Disabled relationships retain potential capabilities in FreeBusyCapabilityIfEnabled and MailTipsCapabilityIfEnabled, while their active capability fields remain empty. Disabled Sharing Policy rows similarly expose CalendarCapabilityWouldBeEligibleIfEnabled and RecommendedCapabilityIfEnabled. These are assessments of current settings, not instructions to enable legacy configuration, and do not make disabled items eligible for command generation.

Sharing Policy analysis retains one row per action, including ContactsSharing. HasContactsSharing and ContactsSharingAssessment describe the entire original rule, even on its calendar action rows. LegacyRuleCanBeRetired is false when the policy is disabled, the calendar action is not eligible, or the original rule contains ContactsSharing or another unsupported action. Generated commands continue to preserve any policy containing an ineligible rule.

Availability Address Space analysis includes the original ConfiguredTargetTenantId, TargetTenantIdValid, ResolvedEntraTenantId, and a migration recommendation. Migration is optional and is not required because of EWS deprecation.

Acknowledgments

Credit to Mirela Buruiana for the idea and first draft of this script.