Module installation, interactive sign-in with MFA, delegated access across customer tenants, unattended app-only authentication, and what the common connection errors actually mean.
Every command catalog on this site assumes a working connection, and in practice the connection is where a surprising share of tickets die. This page covers both modules end to end, written for the realities of MSP work: MFA on every admin account, several customer tenants a day, and a mix of PowerShell 5.1 and 7 across technician machines.
From an elevated or CurrentUser-scoped session
Install-Module ExchangeOnlineManagement -Scope CurrentUser
-Scope CurrentUser avoids needing local admin rights, which matters on locked-down technician machines. The module runs on both Windows PowerShell 5.1 and PowerShell 7; PowerShell 7 is the better experience and is where Microsoft's testing effort goes.
On older Windows PowerShell 5.1 installs, the install can fail with "Unable to resolve package source" because the session is not using TLS 1.2. Fix it for the session, then install again:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 Install-Module ExchangeOnlineManagement -Scope CurrentUser
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
A browser window opens for sign-in, MFA included. Supplying -UserPrincipalName is optional but skips the account-picker step. On a server with no browser, or when the browser window refuses to appear, fall back to device code flow:
Connect-ExchangeOnline -Device
This prints a code and a URL; complete the sign-in on any other device. It is also the practical workaround when conditional access or an overzealous browser profile breaks the normal pop-up.
With GDAP or older delegated admin relationships in place, connect to a customer tenant using your partner credentials:
Connect-ExchangeOnline -UserPrincipalName tech@msp.com -DelegatedOrganization customer.onmicrosoft.com
The value for -DelegatedOrganization is the customer's onmicrosoft.com domain or tenant ID. Confirm which tenant a session is pointed at before changing anything; with several windows open this is the single most valuable habit in multi-tenant work:
Get-ConnectionInformation | Select-Object UserPrincipalName, Organization, State
Unattended scripts authenticate as an app registration with a certificate. The app needs the Exchange.ManageAsApp application permission (admin consented) and an Exchange role assigned to its service principal; then:
Connect-ExchangeOnline -AppId $appId -CertificateThumbprint $thumb -Organization contoso.onmicrosoft.com
Certificates replace stored passwords entirely. If an old script embeds credentials for a basic-auth session, it is both broken and a finding: basic authentication to Exchange Online PowerShell no longer works at all.
Disconnect-ExchangeOnline -Confirm:$false
Exchange Online limits concurrent PowerShell connections per admin. Sessions abandoned by closing the window without disconnecting count against that limit until they time out, which is how "Fail to create a runspace because you have exceeded the maximum number of connections allowed" happens on a busy day. Disconnecting properly avoids it; if you are already locked out, wait for the stale sessions to expire or clear them from another session.
Install-Module Microsoft.Graph -Scope CurrentUser
The meta-module pulls in 30+ sub-modules and takes a while. On Windows PowerShell 5.1, loading the entire SDK can also hit the 4096-function session limit, producing the baffling "function capacity 4096 has been exceeded" error. Both problems have the same answer: install only the sub-modules you use.
Leaner install covering most helpdesk work
Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users, Microsoft.Graph.Groups, Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser
If the function-capacity error appears anyway on 5.1, raise the limit at the top of the script or move to PowerShell 7, where the limit is far higher:
$MaximumFunctionCount = 32768
Connect-MgGraph -Scopes "User.Read.All","Group.Read.All" -NoWelcome
Graph grants a session only the scopes requested at connect time. This is the single biggest mental shift from the retired MSOnline and AzureAD modules, which gave you everything your role allowed. Request the scopes each job needs; every command in the Entra ID builder lists its required scopes for exactly this reason. To see what the current session holds:
Get-MgContext | Select-Object Account, TenantId, Scopes
To connect to a specific customer tenant rather than your home tenant, add -TenantId with the customer's tenant ID or onmicrosoft.com domain. Device code flow is available here too with -UseDeviceCode.
Graph automation uses an app registration with application permissions (admin consented) and a certificate:
Connect-MgGraph -ClientId $appId -TenantId $tenantId -CertificateThumbprint $thumb
App-only sessions hold the application permissions granted to the app, not delegated scopes, so -Scopes is not used. Grant the narrowest application permissions the script needs; an automation app with Directory.ReadWrite.All is a standing risk in every tenant it touches.
Disconnect-MgGraph
As with Exchange, make this the habit between tenants. A cached Graph token pointing at the wrong customer is how a "quick check" lands in the wrong directory.
| Error | Cause and fix |
|---|---|
| The term 'Connect-ExchangeOnline' is not recognized | Module not installed or not imported in this session. Install-Module ExchangeOnlineManagement, then start a fresh session. On 5.1, also check the install actually succeeded past the TLS issue below. |
| Unable to resolve package source 'PSGallery' | Windows PowerShell 5.1 defaulting to old TLS. Set TLS 1.2 for the session ([Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12) and retry. |
| Running scripts is disabled on this system | Execution policy. For the current session only: Set-ExecutionPolicy -Scope Process RemoteSigned. Avoid weakening the machine-wide policy on shared technician boxes. |
| Fail to create a runspace... exceeded the maximum number of connections | Too many concurrent Exchange sessions for this admin, usually from closed-without-disconnect windows. Disconnect-ExchangeOnline religiously; wait out stale sessions if already capped. |
| Insufficient privileges to complete the operation (Graph) | The session lacks a needed scope, or the scope was never admin-consented. Disconnect-MgGraph, reconnect with the missing scope added, and check Get-MgContext afterwards. |
| Need admin approval (during Graph sign-in) | The requested scope requires admin consent in that tenant and the signing-in account cannot grant it. A Global Admin or Privileged Role Admin must consent, or pre-consent the Graph PowerShell app for the scopes your team uses. |
| Function capacity 4096 has been exceeded (Graph on PS 5.1) | The full Graph SDK overflows 5.1's function limit. Import only needed sub-modules, raise $MaximumFunctionCount, or use PowerShell 7. |
| Assembly with same name is already loaded (Graph) | Two Graph module versions in one session, typically after an update. Start a fresh session; uninstall older versions with Uninstall-Module if it persists. |
| Browser window never appears for sign-in | Default browser misconfiguration, a broken WebView, or a server core box. Use device code flow: Connect-ExchangeOnline -Device or Connect-MgGraph -UseDeviceCode. |
| Basic authentication is disabled | A legacy script using New-PSSession with -Authentication Basic. That pattern is permanently dead; rewrite the connection with Connect-ExchangeOnline. See the migration guide. |
The discipline that prevents wrong-tenant incidents costs four lines: connect with the tenant named explicitly, verify, work, disconnect. As a habit it looks like this:
Connect-ExchangeOnline -UserPrincipalName tech@msp.com -DelegatedOrganization customer.onmicrosoft.com Get-ConnectionInformation | Select-Object Organization, State # confirm before touching anything # ... do the work ... Disconnect-ExchangeOnline -Confirm:$false
The same shape applies to Graph with Connect-MgGraph -TenantId, Get-MgContext and Disconnect-MgGraph. Once connected, the Exchange and Entra ID builders on this site generate the commands for the actual work.