Forum Discussion

MKMVMN's avatar
MKMVMN
Copper Contributor
May 15, 2024

Add a calendar ics with Powershell

Hello

 

I need to add a shared calendar (ics) for around thirty people

 

The calendar is from a company A, shared with company B.

 

I want to add this calendar (.ics) automatically for all users in powershell, could you give me a script that would work?

 

Thank in advance

  • LeonPavesic's avatar
    LeonPavesic
    Silver Contributor

    Hi MKMVMN,

    you can try to download the .ics file and then add it tu multiple users with this script:

    # Install the Exchange Online module if not already installed
    Install-Module -Name ExchangeOnlineManagement
    
    # Import the necessary modules
    Import-Module ExchangeOnlineManagement
    
    # connect to Exchange Online
    Connect-ExchangeOnline
    
    # Path to the .ics file
    $icsFilePath = "C:\path\to\shared_calendar.ics"
    
    # List of user email addresses
    $userEmails = @(
        "email address removed for privacy reasons",
        "email address removed for privacy reasons",
        "email address removed for privacy reasons"
    )
    
    # Loop through each user and add the .ics calendar
    foreach ($userEmail in $userEmails) {
        try {
            # Access the user's mailbox
            $mailbox = Get-Mailbox -Identity $userEmail
    
            if ($mailbox) {
                # Load the .ics file
                $icsContent = Get-Content -Path $icsFilePath -Raw
    
                # Add the .ics calendar to the user's mailbox
                Add-MailboxCalendarFolder -Identity $mailbox -Name "Shared Calendar" -Calendar $icsContent
    
                Write-Output "Successfully added calendar to $userEmail"
            } else {
                Write-Output "Mailbox not found for $userEmail"
            }
        } catch {
            Write-Output "Error adding calendar to $userEmail: $_"
        }
    }
    
    # Disconnect from Exchange Online
    Disconnect-ExchangeOnline -Confirm:$false

     

    • Ensure the path to the .ics file is correct.
    • Make sure you have the necessary permissions to access and modify the users' calendars.


    Please click Mark as Best Response & Like if my post helped you to solve your issue.
    This will help others to find the correct solution easily. It also closes the item.


    If the post was useful in other ways, please consider giving it Like.


    Kindest regards,


    Leon Pavesic
    (LinkedIn)
    (Twitter)

    • MKMVMN's avatar
      MKMVMN
      Copper Contributor

      Hello LeonPavesic

       

      Thank for your help,

       

      I have this error message :

       

      PS C:\Windows\system32>  C:\test\script.ps1
      
      ----------------------------------------------------------------------------------------
      This V3 EXO PowerShell module contains new REST API backed Exchange Online cmdlets which doesn't require WinRM for Client-Server communication. You can now run these cmdlets after turning off WinRM Basic Auth in 
      your client machine thus making it more secure. 
      
      Unlike the EXO* prefixed cmdlets, the cmdlets in this module support full functional parity with the RPS (V1) cmdlets.
      
      V3 cmdlets in the downloaded module are resilient to transient failures, handling retries and throttling errors inherently. 
      
      REST backed EOP and SCC cmdlets are also available in the V3 module. Similar to EXO, the cmdlets can be run without WinRM basic auth enabled. 
      
      For more information check https://aka.ms/exov3-module
      ----------------------------------------------------------------------------------------
      
      Error adding calendar to xx@xx : The term 'Add-MailboxCalendarFolder' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the 
      name, or if a path was included, verify that the path is correct and try again.

       

    • JesseG985's avatar
      JesseG985
      Copper Contributor
      I got the same error MKMVMN got. I instead tried using Set-MailboxCalendarFolder, but got

      A Parameter cannot be found that matches parameter name 'Name'.
      • MKMVMN's avatar
        MKMVMN
        Copper Contributor

        JesseG985 

         

         

        I spent hours asking for help on forums regarding a script, but no one could assist me. I opened a ticket with Microsoft, but they couldn't help either.

         

        So, I decided to try using ChatGPT. I spent hundreds of hours testing to finally get a working script.

        I'm posting it here in case someone needs the same thing one day. However, please keep in mind that:

        • This script was tailored to my specific situation.
        • The script might throw errors due to differences in your environment.
        • It's still a temporary, unofficial solution.
        • Outlook may need to be run as administrator 
        • for chatgpt to be more efficient, I also showed it an ics file (so that it manages everything that is date format etc...)

        If you encounter any errors, I recommend revising the script with ChatGPT, providing it with the errors each time. Chatgpt is quite good at fixing them.

         

        Script Summary:

        This script automates the process of downloading calendar files in .ics format from specified URLs and synchronizing them with the user's Outlook calendar.

        1. Download ICS Files:

          • The script downloads two .ics files (you can add more) from given URLs and saves them to a hidden directory. The directory is created automatically if it doesn’t exist.
        2. Processing the ICS Files:

          • The script reads the .ics files and extracts calendar event details such as start time, end time, event title, and location.
        3. Creating/Updating Outlook Events:

          • It creates or finds corresponding folders in the user's Outlook calendar based on the file name (e.g., "MeetingRoomA").
          • It checks for existing events in these Outlook folders:
            • If the event already exists, it skips it.
            • If the event does not exist, it creates a new appointment with the event details (start/end time, title, location).
          • The script also deletes any events from the Outlook calendar that no longer exist in the .ics files, ensuring the Outlook calendar remains up-to-date.
        4. Error Handling & Clean-up:

          • Functions are included to handle errors when creating appointments or deleting existing ones, and COM objects used for Outlook interaction are released after processing to prevent performance issues.

        Key Features:

        • The script ensures no duplicate events are created and removes old events that are no longer in the source calendar.
        • The appointments created are customized with categories, reminders, and busy status.

         

         

         

         

         

        # Define the URLs for downloading and the save paths
        $urls = @(
            "https://outlook.office365.com/owa/calendar/XXX/XXX/reachcalendar.ics",
            "https://outlook.office365.com/owa/calendar/XXX/XXX/reachcalendar.ics"
        )
        
        # Get the path to the "CalendarFiles" directory for the current user
        $basePath = "C:\Company\CalendarUpdates\CalendarFiles"
        if (-not (Test-Path $basePath)) {
            $newDirectory = New-Item -Path $basePath -ItemType Directory -Force
            $newDirectory.Attributes += "Hidden"
        }
        
        $savePaths = @(
            "$basePath\MeetingRoomA.ics",
            "$basePath\MeetingRoomB.ics"
        )
        
        # Download the files
        for ($i = 0; $i -lt $urls.Length; $i++) {
            Invoke-WebRequest -Uri $urls[$i] -OutFile $savePaths[$i]
        }
        
        # Define the path to the folder containing the ICS files
        $ICSpath = $basePath
        $ICSlist = Get-ChildItem $ICSpath -Filter *.ics
        
        # Create the function to delete an existing appointment
        function DeleteAppointment {
            param ($eventData, $folder)
        
            $Start = [datetime]::ParseExact($eventData["DTSTART"], "yyyyMMddTHHmmss", $null)
            $End = [datetime]::ParseExact($eventData["DTEND"], "yyyyMMddTHHmmss", $null)
        
            foreach ($item in $folder.Items) {
                if ($item.Start -eq $Start -and $item.End -eq $End -and $item.Subject -eq $eventData["SUMMARY"]) {
                    $item.Delete()
                    break
                }
            }
        }
        
        # Create the function to create an appointment
        function CreateAppointment {
            param ($eventData, $folder)
        
            if (-not $eventData["DTSTART"] -or -not $eventData["DTEND"] -or -not $eventData["SUMMARY"]) {
                return
            }
        
            try {
                # Delete the existing event if it exists
                DeleteAppointment $eventData $folder
        
                # Add the new event
                $Start = [datetime]::ParseExact($eventData["DTSTART"], "yyyyMMddTHHmmss", $null)
                $End = [datetime]::ParseExact($eventData["DTEND"], "yyyyMMddTHHmmss", $null)
        
                $appointment = $folder.Items.Add(1) # 1 corresponds to olAppointmentItem
                $appointment.Start = $Start
                $appointment.End = $End
                $appointment.Subject = $eventData["SUMMARY"]
                if ($eventData["LOCATION"]) { $appointment.Location = $eventData["LOCATION"] }
                $appointment.Categories = "Presentations"
                $appointment.BusyStatus = 0 # 0=Free
                $appointment.ReminderMinutesBeforeStart = 15 # Customize if desired
                $appointment.ReminderSet = $false # Disable reminders
        
                $appointment.Save()
            } catch {
                # Error while creating the appointment
            }
        }
        
        # Create a new folder in Outlook named after the ICS file
        function CreateOutlookFolder {
            param ($folderName)
        
            $outlook = New-Object -ComObject Outlook.Application
            $namespace = $outlook.GetNamespace("MAPI")
            $calendarFolder = $namespace.GetDefaultFolder(9) # 9 corresponds to the Calendar folder
        
            $importedFolder = $calendarFolder.Folders | Where-Object { $_.Name -eq $folderName }
        
            if (-not $importedFolder) {
                $importedFolder = $calendarFolder.Folders.Add($folderName)
            }
        
            return $importedFolder
        }
        
        # Process the ICS files and add appointments to a folder named after the file
        foreach ($file in $ICSlist) {
            $folderName = [System.IO.Path]::GetFileNameWithoutExtension($file.Name)
            $importedFolder = CreateOutlookFolder -folderName $folderName
        
            # Retrieve the content of the ICS file
            $content = Get-Content $file.FullName -Encoding UTF8
        
            # List of events to add to Outlook
            $eventsToAdd = @()
        
            $eventData = @{}
            $content | ForEach-Object {
                if ($_ -match '^DTSTART;TZID=.*:(\d{8}T\d{6})') {
                    $eventData["DTSTART"] = $matches[1]
                } elseif ($_ -match '^DTEND;TZID=.*:(\d{8}T\d{6})') {
                    $eventData["DTEND"] = $matches[1]
                } elseif ($_ -match '^SUMMARY:(.*)$') {
                    $eventData["SUMMARY"] = $matches[1]
                } elseif ($_ -match '^LOCATION:(.*)$') {
                    $eventData["LOCATION"] = $matches[1]
                } elseif ($_ -match '^END:VEVENT$') {
                    # Add the event to the list of events to add
                    $eventsToAdd += $eventData
                    $eventData = @{}
                }
            }
        
            # Retrieve the current events in Outlook
            $currentEvents = @()
            foreach ($item in $importedFolder.Items) {
                $currentEvents += @{
                    "DTSTART" = $item.Start.ToString("yyyyMMddTHHmmss")
                    "DTEND" = $item.End.ToString("yyyyMMddTHHmmss")
                    "SUMMARY" = $item.Subject
                    "LOCATION" = $item.Location
                }
            }
        
            # Compare and update events in Outlook
            foreach ($event in $eventsToAdd) {
                $found = $false
        
                # Check if the event already exists in Outlook
                foreach ($item in $importedFolder.Items) {
                    if ($item.Start.ToString("yyyyMMddTHHmmss") -eq $event["DTSTART"] -and
                        $item.End.ToString("yyyyMMddTHHmmss") -eq $event["DTEND"] -and
                        $item.Subject -eq $event["SUMMARY"] -and
                        ($item.Location -eq $event["LOCATION"] -or (!$item.Location -and !$event["LOCATION"]))) {
                        
                        # The event already exists with the same details
                        $found = $true
                        break
                    }
                }
        
                if (!$found) {
                    # The event doesn't exist, create a new appointment
                    CreateAppointment $event $importedFolder
                }
            }
        
            # Delete events in Outlook that are not in $eventsToAdd
            foreach ($item in $importedFolder.Items) {
                $found = $false
        
                foreach ($event in $eventsToAdd) {
                    if ($item.Start.ToString("yyyyMMddTHHmmss") -eq $event["DTSTART"] -and
                        $item.End.ToString("yyyyMMddTHHmmss") -eq $event["DTEND"] -and
                        $item.Subject -eq $event["SUMMARY"] -and
                        ($item.Location -eq $event["LOCATION"] -or (!$item.Location -and !$event["LOCATION"]))) {
                        
                        # The event in Outlook is also in $eventsToAdd
                        $found = $true
                        break
                    }
                }
        
                if (!$found) {
                    # The event in Outlook is not in $eventsToAdd, delete it
                    $item.Delete()
                }
            }
        
            # Release the COM objects for this folder
            [System.Runtime.Interopservices.Marshal]::ReleaseComObject($importedFolder) | Out-Null
        }

         

         

         

Resources