Microsoft 365 — Granting One User Access to Another User's Calendar
A complete reference: PowerShell commands, UI paths, end-user guidance, troubleshooting, and auditing.
Throughout this guide:
| Placeholder | Meaning |
|---|---|
user2@contoso.com | The calendar owner — the mailbox being shared |
user1@contoso.com | The delegate — the person receiving access |
1. Prerequisites
| Requirement | Detail |
|---|---|
| Module | ExchangeOnlineManagement (v3.x or later) |
| Admin role | Exchange Administrator, or a custom role with Mail Recipients + Mailbox Folder Permissions entries |
| Licensing | Both mailboxes must be licensed Exchange Online mailboxes |
| MFA | Supported natively by Connect-ExchangeOnline |
Install-Module ExchangeOnlineManagement -Scope CurrentUser # one-time
Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
2. Core commands
$Owner = "user2@contoso.com" # calendar owner
$Delegate = "user1@contoso.com" # who gets access
# 1. Check existing permissions
Get-MailboxFolderPermission -Identity "$($Owner):\Calendar"
# 2. Grant access
Add-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User $Delegate -AccessRights Editor
# 3. Change an existing entry (Add- fails if the user is already listed)
Set-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User $Delegate -AccessRights Reviewer
# 4. Remove access
Remove-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User $Delegate -Confirm:$false
The colon-backslash syntax (
mailbox:\Folder) is required.$($Owner):\Calendaruses a subexpression because PowerShell would otherwise treat$Owner:as a scope qualifier.
3. Access rights reference
| Right | What the delegate can do |
|---|---|
None | No access (explicit deny entry) |
AvailabilityOnly | Free/busy only — the tenant default for Default |
LimitedDetails | Free/busy + subject + location |
Reviewer | Read all item details |
Contributor | Create items; cannot read existing ones |
NonEditingAuthor | Read all; delete own items only |
Author | Read, create, edit/delete own items |
PublishingAuthor | Author + create subfolders |
Editor | Read, create, edit/delete all items |
PublishingEditor | Editor + create subfolders |
Owner | Full control, including changing permissions |
Editor is the usual choice for an assistant managing someone's calendar. Reviewer is the usual choice for visibility-only.
4. True delegate access
Folder permissions alone do not make someone a delegate. A delegate additionally receives meeting invitations and can respond on the owner's behalf.
# Delegate flag on the calendar folder
Add-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User $Delegate `
-AccessRights Editor -SharingPermissionFlags Delegate
# Delegate + can see private items
Set-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User $Delegate `
-AccessRights Editor -SharingPermissionFlags Delegate,CanViewPrivateItems
# Send on behalf of the owner
Set-Mailbox -Identity $Owner -GrantSendOnBehalfTo @{Add=$Delegate}
# Route meeting invites to the delegate
Set-CalendarProcessing -Identity $Owner -ResourceDelegates $Delegate
# Full mailbox access (only if the whole mailbox is needed, not just the calendar)
Add-MailboxPermission -Identity $Owner -User $Delegate -AccessRights FullAccess -InheritanceType All
Notes:
-SharingPermissionFlagsis only valid when-AccessRightsisEditor.CanViewPrivateItemscannot be set withoutDelegate.- Use
@{Add=...}/@{Remove=...}onGrantSendOnBehalfTo— assigning directly overwrites every existing entry.
Send on Behalf vs Send As
| Appears as | Command | |
|---|---|---|
| Send on Behalf | user1 on behalf of user2 | Set-Mailbox -GrantSendOnBehalfTo |
| Send As | user2 (no trace of user1) | Add-RecipientPermission -AccessRights SendAs |
Send As is stronger and generally reserved for shared mailboxes rather than personal ones.
5. The Default and Anonymous entries
Every calendar has two built-in principals that are easy to overlook:
Get-MailboxFolderPermission -Identity "$($Owner):\Calendar" |
Where-Object { $_.User -match "Default|Anonymous" }
# Let everyone in the organisation see subject and location
Set-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User Default -AccessRights LimitedDetails
# Ensure external/anonymous users have nothing
Set-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User Anonymous -AccessRights None
Raising Default is often a better answer than granting dozens of individual permissions. Set the org-wide baseline for new mailboxes with:
Get-OrganizationConfig | Select-Object DefaultAuthenticationPolicy, ACLableSyncedObjectEnabled
Set-MailboxFolderPermission -Identity "$($Owner):\Calendar" -User Default -AccessRights AvailabilityOnly
There is no supported tenant switch that changes the default for future mailboxes, so this is normally handled by a scheduled script over new users.
6. Non-English mailboxes
The folder is named Kalender, Calendrier, カレンダー and so on depending on the mailbox language, so a hard-coded :\Calendar fails. Resolve the name dynamically:
$CalFolder = (Get-MailboxFolderStatistics -Identity $Owner -FolderScope Calendar |
Where-Object { $_.FolderType -eq "Calendar" }).Name
Add-MailboxFolderPermission -Identity "$($Owner):\$CalFolder" -User $Delegate -AccessRights Editor
This is the single most common cause of "the mailbox folder cannot be found" errors in multinational tenants.
7. Bulk operations
Grant from CSV
CSV with columns Owner,Delegate,Rights:
Import-Csv .\CalendarAccess.csv | ForEach-Object {
$id = "$($_.Owner):\Calendar"
$existing = Get-MailboxFolderPermission -Identity $id -User $_.Delegate -ErrorAction SilentlyContinue
if ($existing) {
Set-MailboxFolderPermission -Identity $id -User $_.Delegate -AccessRights $_.Rights
} else {
Add-MailboxFolderPermission -Identity $id -User $_.Delegate -AccessRights $_.Rights
}
}
Idempotent helper function
function Set-CalendarAccess {
[CmdletBinding()]
param(
[Parameter(Mandatory)][string]$Owner,
[Parameter(Mandatory)][string]$Delegate,
[ValidateSet('AvailabilityOnly','LimitedDetails','Reviewer','Contributor',
'NonEditingAuthor','Author','PublishingAuthor','Editor',
'PublishingEditor','Owner','None')]
[string]$Rights = 'Reviewer',
[switch]$AsDelegate
)
$folder = (Get-MailboxFolderStatistics -Identity $Owner -FolderScope Calendar |
Where-Object { $_.FolderType -eq 'Calendar' }).Name
$id = "$($Owner):\$folder"
$params = @{ Identity = $id; User = $Delegate; AccessRights = $Rights }
if ($AsDelegate -and $Rights -eq 'Editor') { $params.SharingPermissionFlags = 'Delegate' }
$current = Get-MailboxFolderPermission -Identity $id -User $Delegate -ErrorAction SilentlyContinue
if ($current) {
Set-MailboxFolderPermission @params
Write-Host "Updated $Delegate -> $Owner ($Rights)" -ForegroundColor Yellow
} else {
Add-MailboxFolderPermission @params
Write-Host "Granted $Delegate -> $Owner ($Rights)" -ForegroundColor Green
}
}
# Usage
Set-CalendarAccess -Owner "user2@contoso.com" -Delegate "user1@contoso.com" -Rights Editor -AsDelegate
Tenant-wide audit report
$report = foreach ($mbx in Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox) {
$folder = (Get-MailboxFolderStatistics -Identity $mbx.UserPrincipalName -FolderScope Calendar |
Where-Object { $_.FolderType -eq 'Calendar' }).Name
Get-MailboxFolderPermission -Identity "$($mbx.UserPrincipalName):\$folder" -ErrorAction SilentlyContinue |
Where-Object { $_.User.DisplayName -notin @('Default','Anonymous') } |
Select-Object @{N='Owner';E={$mbx.UserPrincipalName}},
@{N='Delegate';E={$_.User.DisplayName}},
@{N='Rights';E={$_.AccessRights -join ','}},
@{N='Flags';E={$_.SharingPermissionFlags}}
}
$report | Export-Csv .\CalendarPermissionReport.csv -NoTypeInformation
Run this against a subset first — a full tenant scan makes two calls per mailbox and is slow on large directories.
Offboarding cleanup
When someone leaves, their entries persist on every calendar they had access to. Find and clear them:
$LeaverUpn = "user1@contoso.com"
foreach ($mbx in Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox) {
$id = "$($mbx.UserPrincipalName):\Calendar"
if (Get-MailboxFolderPermission -Identity $id -User $LeaverUpn -ErrorAction SilentlyContinue) {
Remove-MailboxFolderPermission -Identity $id -User $LeaverUpn -Confirm:$false
Write-Host "Removed from $($mbx.UserPrincipalName)"
}
}
8. Room and resource mailboxes
Resource calendars behave differently — booking is controlled by Set-CalendarProcessing, not folder rights.
Get-CalendarProcessing -Identity "room1@contoso.com" | Format-List
Set-CalendarProcessing -Identity "room1@contoso.com" `
-AutomateProcessing AutoAccept `
-AllBookInPolicy $false `
-BookInPolicy "team-group@contoso.com" `
-ResourceDelegates "user1@contoso.com" `
-AddOrganizerToSubject $true `
-DeleteSubject $false
Set -AutomateProcessing AutoUpdate when a human delegate should approve every request instead of auto-accepting.
9. External / cross-tenant calendar sharing
Sharing with someone outside the tenant is governed by a sharing policy, not by folder permissions.
Get-SharingPolicy | Format-List Name, Domains, Enabled, Default
# Allow free/busy with a specific partner domain
Set-SharingPolicy -Identity "Default Sharing Policy" `
-Domains "Anonymous:CalendarSharingFreeBusySimple",
"partner.com:CalendarSharingFreeBusyReviewer"
Domain-level values: CalendarSharingFreeBusySimple, CalendarSharingFreeBusyDetail, CalendarSharingFreeBusyReviewer, ContactsSharing.
For richer cross-tenant free/busy, Organization Relationships (New-OrganizationRelationship) or B2B direct connect are the appropriate mechanisms.
10. Where each permission is visible in the UI
| Permission type | In Exchange admin center? | Where to actually check |
|---|---|---|
| Calendar folder rights (Reviewer, Editor…) | No | Owner's Outlook, or PowerShell |
| Delegate flag / invite forwarding | No | Owner's Outlook → Delegate Access |
| Send on behalf | Yes | EAC → Recipients → Mailboxes → owner → Delegation |
| Full Access / Send As | Yes | EAC → Recipients → Mailboxes → owner → Delegation |
The most common gap: calendar-level sharing is a mailbox folder property, so it does not appear anywhere in the Microsoft 365 admin center. Get-MailboxFolderPermission remains the only reliable admin-side check.
Checking in EAC
- Go to
admin.exchange.microsoft.com→ Recipients → Mailboxes - Select the owner → Delegation tab
- Review Read and manage, Send as, Send on behalf
11. Owner-side UI steps (granting access)
New Outlook / Outlook on the web Calendar → hover My Calendars → Calendar → … → Sharing and permissions → enter the delegate → choose a permission level → Share.
Classic Outlook Calendar → right-click Calendar → Properties → Permissions tab → Add → select the delegate → set the level → OK.
Delegate access (accepts invites on the owner's behalf) Classic Outlook → File → Account Settings → Delegate Access → Add → set Calendar to Editor → tick Delegate receives copies of meeting-related messages.
Outlook mobile Calendar sharing cannot be granted from mobile; only viewing already-shared calendars is supported.
12. Delegate-side UI steps (opening the calendar)
| Client | Steps |
|---|---|
| New Outlook / OWA | Calendar → Add calendar → Add from directory → select own account → type the owner → choose Other calendars → Add |
| Classic Outlook | Calendar → Add Calendar → Open Shared Calendar → type the owner → OK |
| Outlook mobile | Calendar → menu → Add calendar → Add shared calendars → search the owner |
Important: when access is granted via PowerShell, no sharing invitation email is sent. The delegate must add the calendar manually. This is the step most people get stuck on.
Ready-to-send note for the delegate
Access to [Owner]'s calendar has been granted. Nothing will arrive in your inbox, so please add it manually: in Outlook go to Calendar → Add Calendar → Open Shared Calendar, type the owner's address, and select OK. In the web or new Outlook, use Add calendar → Add from directory. It can take up to an hour to appear; if it does not, restart Outlook once.
13. Verification
Get-MailboxFolderPermission -Identity "user2@contoso.com:\Calendar" |
Format-Table User, AccessRights, SharingPermissionFlags -AutoSize
SharingPermissionFlags showing Delegate confirms true delegate status rather than plain folder sharing.
Also worth checking:
# Who can send on behalf
Get-Mailbox -Identity $Owner | Select-Object -ExpandProperty GrantSendOnBehalfTo
# Full mailbox access
Get-MailboxPermission -Identity $Owner | Where-Object { $_.User -notlike "NT AUTHORITY\*" }
# Send As
Get-RecipientPermission -Identity $Owner | Where-Object { $_.Trustee -notlike "NT AUTHORITY\*" }
14. Troubleshooting
| Symptom / error | Cause | Fix |
|---|---|---|
| The mailbox folder cannot be found | Localised folder name, or unlicensed/uninitialised mailbox | Resolve the folder name via Get-MailboxFolderStatistics (section 6) |
| An existing permission entry was found for user | Entry already exists | Use Set-MailboxFolderPermission instead of Add- |
| There is no existing permission entry found for user | No entry to modify | Use Add-MailboxFolderPermission first |
| Couldn't find object … please make sure you've typed it correctly | Mail contact or unlicensed account | Confirm with Get-Recipient |
| Access granted but calendar not visible | Cached mode / OST not refreshed | Restart Outlook; allow up to 60 minutes; test in OWA first to isolate |
| Delegate sees free/busy only | Permission applied to the wrong folder or Default overrides expectation | Re-run Get-MailboxFolderPermission and confirm the named entry |
| Private items hidden | CanViewPrivateItems not set | Re-apply with -SharingPermissionFlags Delegate,CanViewPrivateItems |
| Delegate not receiving invites | Folder permission set but no delegate flag | Add -SharingPermissionFlags Delegate and set ResourceDelegates |
| Permission reverts after a while | A competing script or sharing policy is resetting it | Check the audit log (below) |
Audit who changed a permission
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) `
-Operations "Add-MailboxFolderPermission","Set-MailboxFolderPermission","Remove-MailboxFolderPermission" `
-ResultSize 500 | Select-Object CreationDate, UserIds, Operations, AuditData
Requires unified auditing to be enabled (Get-AdminAuditLogConfig | Select UnifiedAuditLogIngestionEnabled).
15. Microsoft Graph alternative
Graph is the better fit for app-only automation, Azure Functions, or Logic Apps where an interactive Exchange session is impractical.
POST https://graph.microsoft.com/v1.0/users/{owner-id}/calendar/calendarPermissions
Content-Type: application/json
{
"isRemovable": true,
"isInsideOrganization": true,
"role": "write",
"emailAddress": {
"address": "user1@contoso.com",
"name": "User One"
}
}
Graph role | Equivalent Exchange right |
|---|---|
freeBusyRead | AvailabilityOnly |
limitedRead | LimitedDetails |
read | Reviewer |
write | Editor |
delegateWithoutPrivateEventAccess | Editor + Delegate |
delegateWithPrivateEventAccess | Editor + Delegate + CanViewPrivateItems |
custom | Read-only value; cannot be assigned |
Required permission: Calendars.ReadWrite (delegated) or Calendars.ReadWrite (application, ideally scoped with an application access policy).
Graph does not expose Send on Behalf or Full Access — those remain Exchange PowerShell operations.
16. Cleanup
Disconnect-ExchangeOnline -Confirm:$false
Always disconnect in scripted or runbook contexts; unclosed sessions count toward the concurrent connection limit.
Quick decision guide
| Requirement | What to configure |
|---|---|
| See if someone is busy | Default = AvailabilityOnly (usually already set) |
| See subjects across the org | Default = LimitedDetails |
| Read one person's full calendar | Reviewer for that user |
| Manage a manager's calendar | Editor + -SharingPermissionFlags Delegate + GrantSendOnBehalfTo |
| Also handle private appointments | Add CanViewPrivateItems |
| Manage a meeting room | Set-CalendarProcessing with ResourceDelegates |
| Share with an external partner | Sharing policy or organization relationship |
| Access the whole mailbox | Add-MailboxPermission -AccessRights FullAccess |
No comments:
Post a Comment