Forum Discussion
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
- LeonPavesicSilver 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.
- MKMVMNCopper 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.
- JesseG985Copper ContributorI got the same error MKMVMN got. I instead tried using Set-MailboxCalendarFolder, but got
A Parameter cannot be found that matches parameter name 'Name'.- MKMVMNCopper Contributor
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.
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.
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.
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.
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 }