Search code examples
powershelloutlookcalendarexchange-server

Retrieve Outlook appointments for other users


I want to query Outlook calendar appointments for the working days of the current week for different users (whose calendars I have access to in Outlook). I get the information for my own calendar, but how/where can I define which other user's calendar information I want to get?

This is what I've got working so far:

Function Get-OutlookCalendar
{
 Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
 $olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]
 $outlook = new-object -comobject outlook.application
 $namespace = $outlook.GetNameSpace("MAPI")
 $folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar)

 $Appointments = $folder.Items
 $Appointments.IncludeRecurrences = $true
 $Appointments.Sort("[Start]")

 $Appointments | Where-object { $_.start -gt "10/12/2020" -AND $_.start -lt "10/17/2020" -AND ($_.busystatus -eq 1 -OR $_.busystatus -eq 2) } | Select-Object -Property Subject, Start, BusyStatus   
} #end function Get-OutlookCalendar

Get-OutlookCalendar

Solution

  • I wrote a function to export shared calendars for a user at powershell.org.

    It's rather long but for completeness here it is.

    Function Export-OutlookSharedCalendar{
        <#
        .Synopsis
        Allows a user to export a shared calendar from their outlook.
        .DESCRIPTION
        Allows a user to export a shared calendar from their outlook.
        .NOTES
        Name: Export-OutlookSharedCalendar.ps1
        Author: Doug Maurer
        Version: 1.0.0.5
        DateCreated: 2020-04-14
        DateUpdated: 2020-04-14
        .LINK
        .INPUTS
        None
        .OUTPUTS
        An ics file of the calendar
        .PARAMETER Owner
        The actual owner of the shared calendar
        .PARAMETER Path
        Full path for the exported file, including the filename.
        .PARAMETER StartDate
        The start date of the desired export period
        .PARAMETER EndDate
        The end date of the desired export period
        .PARAMETER Detail
        The level of calendar detail to export.
        .PARAMETER RestrictToWorkingHours
        Used to restrict the export to working hours
        .PARAMETER IncludePrivateDetails
        Switch for including private details of the calendar items
        .PARAMETER IncludeAttachments
        Switch for including attachments with the calendar items
        .EXAMPLE
        Export-OutlookSharedCalendar -Owner 'first.last@contoso.com' -Path 'c:\temp\contoso shared calendar.ics' -startdate 01/01/2019 -enddate 09/01/2019 -detail FullDetails -AllowClobber
        Description
        -----------
        Exports specific items from shared calendar owned by first.last@constoso.com from the default (or chosen) outlook profile and exports it to 'c:\temp\contoso shared calendar.ics'
        .EXAMPLE
        Export-OutlookSharedCalendar -Owner 'first.last@contoso.com' -Path 'c:\users\windowsuser\documents\salescalendar.ics' -detail FreeBusyAndSubject -RestrictToWorkingHours
    
        .EXAMPLE
        Export-OutlookSharedCalendar -CalendarOwner 'John Smith' -FilePath 'G:\HR\Shared\PTO.ics' -Detail FreeBusyAndSubject -IncludePrivateDetails -StartDate 01/01/2017 -EndDate 12/31/2018
        #>
        [cmdletbinding()]
        Param(
    
            [alias('CalendarOwner')]
            [Parameter(Mandatory=$true)]
            $Owner,
    
            [alias('Filepath')]
            [Parameter(Mandatory=$true)]
            $Path,
    
            [datetime]$StartDate,
    
            [datetime]$EndDate,
    
            [Parameter()][ValidateSet("FreeBusyOnly","FreeBusyAndSubject","FullDetails")]$Detail = "FreeBusyOnly",
    
            [switch]$RestrictToWorkingHours = $false,
    
            [switch]$IncludeAttachments = $false,
    
            [switch]$IncludePrivateDetails = $false,
    
            [switch]$AllowClobber = $false
    
        )
    
        begin{
            function Quit-Outlook {
                try 
                {
                    if($stopoutlook){$outlook.Quit()}
                    [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($outlook)
                    [void][System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($outlook)
                    [gc]::Collect()
                    if($stopoutlook){Get-Process OUTLOOK -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue}
                }
                catch {}
                finally 
                {
                    $namespace = $outlook = $Calendarowner = $owner = $CalendarFolder = $recipient = $null
                }
                break
            }
    
            Function Stop-Function{
                Param($message)
                $Exception = [Exception]::new($message)
                $ErrorRecord = [System.Management.Automation.ErrorRecord]::new(
                    $Exception,
                    10010,
                    [System.Management.Automation.ErrorCategory]::ObjectNotFound,
                    $error[0] # usually the object that triggered the error, if possible
                )
                $PSCmdlet.WriteError($ErrorRecord)
                Quit-Outlook
            }
        }
    
        process{
        
            $ErrorActionPreference = 'stop'
    
            # make a note if outlook is not running so we can try to close it after
            if(-not [bool](Get-Process -Name outlook -ErrorAction SilentlyContinue)){$stopoutlook = $true}
    
            # load the required .NET types
            Add-Type -AssemblyName 'Microsoft.Office.Interop.Outlook'
    
            # access Outlook object model
            try{
                $outlook = New-Object -ComObject outlook.application -ErrorAction Stop
            }
            catch{
                Stop-Function 'Unable to open Outlook'
            }
    
            # connect to the appropriate location
            try{
                $namespace = $outlook.GetNameSpace('MAPI')
            }
            catch{
                Stop-Function 'Unable to load Outlook profile'
            }
    
            # prepare some objects (avoids occasional type conversion failure for recipient/calendar)
            $recipient = [Microsoft.Office.Interop.Outlook.Recipient] -as [type]
            $calendarfolder = [Microsoft.Office.Interop.Outlook.MAPIFolder] -as [type]
            $olFolderCalendar = [Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderCalendar
    
            # create a recipient object representing the owner of the shared calendar you want to export
            try{
                $recipient = $namespace.CreateRecipient($owner) # can be the full smtp address (what i prefer), display name, or alias.
                $null = $recipient.Resolve()
            }
            catch{
                Stop-Function "Error with recipient: $($owner)"
            }
    
            # get the specified user/specified default folder
            try{
                $CalendarFolder = $namespace.GetSharedDefaultFolder($recipient, $olFolderCalendar)
            }
            catch{
                Stop-Function "Error retrieving calendar for $($recipient.name)"
            }
    
            # Set up the exporter
    
            try{
                #Set up the exporter
                $calendarsharing = $CalendarFolder.GetCalendarExporter()
            }
            catch{
                Stop-Function 'Unable to create calendar exporter'
            }
    
            #assign any switches first, because detail may override these settings
            if($RestrictToWorkingHours){$CalendarSharing.RestrictToWorkingHours = $true}
            if($IncludeAttachments){$CalendarSharing.IncludeAttachments = $true}
            if($IncludePrivateDetails){$CalendarSharing.IncludePrivateDetails = $true}
    
            switch($Detail){
                "FreeBusyOnly" {
                    $CalendarSharing.CalendarDetail = 0
                    $CalendarSharing.IncludeAttachments = $false
                    $CalendarSharing.IncludePrivateDetails = $false
                }
                "FreeBusyAndSubject" {
                    $CalendarSharing.CalendarDetail = 1
                    $CalendarSharing.IncludeAttachments = $false
                    $CalendarSharing.RestrictToWorkingHours = $false
                }
                "FullDetails" {
                    $CalendarSharing.CalendarDetail = 2
                    $CalendarSharing.RestrictToWorkingHours = $false
                }
            }
    
            if($startdate){
                $CalendarSharing.startDate = $startdate
                if($enddate){
                    $CalendarSharing.endDate = $enddate
                }else{
                    $CalendarSharing.endDate = (get-date)
                }
            } else {
                $CalendarSharing.IncludeWholeCalendar = $true
            }
    
            #export the calendar
            if($pathtest = [bool](Test-Path $Path) -and ($AllowClobber -eq $false)){Write-Warning "File $path already exists and AllowClobber not specified";Quit-Outlook;break}
            if($pathtest){Remove-Item $path -Force}
            try{
                $MailItem = $CalendarSharing.SaveAsICal($Path)
                Get-ChildItem $Path
            }
            catch{
                Stop-Function "Error saving calendar file: $Path"
            }
        }
    
        end{
            Quit-Outlook
        }
    }
    

    You can also download it as a gist here Export-OutlookSharedCalendar