Add Calendar Permissions in Microsoft 365 via PowerShell

Key Takeaways

  • The connection method in every old guide is dead. New-PSSession against Exchange Online stopped working in 2023. It’s Connect-ExchangeOnline from the V3 module now, and there’s no Remove-PSSession to clean up afterwards.
  • Add- only works once. If the user already has any entry on that folder, Add-MailboxFolderPermission throws. Set-MailboxFolderPermission is what you want for a change. Most scripts that “randomly fail” are hitting this.
  • The folder isn’t always called Calendar. The identity path uses the localised folder name, so :\Calendar fails on a mailbox created in German or French. Resolve it with Get-MailboxFolderStatistics instead of hard-coding.
  • Delegate and Editor aren’t the same thing. Editor can write to the calendar. A delegate also receives the meeting invites. That’s the -SharingPermissionFlags parameter, and it’s Exchange Online only.

Exchange has never given administrators a sane GUI for calendar permissions. You can set them per-mailbox in the Exchange admin center, one user at a time, clicking through a modal. For anything involving more than about three mailboxes, that stops being viable, and you end up in PowerShell whether you wanted to be there or not.

This is the reference I keep for that. It’s Exchange Online / Microsoft 365 specific — on-premises Exchange shares most of the cmdlets but not the delegate flags.

Connecting, the way that still works

Most of the guides you’ll find for this were written between 2016 and 2019 and open with New-PSSession pointed at outlook.office365.com, followed by Import-PSSession. Don’t bother. Microsoft deprecated the Remote PowerShell protocol for Exchange Online and finished switching it off in 2023. The UseRPSSession escape hatch is gone too.

The replacement is the Exchange Online PowerShell V3 module, which talks to a REST endpoint and exposes the same cmdlet surface:

Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser
Import-Module ExchangeOnlineManagement

Connect-ExchangeOnline -UserPrincipalName [email protected]

That opens a browser for modern auth, which means MFA works without the app-password contortions the old module needed. When you’re done:

Disconnect-ExchangeOnline -Confirm:$false

If you’re scripting this unattended, use certificate-based app-only authentication rather than storing a credential. That’s a different post.

Two terms do most of the work in this post. Exchange Online PowerShell is Microsoft’s remote administration module for Exchange Online, and the V3 module is the current one; the connection method changed in 2023 and most guides still teach the dead one. A mailbox folder permission is an access-control entry attached to one folder inside a mailbox rather than to the mailbox as a whole, which is what makes it possible to share a calendar without exposing mail.

Read before you write

Always look at what’s there first. Get-MailboxFolderPermission takes the same identity syntax as everything else here — MailboxID:\Folder:

Get-MailboxFolderPermission -Identity [email protected]:\Calendar

Typical output on a mailbox nobody’s touched:

FolderName  User     AccessRights      SharingPermissionFlags
----------  ----     ------------      ----------------------
Calendar    Default  {AvailabilityOnly}
Calendar    Anonymous {None}

Two entries matter here and neither is a real person:

  • Default — everyone in the tenant who isn’t listed explicitly. Out of the box it’s AvailabilityOnly: free/busy blocks, no subject, no location.
  • Anonymous — external users hitting the published calendar URL. Leave this at None unless you have a specific reason.

If you’re on a large tenant, Get-EXOMailboxFolderPermission is the REST-native equivalent and is noticeably faster across a lot of mailboxes.

The localised folder name trap

This is the one that costs an afternoon. The identity path takes the folder’s display name, in the language the mailbox was created in. A mailbox provisioned with a German locale has :\Kalender, not :\Calendar. French gets :\Calendrier. Your script works for 400 users and throws on the other 12.

Don’t hard-code it. Ask the mailbox what its calendar is called:

$mbx = "[email protected]"
$cal = (Get-MailboxFolderStatistics -Identity $mbx -FolderScope Calendar |
        Where-Object { $_.FolderType -eq "Calendar" }).Name

Get-MailboxFolderPermission -Identity "$($mbx):\$cal"

FolderType is the language-independent property. Filtering on it rather than on Name is what makes this survive contact with an international tenant.

The permission roles, and which ones are calendar-only

A delegate is distinct from a user who merely holds write access: both can edit the calendar, but only a delegate also receives the meeting invitations addressed to the mailbox owner. That difference is carried by -SharingPermissionFlags and is the single most common source of “the permission is set but it still is not working”.

Microsoft documents the roles as bundles of individual rights. These are the ones worth knowing, from the Add-MailboxFolderPermission reference:

RoleWhat it actually allows
AvailabilityOnlyFree/busy only. Calendar folders only. The default.
LimitedDetailsFree/busy plus subject and location. Calendar folders only.
ReviewerRead everything. No writes.
ContributorCreate items, but can’t read existing ones. Rarely what anyone means.
NonEditingAuthorRead all, create, delete only their own.
AuthorRead all, create, edit and delete their own.
EditorRead, create, edit and delete anything. The usual answer for a shared team calendar.
OwnerEditor plus subfolders and folder ownership. Hand out sparingly.

AvailabilityOnly and LimitedDetails exist only for calendar folders — you can’t apply them to an inbox.

Granting, changing, removing

Three cmdlets, and the distinction between the first two is where most scripts break.

First time — the user has no existing entry on that folder:

Add-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User [email protected] -AccessRights Reviewer

Changing an existing entry — including anything already covered by Default:

Set-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User [email protected] -AccessRights Editor

Run Add- against a user who already has a permission entry and it fails with an “existing permission entry was found” error. This is the single most common reason a bulk script dies a third of the way through. If you don’t know the current state, either check first or just call Set-.

Removing:

Remove-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User [email protected] -Confirm:$false

To reset the whole tenant’s default visibility to something more useful than free/busy, target Default — it’s a valid -User value:

Set-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User Default -AccessRights LimitedDetails

Delegates are not just Editors

Here’s the distinction that trips people up. Editor lets someone modify the calendar. A delegate also receives the meeting invitations and responses — the thing the mailbox owner usually actually wanted when they asked for “calendar access”.

That’s -SharingPermissionFlags, and per the Microsoft reference it’s Exchange Online only and valid only when -AccessRights is Editor:

Add-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User [email protected] -AccessRights Editor `
    -SharingPermissionFlags Delegate

Add private-item visibility on top — note that CanViewPrivateItems has to be combined with Delegate, it isn’t standalone:

Add-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User [email protected] -AccessRights Editor `
    -SharingPermissionFlags Delegate,CanViewPrivateItems

One documented limitation worth knowing before someone raises a ticket about it: the Outlook setting controlling where meeting requests get delivered — owner, delegate, or both — can’t be set from PowerShell at all. Microsoft points you at EWS for that.

Groups work, and they’re usually the right answer

-User accepts mail-enabled security groups, including nested ones. Granting to a group beats looping over its members: membership changes then flow through without anyone re-running a script.

Add-MailboxFolderPermission -Identity [email protected]:\Calendar `
    -User "SG-Reception" -AccessRights Editor

Note the constraint — it has to be a mail-enabled security group. A plain distribution group has no SID to assign rights to, and a Microsoft 365 group isn’t the same object either.

Doing it in bulk

The pattern I use, with the localisation fix folded in and Set- rather than Add- so re-running it is safe:

$target = "[email protected]"

Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox |
ForEach-Object {
    $mbx = $_.PrimarySmtpAddress

    $cal = (Get-MailboxFolderStatistics -Identity $mbx -FolderScope Calendar |
            Where-Object { $_.FolderType -eq "Calendar" }).Name

    try {
        Set-MailboxFolderPermission -Identity "$($mbx):\$cal" `
            -User $target -AccessRights Reviewer -ErrorAction Stop
        Write-Host "OK   $mbx"
    }
    catch {
        Write-Warning "FAIL $mbx - $($_.Exception.Message)"
    }
}

Two things make this survivable on a real tenant. -RecipientTypeDetails UserMailbox keeps you out of room, equipment and shared mailboxes, which usually want different rules. And the try/catch means one broken mailbox logs a warning instead of killing the run at mailbox 340 of 600.

Test it against a handful first. -WhatIf is supported on all three permission cmdlets and costs nothing.

Things that will bite you

  • Replication lag. Changes aren’t instant across the service. If a user says it didn’t work, check with Get-MailboxFolderPermission before changing anything — the permission is often already correct and Outlook simply hasn’t caught up.
  • Outlook caches aggressively. A cached-mode client can sit on a stale view of a shared calendar for a while. Testing in OWA tells you what the service actually thinks.
  • Sub-calendars are separate folders. Permissions on :\Calendar say nothing about :\Calendar\Projects. Each one is its own ACL.
  • -SendNotificationToUser is picky. It only applies to calendar folders, and only with AvailabilityOnly, LimitedDetails, Reviewer or Editor.

Sources


Related: Pushing Routes with DHCP Option 121 and 249, How to make a private VPN server in 10 minutes, Making Home Lab. Part 1

More about Mike →

← Previous
Next →