Set-CalendarProcessing PowerShell

If you’ve ever had to fix a meeting room that isn’t accepting bookings properly, you’ve probably ended up looking at Set-CalendarProcessing.

It’s one of those Exchange PowerShell cmdlets that looks fairly simple until you start changing the booking behaviour of a room. There are quite a few settings involved, and some of them only make sense when you understand how they work together.

This is particularly important 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 trying to document 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 so you can find inconsistencies.

Connecting to Exchange Online

Before any of this works, you need a connection to Exchange Online.

If you haven’t installed the module:

Install-Module ExchangeOnlineManagement -Scope CurrentUser

Then connect:

Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com

One thing worth knowing if you’re working from older scripts or examples: 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. There’s no point troubleshooting room settings against a connection that hasn’t been established.

When you’re finished:

Disconnect-ExchangeOnline

First, confirm it’s actually a room mailbox

Set-CalendarProcessing is for resource mailboxes. It isn’t a general-purpose calendar configuration cmdlet that you can apply to any mailbox. Microsoft documents it for resource mailboxes such as room and equipment mailboxes.

Before changing anything, check the mailbox type:

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

For a normal meeting room, you should see:

RecipientTypeDetails : RoomMailbox
ResourceType         : Room

If RecipientTypeDetails isn’t RoomMailbox, stop and investigate before changing calendar processing.

This is also a useful troubleshooting step when someone tells you that Set-CalendarProcessing isn’t doing anything. Make sure you’re actually working with the resource mailbox you think you are.

Read the current settings before changing anything

Before running Set-CalendarProcessing, I normally want to know what the room is doing now.

Get-CalendarProcessing -Identity "Boardroom@company.com" |
    Format-List

For a more focused view:

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 simply applying a block of settings copied from another room.

The settings that control normal room bookings

For a standard room, the most important setting is:

-AutomateProcessing AutoAccept

This tells the resource mailbox to automatically process incoming meeting requests.

For example:

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

With normal automatic processing, an available room accepts the request and a conflicting booking is declined.

Preventing conflicts

You may see commands online that suggest:

-AllowConflicts $false

It’s worth understanding what this actually controls.

AllowConflicts determines whether conflicting meeting requests are permitted at all. For a room that should never be double-booked:

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

The two related settings you’ll see mentioned alongside it, ConflictPercentageAllowed and MaximumConflictInstances, apply to recurring meetings and only come into play when conflicts are permitted. They control how much conflict a recurring series can contain before the whole series is declined.

So if you do allow conflicts and want to put a limit on recurring bookings:

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

The important point is that these settings are related. AllowConflicts isn’t simply a magic “prevent double-booking” switch that should be considered in isolation, and setting the tolerance parameters on a room that doesn’t allow conflicts in the first place achieves nothing.

For most rooms, allowing conflicts isn’t something I’d enable unless there is a specific reason for doing so.

Booking windows and meeting duration

Two settings are particularly useful when you want to stop a room being booked indefinitely far into the future or tied up for unreasonable lengths of time.

Booking window

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

This limits how far in advance bookings can be made.

Maximum duration

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

This limits a booking to two hours.

You can combine the settings:

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 control whether recurring meetings are allowed:

Set-CalendarProcessing `
    -Identity "Boardroom@company.com" `
    -AllowRecurringMeetings $false

You can also control how recurring bookings interact with the booking window.

For example, suppose a room can be booked 90 days in advance. Someone creates a weekly meeting today that continues for six months.

The first several meetings fall within the scheduling horizon, but later occurrences don’t.

That’s where EnforceSchedulingHorizon matters. It controls how recurring meetings that extend beyond BookingWindowInDays are handled rather than simply determining whether the initial booking is inside the window.

If you are troubleshooting a recurring meeting that behaves differently from a normal meeting, this is one of the settings I’d check.

The booking-policy settings are easy to get wrong

This is probably the most confusing part of Set-CalendarProcessing.

There are four related concepts:

  • AllBookInPolicy
  • BookInPolicy
  • AllRequestInPolicy
  • RequestInPolicy

There are also corresponding out-of-policy settings:

  • AllRequestOutOfPolicy
  • RequestOutOfPolicy

The easiest way to understand them is to separate who can book automatically from who can submit a request for approval.

Automatic booking

This:

-AllBookInPolicy $true

allows everyone to submit in-policy requests that can be automatically approved when the room is available.

If you want only specific users or groups to receive automatic approval, use:

-AllBookInPolicy $false `
-BookInPolicy "room-bookers@company.com"

The BookInPolicy list identifies the users or groups that are allowed to submit in-policy requests for automatic approval. Microsoft also notes that nested groups aren’t supported for these resource mailbox group settings, which catches people out when a distribution group contains other groups rather than users.

Delegate approval

This is where a lot of configurations go wrong.

If you want everyone to be able to request the room, but somebody needs to approve those requests, you need a different combination:

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

The important setting here is:

-AllBookInPolicy $false

If you leave AllBookInPolicy set to $true, the requests are automatically approved and the delegate approval workflow isn’t doing what you intended.

AllRequestInPolicy $true means users can submit the request for approval. ResourceDelegates identifies who can approve or reject it, and ForwardRequestsToDelegates controls whether incoming requests are forwarded to those delegates.

That combination is much more useful to understand than simply knowing what each parameter means individually.

Delegate behaviour is also the area where I’d most strongly recommend testing before rolling anything out. Submit a real booking and confirm the delegate actually receives it.

Privacy settings aren’t all the same thing

Resource mailboxes also have several settings that control what happens to the information contained in meeting requests.

For example:

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

These two settings work together.

DeleteSubject removes the original meeting subject, while AddOrganizerToSubject adds the organiser’s name. So instead of leaving something like:

Project Acquisition - Confidential

on the room calendar, the room can show the organiser rather than the original subject.

There is another setting that is easy to overlook:

-RemovePrivateProperty

When this is $true, the private flag from an incoming meeting is cleared. When it is $false, the private flag is preserved.

That matters if you’re relying on private meetings to remain private when they’re placed on a resource calendar.

I wouldn’t change these settings without first deciding what users are actually supposed to see when they open the room calendar.

External meeting requests can be a surprise

Another setting worth checking is:

-ProcessExternalMeetingMessages

This controls whether the room processes meeting requests originating outside your Exchange organisation.

For example:

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

If this is $false, external meeting requests are rejected.

This can create 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 have decided what your standard room configuration should look like, PowerShell becomes much more useful.

I’d use ForEach-Object rather than piping mailboxes straight into Set-CalendarProcessing. Pipeline binding does generally work, but it behaves inconsistently across Exchange versions and can fail quietly. Being explicit costs nothing and makes the failure obvious if something goes wrong.

Run it with -WhatIf first:

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

Read the output, confirm it’s touching the rooms you expect, then remove -WhatIf and run it properly:

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

I would be careful about running a large configuration 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 exactly the same policy.

The better approach is to define a small number of room policies and apply them deliberately.

Audit all your rooms

This is one of the most useful things you can do once you have more than a few resource mailboxes.

Instead of opening each room individually, export the current configuration:

$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
    }
}

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

Now you have something much more useful than a list of commands: a snapshot of how every room is actually configured.

One practical note. This runs Get-CalendarProcessing once per room, so on a tenant with hundreds of resource mailboxes it takes a while and can run into Exchange Online throttling. If you see intermittent failures, run it outside business hours or add a short pause between rooms:

Start-Sleep -Milliseconds 200

Open the CSV in Excel and you can quickly find rooms that don’t match the configuration you intended.

For example, you might discover that most rooms have:

AutomateProcessing  = AutoAccept
AllowConflicts      = False
BookingWindowInDays = 180

while three rooms have completely different values.

That’s often where the real problem is.

Verify the change

After making a change, don’t assume that because PowerShell accepted the command, the booking behaviour is correct.

First check the configuration:

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

Then test the actual booking behaviour.

For a normal room, test:

  • A booking when the room is free
  • A booking that overlaps an existing meeting
  • A recurring booking
  • A booking outside the scheduling window
  • A booking longer than the permitted duration
  • An external booking if external requests are expected
  • An approval request if the room uses delegates

The last step matters because resource mailbox behaviour is the result of several settings working together. Looking at one parameter in isolation doesn’t always tell you what a user will experience.

A few settings worth knowing about

There are plenty of other Set-CalendarProcessing parameters that can be useful depending on the environment.

A few worth knowing about are:

ScheduleOnlyDuringWorkHours restricts scheduling to the resource’s working hours.

AddAdditionalResponse and AdditionalResponse let you add a custom response message to meeting requests.

TentativePendingApproval controls whether new requests are added tentatively while awaiting approval.

ForwardRequestsToDelegates controls whether incoming requests are forwarded to configured resource delegates.

ProcessExternalMeetingMessages controls whether external meeting requests are processed.

RemovePrivateProperty controls whether the private flag on incoming meetings is preserved.

There are also Exchange Online-specific resource features, such as capacity enforcement, that can be relevant to newer workspace and room scenarios. The exact parameters available depend on whether you’re working with Exchange Online or an on-premises Exchange version, so it’s worth checking the current cmdlet documentation rather than assuming every parameter exists everywhere.

Where I’d start when a room isn’t behaving properly

If someone gives me a ticket saying:

“This meeting room isn’t working properly.”

I wouldn’t immediately start changing settings.

I’d work through it in this order:

  1. Confirm the mailbox is actually a RoomMailbox.
  2. Run Get-CalendarProcessing.
  3. Compare its settings with another room that behaves correctly.
  4. Check AutomateProcessing.
  5. Check the in-policy settings.
  6. Check delegates and ForwardRequestsToDelegates.
  7. Check conflict and recurring-meeting settings.
  8. Check the booking window and duration.
  9. Check external meeting processing if the request is coming from outside the organisation.
  10. Make one change at a time and test the actual booking behaviour.

That approach is much safer than copying a large Set-CalendarProcessing command from another environment and hoping it fixes the problem.

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 will automatically go to those delegates.

BookInPolicy doesn’t mean anything unless the corresponding 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 room because one or two settings were changed years ago.

That’s why I find Get-CalendarProcessing just as important as Set-CalendarProcessing.

If you’re managing a small number of rooms, understanding the important settings will save you troubleshooting time. If you’re managing dozens or hundreds, exporting the configuration and looking for differences becomes much more valuable.

Microsoft’s Set-CalendarProcessing reference and its guidance on managing resource mailboxes in Exchange Online are the places to go when you need the complete parameter list. For day-to-day administration, though, the important thing is understanding which settings work together and checking the actual configuration before changing it.

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 *