Set-CalendarProcessing PowerShell

If you’ve ever had to fix a meeting room that isn’t accepting bookings properly, you’ve probably ended up using Set-CalendarProcessing in PowerShell. It’s one of those Exchange cmdlets that looks fairly simple until you start changing how a room behaves. Quite a few settings are involved, and some of them only make sense once you understand how they work together.

That matters most when you have more than a handful of rooms. A room might have been created years ago, had a few settings changed by different administrators, and ended up behaving differently from every other room in the organisation. Rather than documenting every parameter Microsoft exposes, this article focuses on the settings that actually affect how a room behaves, the combinations that commonly cause confusion, and how to audit your rooms to find inconsistencies.

Connecting to Exchange Online

Before any of this works, you need a connection to Exchange Online. Install the module if you haven’t already, then connect:

powershell

Install-Module ExchangeOnlineManagement -Scope CurrentUser
Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com

If you’re working from older scripts, note that the Exchange Online module now uses REST-based cmdlets. Basic authentication and the old remote PowerShell session approach have been retired, so a script written a few years ago may fail at the connection step before it reaches anything to do with calendar processing. If you get an authentication error, sort that out first, because there’s no point troubleshooting room settings over a connection that hasn’t been established. Run Disconnect-ExchangeOnline when you’re finished.

First, confirm it’s actually a room mailbox

Set-CalendarProcessing is for resource mailboxes, such as room and equipment mailboxes. It isn’t a general calendar configuration cmdlet for any mailbox. Before changing anything, check the mailbox type:

powershell

Get-Mailbox -Identity "Boardroom@company.com" |
    Format-List DisplayName,PrimarySmtpAddress,RecipientTypeDetails,ResourceType

For a normal meeting room, you should see:

text

RecipientTypeDetails : RoomMailbox
ResourceType         : Room

If RecipientTypeDetails isn’t RoomMailbox, stop and investigate before changing calendar processing. This is also a useful first step when someone tells you Set-CalendarProcessing “isn’t doing anything”: make sure you’re working with the resource mailbox you think you are.

Read the current settings before changing anything

Before running Set-CalendarProcessing, find out what the room is doing now. Get-CalendarProcessing -Identity "Boardroom@company.com" | Format-List shows everything. For a more focused view of the settings this article covers:

powershell

Get-CalendarProcessing -Identity "Boardroom@company.com" |
    Format-List AutomateProcessing,
        AllowConflicts,
        ConflictPercentageAllowed,
        MaximumConflictInstances,
        BookingWindowInDays,
        MaximumDurationInMinutes,
        AllowRecurringMeetings,
        EnforceSchedulingHorizon,
        AllBookInPolicy,
        BookInPolicy,
        AllRequestInPolicy,
        RequestInPolicy,
        AllRequestOutOfPolicy,
        RequestOutOfPolicy,
        ResourceDelegates,
        ForwardRequestsToDelegates,
        ProcessExternalMeetingMessages,
        DeleteSubject,
        AddOrganizerToSubject,
        DeleteComments,
        RemovePrivateProperty,
        ScheduleOnlyDuringWorkHours

That gives you a much better starting point than applying a block of settings copied from another room.

The settings that control normal room bookings

For a standard room, the setting that matters first is AutomateProcessing. Setting it to AutoAccept tells the resource mailbox to process incoming meeting requests automatically. An available room accepts the request, and a conflicting booking is declined:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -AutomateProcessing AutoAccept

Preventing conflicts

AllowConflicts decides whether overlapping meeting requests are allowed. For a room that should never be double-booked, set it to $false:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -AutomateProcessing AutoAccept `
    -AllowConflicts $false

Two related settings, ConflictPercentageAllowed and MaximumConflictInstances, control recurring meetings, and this is where they’re easy to misread. According to Microsoft’s Set-CalendarProcessing reference, they only apply when AllowConflicts is $false. In that case, a recurring series is declined if the number or percentage of conflicting occurrences exceeds those values. If it doesn’t, the series is accepted and only the clashing occurrences are declined. When AllowConflicts is $true, the series is accepted regardless of conflicts, and both values are ignored.

So the useful combination is a room that can’t be double-booked, but that still accepts a weekly series with a couple of clashing dates:

powershell

Set-CalendarProcessing `
    -Identity "TrainingRoom@company.com" `
    -AutomateProcessing AutoAccept `
    -AllowConflicts $false `
    -ConflictPercentageAllowed 25 `
    -MaximumConflictInstances 3

The point is that these settings work together. AllowConflicts isn’t a standalone “prevent double-booking” switch, and setting the tolerance values on a room that allows conflicts achieves nothing. For most rooms, I wouldn’t allow conflicts without a specific reason.

Booking windows and meeting duration

Two settings stop a room being booked indefinitely far ahead, or tied up for unreasonable lengths of time. BookingWindowInDays limits how far in advance bookings can be made, and MaximumDurationInMinutes limits how long each booking can run. This example allows bookings up to 90 days ahead, for up to two hours each:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -BookingWindowInDays 90 `
    -MaximumDurationInMinutes 120

Whether those limits make sense depends on the room. A small meeting room might benefit from a two-hour limit, while a training room could legitimately need an entire day.

Recurring meetings are where things get interesting

Recurring meetings deserve more attention than they usually get. You can block them entirely with -AllowRecurringMeetings $false, but you can also control how they interact with the booking window.

Suppose a room can be booked 90 days in advance, and someone creates a weekly meeting today that runs for six months. The first several occurrences fall inside the scheduling horizon, but the later ones don’t. That’s where EnforceSchedulingHorizon matters. It controls how a recurring series that extends beyond BookingWindowInDays is handled, rather than just whether the first booking sits inside the window. If a recurring meeting is behaving differently from a single meeting, it’s one of the settings I’d check.

The booking policy settings are easy to get wrong

This is the part of Set-CalendarProcessing that confuses people most. There are four related settings, AllBookInPolicy, BookInPolicy, AllRequestInPolicy and RequestInPolicy, plus their out-of-policy counterparts, AllRequestOutOfPolicy and RequestOutOfPolicy. The easiest way to understand them is to separate who can book automatically from who can submit a request for approval.

Automatic booking

-AllBookInPolicy $true lets everyone make in-policy bookings that are approved automatically when the room is free. To give automatic approval only to specific users or groups, turn that off and list them:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -AllBookInPolicy $false `
    -BookInPolicy "room-bookers@company.com"

Be careful when changing BookInPolicy later. Setting it replaces the existing list rather than adding to it, so to add a user, read the current values first:

powershell

$current = (Get-CalendarProcessing -Identity "Boardroom@company.com").BookInPolicy
Set-CalendarProcessing -Identity "Boardroom@company.com" `
    -BookInPolicy ($current + "new.user@company.com")

If you use a group, test it with a real member’s booking. Membership is expanded from the group you specify, so a group that only contains other groups won’t necessarily behave as you expect.

Delegate approval

This is where many configurations go wrong. If you want everyone to be able to request the room, but somebody has to approve each request, you need a different combination:

powershell

Set-CalendarProcessing `
    -Identity "ExecutiveBoardroom@company.com" `
    -AutomateProcessing AutoAccept `
    -AllBookInPolicy $false `
    -AllRequestInPolicy $true `
    -ResourceDelegates "manager@company.com" `
    -ForwardRequestsToDelegates $true

The key setting is -AllBookInPolicy $false. If you leave it at $true, requests are approved automatically and the delegate workflow never does what you intended. AllRequestInPolicy $true lets users submit requests for approval, ResourceDelegates identifies who can approve or reject them, and ForwardRequestsToDelegates controls whether incoming requests are sent to those delegates.

Understanding that combination is far more useful than knowing what each parameter means on its own. This is also the area where testing before rollout matters most: submit a real booking and confirm the delegate actually receives it.

Privacy settings aren’t all the same thing

Resource mailboxes have several settings that control what happens to the information in meeting requests. Two of them work together:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -DeleteSubject $true `
    -AddOrganizerToSubject $true

DeleteSubject removes the original meeting subject, and AddOrganizerToSubject adds the organiser’s name. So instead of “Project Acquisition – Confidential” appearing on the room calendar, the room shows who booked it.

RemovePrivateProperty is easy to overlook. When it’s $true, the private flag on an incoming meeting is cleared, and when it’s $false, the flag is kept. That matters if you’re relying on private meetings staying private once they land on a resource calendar. I wouldn’t change any of these settings without first deciding what users should actually see when they open the room calendar.

External meeting requests can be a surprise

ProcessExternalMeetingMessages controls whether the room processes meeting requests from outside your Exchange organisation. If it’s $false, external requests are rejected. To allow them:

powershell

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -ProcessExternalMeetingMessages $true

This explains a particularly confusing support ticket: “The room works perfectly for internal meetings, but an external customer can’t book it.” Before troubleshooting Outlook, Teams or calendar permissions, check the room’s calendar processing settings.

Applying the same policy to every room

Once you’ve decided what your standard room configuration should look like, PowerShell becomes much more useful. I prefer ForEach-Object with an explicit -Identity over piping mailboxes straight into Set-CalendarProcessing, because it makes it obvious which room each change applies to. Run it with -WhatIf first:

powershell

Get-Mailbox -ResultSize Unlimited `
    -Filter "RecipientTypeDetails -eq 'RoomMailbox'" |
    ForEach-Object {
        Set-CalendarProcessing `
            -Identity $_.Identity `
            -ScheduleOnlyDuringWorkHours $true `
            -MaximumDurationInMinutes 120 `
            -WhatIf
    }

Read the output and confirm it’s touching the rooms you expect, then remove -WhatIf and run it for real. Be careful about applying a large change like this blindly, though. If your organisation has executive rooms, training rooms, interview rooms and general meeting rooms, they probably shouldn’t all have the same policy. It’s better to define a small number of room policies and apply each one deliberately.

Audit all your rooms

Once you have more than a few resource mailboxes, this is one of the most valuable things you can do. Instead of opening each room individually, export every room’s configuration:

powershell

$Rooms = Get-Mailbox -ResultSize Unlimited `
    -Filter "RecipientTypeDetails -eq 'RoomMailbox'"

$Report = foreach ($Room in $Rooms) {

    $Config = Get-CalendarProcessing -Identity $Room.Identity

    [PSCustomObject]@{
        DisplayName                    = $Room.DisplayName
        PrimarySmtpAddress             = $Room.PrimarySmtpAddress
        AutomateProcessing             = $Config.AutomateProcessing
        BookingWindowInDays            = $Config.BookingWindowInDays
        MaximumDurationInMinutes       = $Config.MaximumDurationInMinutes
        AllowRecurringMeetings         = $Config.AllowRecurringMeetings
        AllowConflicts                 = $Config.AllowConflicts
        ConflictPercentageAllowed      = $Config.ConflictPercentageAllowed
        MaximumConflictInstances       = $Config.MaximumConflictInstances
        AllBookInPolicy                = $Config.AllBookInPolicy
        BookInPolicy                   = ($Config.BookInPolicy -join ';')
        AllRequestInPolicy             = $Config.AllRequestInPolicy
        RequestInPolicy                = ($Config.RequestInPolicy -join ';')
        AllRequestOutOfPolicy          = $Config.AllRequestOutOfPolicy
        RequestOutOfPolicy             = ($Config.RequestOutOfPolicy -join ';')
        ResourceDelegates              = ($Config.ResourceDelegates -join ';')
        ProcessExternalMeetingMessages = $Config.ProcessExternalMeetingMessages
        DeleteSubject                  = $Config.DeleteSubject
        AddOrganizerToSubject          = $Config.AddOrganizerToSubject
        RemovePrivateProperty          = $Config.RemovePrivateProperty
        ScheduleOnlyDuringWorkHours    = $Config.ScheduleOnlyDuringWorkHours
    }

    Start-Sleep -Milliseconds 200
}

$Report | Export-Csv `
    -Path ".\RoomMailboxCalendarProcessing.csv" `
    -NoTypeInformation `
    -Encoding UTF8

The script runs Get-CalendarProcessing once per room. On a tenant with hundreds of resource mailboxes that takes a while and can run into Exchange Online throttling, which is why there’s a short pause between rooms. If you still see intermittent failures, run it outside business hours.

Open the CSV in Excel and you can quickly spot rooms that don’t match the configuration you intended. You might find that most rooms have AutomateProcessing set to AutoAccept, AllowConflicts set to False and BookingWindowInDays set to 180, while three rooms have completely different values. That’s often where the real problem is.

Verify the change

Don’t assume that because PowerShell accepted the command, the booking behaviour is right. Check the configuration first:

powershell

Get-CalendarProcessing -Identity "Boardroom@company.com" |
    Format-List AutomateProcessing,BookingWindowInDays,MaximumDurationInMinutes

Then test the actual booking behaviour. For a normal room, try a booking when the room is free, one that overlaps an existing meeting, and a recurring series. Also try a booking outside the scheduling window and one longer than the permitted duration. Add an external booking if external requests are expected, and an approval request if the room uses delegates. Resource mailbox behaviour comes from several settings working together, so looking at one parameter in isolation doesn’t always tell you what a user will experience.

A few other settings worth knowing about

There are plenty of other Set-CalendarProcessing parameters that can be useful, depending on your environment:

  • ScheduleOnlyDuringWorkHours restricts scheduling to the resource’s working hours.
  • AddAdditionalResponse and AdditionalResponse add a custom response message to meeting requests.
  • TentativePendingApproval controls whether new requests are added tentatively while they await approval.

Exchange Online also has newer resource features, such as capacity enforcement, that matter for some workspace and room scenarios. The parameters available differ between Exchange Online and on-premises Exchange versions, so check the current cmdlet documentation rather than assuming every parameter exists everywhere. Microsoft’s guide to managing resource mailboxes in Exchange Online covers the broader setup.

The useful part of Set-CalendarProcessing

The cmdlet itself isn’t particularly complicated. The difficult part is understanding how the settings interact. AutoAccept doesn’t mean every request will be accepted. ResourceDelegates doesn’t mean requests automatically go to those delegates. BookInPolicy means nothing unless the matching in-policy behaviour is configured correctly. BookingWindowInDays has implications for recurring meetings. And a room that looks correctly configured can still behave differently from another because one or two settings were changed years ago.

That’s why I find Get-CalendarProcessing just as important as Set-CalendarProcessing. With a small number of rooms, understanding the key settings saves troubleshooting time. With dozens or hundreds, exporting the configuration and looking for differences becomes far more valuable.

Room mailbox troubleshooting checklist

When a ticket says “this meeting room isn’t working properly”, don’t start changing settings straight away. Work through it in this order:

  1. Confirm the mailbox’s RecipientTypeDetails is RoomMailbox.
  2. Run Get-CalendarProcessing and save the current settings before changing anything.
  3. Compare its settings with a room that behaves correctly, or with your audit CSV.
  4. Check AutomateProcessing.
  5. Check the in-policy settings, especially whether AllBookInPolicy is overriding a delegate workflow.
  6. Check ResourceDelegates and ForwardRequestsToDelegates, then test that a delegate actually receives a request.
  7. Check AllowConflicts, and remember that the conflict tolerance values only apply when it’s $false.
  8. Check the booking window, maximum duration and recurring meeting settings.
  9. Check ProcessExternalMeetingMessages if the request comes from outside the organisation.
  10. Make one change at a time, and test the actual booking behaviour after each one.

References

One thought on “Set-CalendarProcessing in Exchange Online: Managing Room Mailboxes with PowerShell”
  1. OK. So a curly one. Delegates of resource calendars in our org are complaining that conflicts are being allowed which is taking up time when the resource is already booked and they have to decline. Microsoft are telling me that this is expected behaviour with the delegates being the authoritative source, so automation is completely bypassed and doesn’t even process conflicts.
    The problem is that this appears to be a recent change as all our calendars are set to not accept any conflicts.
    Is this behaviour something that you can confirm?

Leave a Reply

Your email address will not be published. Required fields are marked *