All scripts
Microsoft 365 617

Calendar Permissions Report

Audits Calendar folder permissions across Exchange Online mailboxes, so you can see who has access to whose calendar and at what level before an access review or an offboarding.

Get-CalendarPermissionsReport.ps1
<#
.SYNOPSIS
    Reports calendar folder permissions across user mailboxes in Exchange Online.

.DESCRIPTION
    Connects to Exchange Online and checks the Calendar folder permissions for every
    user mailbox, or a specific list you provide. Useful before an access review, an
    offboarding, or when you just want to know who can see or edit someone else's
    calendar. Skips the Default and Anonymous entries unless you ask for them, since
    those show up on almost every calendar and just add noise to the report.

.PARAMETER Mailboxes
    Optional list of specific mailbox addresses to check. If you leave this out, the
    script checks every user mailbox in the tenant.

.PARAMETER IncludeDefaultAndAnonymous
    Switch to include the Default and Anonymous permission entries in the results.
    Off by default.

.PARAMETER OutputPath
    Path for the CSV report. Defaults to CalendarPermissionsReport.csv in the current
    folder.

.EXAMPLE
    .\Get-CalendarPermissionsReport.ps1

    Checks every user mailbox in the tenant and writes CalendarPermissionsReport.csv.

.EXAMPLE
    .\Get-CalendarPermissionsReport.ps1 -Mailboxes "jane@contoso.com","sales@contoso.com" -OutputPath "C:\Reports\calendars.csv"

    Checks only the two named mailboxes and writes the report to the given path.

.AUTHOR
    Shehryar Hassan
#>

[CmdletBinding()]
param(
    [string[]]$Mailboxes,
    [switch]$IncludeDefaultAndAnonymous,
    [string]$OutputPath = ".\CalendarPermissionsReport.csv"
)

if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
    Write-Error "The ExchangeOnlineManagement module is not installed. Run: Install-Module ExchangeOnlineManagement"
    return
}

Import-Module ExchangeOnlineManagement

try {
    Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop
}
catch {
    Write-Error "Could not connect to Exchange Online. $_"
    return
}

if (-not $Mailboxes) {
    Write-Host "No mailbox list given, pulling every user mailbox in the tenant..."
    $Mailboxes = (Get-EXOMailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited).PrimarySmtpAddress
}

$results = New-Object System.Collections.Generic.List[Object]
$total = $Mailboxes.Count
$counter = 0

foreach ($mailbox in $Mailboxes) {
    $counter++
    Write-Progress -Activity "Checking calendar permissions" -Status "$mailbox ($counter of $total)" -PercentComplete (($counter / $total) * 100)

    $calendarFolder = "$mailbox`:\Calendar"

    try {
        $permissions = Get-MailboxFolderPermission -Identity $calendarFolder -ErrorAction Stop
    }
    catch {
        Write-Warning "Skipped $mailbox, could not read the Calendar folder. $_"
        continue
    }

    foreach ($permission in $permissions) {
        if (-not $IncludeDefaultAndAnonymous -and $permission.User.DisplayName -in @("Default", "Anonymous")) {
            continue
        }

        $results.Add([PSCustomObject]@{
            Mailbox          = $mailbox
            GrantedTo        = $permission.User.DisplayName
            AccessRights     = ($permission.AccessRights -join ", ")
            SharingPermFlags = $permission.SharingPermissionFlags
        })
    }
}

Write-Progress -Activity "Checking calendar permissions" -Completed

if ($results.Count -eq 0) {
    Write-Host "No calendar permissions found to report."
}
else {
    $results | Sort-Object Mailbox, GrantedTo | Export-Csv -Path $OutputPath -NoTypeInformation
    Write-Host "Done. $($results.Count) permission entries written to $OutputPath"
}

Disconnect-ExchangeOnline -Confirm:$false

Read it before you run it, and test in a safe tenant first.