Using scripts for Microsoft Bookings calendar and mailbox

Altamish Ahmad

Microsoft Bookings let’s enterprise organizations and small business owners manage customer bookings and information with minimal setup. Today, we’re going to take a deeper look at using scripts to get information about your organization’s Bookings calendars and mailboxes.

When creating a Bookings calendar, a scheduling mailbox is also created. This mailbox stores details of the Bookings calendar such as Appointments, Services, Staff Members, and Business details. Tenant admins want to be aware of these mailboxes, the users and usage.

Common questions tenant/IT admins may have

What are the Bookings calendars in my organization?

Tenant/IT admins want the list of all the Bookings calendars in their organization.

Who are the users of a Booking calendars?

Tenant admins are concerned about who is using the Bookings calendar within the organization, typically wanting to know which staff members have an “admin” role. These admins can modify settings of the calendar and can create and manage appointments for themselves and others.

Do we have proper admin coverage on the Bookings calendars?

If a Bookings calendar has no staff members with an “admin” role, it is considered orphan. This can happen when the existing admins are removed from the calendar or they leave your organization. The content in the calendar is unaffected by this – the content isn’t tied to any admin’s account, but not having an admin means there’s nobody with permissions to manage the calendar.

Is the Booking calendar being used?

A Bookings calendar is considered “unused” if no appointments have been created on it in a defined period of time. For example, one of our customers shared that if there is no appointment created within one quarter (3 months), they consider the calendar inactive or unused.

Is the Booking calendar published?

Tenant admins may want to know if the Bookings calendar is published for customers to create appointments.

Sample Scripts

To address these questions, we have created a script which allows tenant admins to gather all this information from PowerShell. Some of these PowerShell commands are derived from existing Bookings Graph APIs and will need the permissions for those APIs.

Please remember that these are guidelines. You should always refer to the latest updates for Microsoft Graph and Microsoft Graph PowerShell Cmdlets to ensure the scripts are using the latest and updated syntax.

List of Bookings Calendars

Get the list of Booking calendars from this Microsoft Graph API. Alternatively, you can use the following commands to get the list of Bookings calendars in the organization:

“Get-Mailbox -RecipientTypeDetails SchedulingMailbox -ResultSize:Unlimited”.

Initial Script

 #connecting to MgGraph
Select-MgProfile beta
Connect-MgGraph -Scopes "User.Read.All","Bookings.Manage.All,Calendars.Read.Shared" | Out-null
 
#Getting the booking business details
$MBInfo = get-mgbookingbusiness -BookingBusinessId $BookingBusinessId
 
#getting the appointment and staff related data
$appointments = Get-MgBookingBusinessAppointment -BookingBusinessId $BookingBusinessId -Top 500
$appointmentCount =  $appointments.count
 
$StaffData = Get-MgBookingBusinessStaffMember -BookingBusinessId $BookingBusinessId -All
$AdminData = $StaffData | Where-Object -FilterScript {$_.Role -EQ 'administrator'}
$adminStaffCount = $AdminData.count

In the script above, we connect to Microsoft Graph Booking APIs through PowerShell. We get the appointment and staff data for the Booking Business (denoted in the variable “BookingBusinessId”). We use this as a base for the code snippets below:

Users, admins, and orphan calendars

 $list = New-Object Collections.Generic.List[String]
 foreach($staff in $staffData){
 $list.add($staff.displayName + ':' + $staff.Role)
 }
 $staffMemberList = ($list | Out-string)

The above gets the list of all users or staff members in the calendar. Note that the staff members tagged with role ‘administrator’ are the admins of the Bookings calendar. Remember, a Bookings calendar is orphaned if there are no staff members with “admin” role. The first clue to this is that the variable “adminStaffCount” will be 0. To confirm, we can check if the staff members with the administrator role exists in the organization.

$count = 0;
foreach($admin in $AdminData){
 
  try {
    $user = Get-MgUser -UserId $admin.EmailAddress -ErrorAction Stop
    if(-not $user.accountEnabled) {
    $count++
  	}
  }
  catch {
    if ($PSItem.Exception.Code -eq "Request_ResourceNotFound") {
    $count++;
  	}
  }
}
 
$orphan = ($adminStaffCount -eq $count)

The above code fragment checks if the staff member’s email id is enabled and if it exists in organization. If none of the administrators exist (or are ), the calendar is orphan.

Appointment Counts and Unused Calendars

One of the ways you can determine if your calendar is unused is to check when the last appointment was created. You may also want to see the count of appointments in the last ‘n’ months.

$today = Get-Date
 
$endDate = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId(($today), 'UTC')
$startDate = $endDate.AddMonths(-6)
 
$appointmentCountLast6Months = (Get-MgUserCalendarView -UserId $BookingBusinessId -StartDateTime $startDate -EndDateTime $endDate -All).count
 
 
#---------------------------- Last Appointment Creation Date ----------------------------
$lastCreatedEvent = Get-MgUserEvent -UserId $BookingBusinessId -Top 1
$lastAppointmentCreationDate = $lastCreatedEvent.CreatedDateTime

Other information in the Bookings Calendar

The list of Bookings Calendar attributes can be found here. An example to get some key attributes is given below:

$Details = [ordered]@{
      'Display Name' = $MBInfo.DisplayName
      'Id' = $MBInfo.Id
      'Is Published?' = $MBInfo.IsPublished
      'Phone' = $MBInfo.Phone
      'Public Url' = $MBInfo.PublicUrl
      'Website Url' = $MBInfo.WebSiteUrl
 	 }

Get started with resources

Read the documentation:

We hope this gives you some ideas on how to use Bookings calendars and mailboxes. Provide feedback and keep reaching out to us with your use cases and requests.

Stay tuned for more updates:

Happy coding!

1 comment

Discussion is closed. Login to edit/delete existing comments.

  • Admin Christopher Jones 2

    Hi, great article. However, I’m having issues when I attempt to run your code, I hope you can help as I believe I’m missing something. As you can see I’m attempting to connect with the most privileged scopes and I’m registered as a Global Admin for my organisation. Here is the script I’m running.

    #Requires -Version 7
    #Requires -Modules Microsoft.Graph.Calendar, Microsoft.Graph.Bookings, ExchangeOnlineManagement
    Disconnect-ExchangeOnline
    Disconnect-MgGraph
    Connect-ExchangeOnline
    Select-MgProfile beta
    Connect-MgGraph -Scopes "User.Read.All", "Bookings.Manage.All", "Bookings.Read.All", "Bookings.ReadWrite.All", "BookingsAppointment.ReadWrite.All" | Out-null
    
    $allBookingsMailboxes = Get-ExoMailbox -RecipientTypeDetails SchedulingMailbox -ResultSize:1
    foreach($calendar in $allBookingsMailboxes)
    {
        #Getting the booking business details
    $MBInfo = get-mgbookingbusiness -BookingBusinessId $calendar.UserPrincipalName
    
    #getting the appointment and staff related data
    $appointments = Get-MgBookingBusinessAppointment -BookingBusinessId $calendar.UserPrincipalName -Top 500
    $appointmentCount =  $appointments.count
    $StaffData = Get-MgBookingBusinessStaffMember -BookingBusinessId $calendar.UserPrincipalName -All
    $AdminData = $StaffData | Where-Object -FilterScript {$_.Role -EQ 'administrator'}
    $adminStaffCount = $AdminData.count
    
    $list = New-Object Collections.Generic.List[String]
    foreach($staff in $staffData){
    $list.add($staff.displayName + ':' + $staff.Role)
    }
    $staffMemberList = ($list | Out-string)
    
    
    $count = 0;
    foreach($admin in $AdminData){
      try {
        $user = Get-MgUser -UserId $admin.EmailAddress -ErrorAction Stop
        if(-not $user.accountEnabled) {
        $count++
      	}
      }
      catch {
        if ($PSItem.Exception.Code -eq "Request_ResourceNotFound") {
        $count++;
      	}
      }
    }
    $orphan = ($adminStaffCount -eq $count)
    
    $today = Get-Date
    $endDate = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId(($today), 'UTC')
    $startDate = $endDate.AddMonths(-6)
    $appointmentCountLast6Months = (Get-MgUserCalendarView -UserId $calendar.UserPrincipalName -StartDateTime $startDate -EndDateTime $endDate -All).count
    #---------------------------- Last Appointment Creation Date ----------------------------
    $lastCreatedEvent = Get-MgUserEvent -UserId $calendar.UserPrincipalName -Top 1
    $lastAppointmentCreationDate = $lastCreatedEvent.CreatedDateTime
    }

    Here are the errors I’m getting:

    Get-MgBookingBusiness_Get: E:\Scripts\Dev\cjj8\BookingsOrphans.ps1:13:1
    Line |
      13 |  $MBInfo = get-mgbookingbusiness -BookingBusinessId $calendar.UserPrin …
         |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | Access is denied. Check credentials and try again., Cannot find row based on condition.
    Get-MgBookingBusinessAppointment_List: E:\Scripts\Dev\cjj8\BookingsOrphans.ps1:16:1
    Line |
      16 |  $appointments = Get-MgBookingBusinessAppointment -BookingBusinessId $ …
         |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The specified object was not found in the store., Default folder Calendar not found.
    Get-MgBookingBusinessStaffMember_List: E:\Scripts\Dev\cjj8\BookingsOrphans.ps1:18:1
    Line |
      18 |  $StaffData = Get-MgBookingBusinessStaffMember -BookingBusinessId $cal …
         |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The specified object was not found in the store., Default folder Calendar not found.
    Get-MgUserCalendarView_List: E:\Scripts\Dev\cjj8\BookingsOrphans.ps1:48:1
    Line |
      48 |  $appointmentCountLast6Months = (Get-MgUserCalendarView -UserId $calen …
         |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The specified object was not found in the store.
    Get-MgUserEvent_List: E:\Scripts\Dev\cjj8\BookingsOrphans.ps1:50:1
    Line |
      50 |  $lastCreatedEvent = Get-MgUserEvent -UserId $calendar.UserPrincipalNa …
         |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | The specified object was not found in the store.

    Any help or advice anyone could give would be helpful.

    Edit: Fun Fact! Apparently you have to be an Administrator of the Calendar in question in order to retrieve any info from it whatsoever. Apparently the fact you are Global Admin and Connect with the most permissive scopes does not factor into it at all.

Feedback usabilla icon