Forum Discussion
Add a calendar ics with Powershell
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.
A Parameter cannot be found that matches parameter name 'Name'.
- MKMVMNSep 18, 2024Copper 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 }