Skip to content

Instantly share code, notes, and snippets.

@Maistho
Created February 16, 2022 02:29
Show Gist options
  • Save Maistho/df3fa5f8ea78e0cc87d130708e26772f to your computer and use it in GitHub Desktop.
Save Maistho/df3fa5f8ea78e0cc87d130708e26772f to your computer and use it in GitHub Desktop.
Powershell scripts to export calendar events from outlook to ics
. ".\Get-OutlookCalendar.ps1"
. ".\Format-String-Ics.ps1"
. ".\New-IcsEvent.ps1"
$ErrorActionPreference = "Stop"
Get-OutlookCalendar | New-IcsEvent -CalendarName "your email" -CalendarTimezone "Europe/Stockholm" -CalendarDescription "Your Calendar Name" -OutputPath .\export.ics -CalendarDomain "example.com"
Invoke-WebRequest -Method PUT -Infile .\export.ics -Uri https://your-ics-server.here
function Format-String-Ics([string] $Body) {
$Str = $Body.Replace("`n", "\n").Replace("`r", "")
$Result = ""
[Int]$i = 0
while (($Str.Length - $i) -gt 60) {
$next = [math]::Min($i + 60, $Str.Length - 1)
$Result = "$Result$($Str.Substring($i, $next - $i))`r`n "
$i = $next
}
if ($i -eq 0) {
return $Str
}
else {
return "$Result$($Str.Substring($i, $Str.Length - $i))"
}
}
function Get-OutlookCalendar {
<#
.Synopsis
This function returns appointment items from default Outlook profile
.Description
This function returns appointment items from default Outlook profile. It
uses the Outlook interop assembly to use the olFolderCalendar enumeration.
It creates a custom object consisting of Subject, Start, Duration, Location
for each appointment item.
.Example
Get-OutlookCalendar |
where-object { $_.start -gt [datetime]”5/10/2011″ -AND $_.start -lt `
[datetime]”5/17/2011″ } | sort-object Duration
Displays subject, start, duration and location for all appointments that
occur between 5/10/11 and 5/17/11 and sorts by duration of the appointment.
The sort is shortest appointment on top.
.Notes
NAME: Get-OutlookCalendar
AUTHOR: ed wilson, msft
LASTEDIT: 05/10/2011 08:36:42
KEYWORDS: Microsoft Outlook, Office
HSG: HSG-05-24-2011
.Link
Http://www.ScriptingGuys.com/blog
#Requires -Version 2.0
#>
#Add-Type -Path $env:WINDIR\assembly\GAC_MSIL\office\15.0.0.0__71e9bce111e9429c\office.dll
Add-Type -Path $env:WINDIR\assembly\GAC_MSIL\Microsoft.Office.Interop.Outlook\15.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Outlook.dll
#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)
$folder.items
} #end function Get-OutlookCalendar
function Get-Byday {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[int]
$BitMask
)
$MaskTable = @{
1 = "SU"
2 = "MO"
4 = "TU"
8 = "WE"
16 = "TH"
32 = "FR"
64 = "SA"
}
$arr = $()
foreach ($WeekDay in $MaskTable.Keys | Sort-Object) {
if ($BitMask -band $WeekDay) {
$arr += , $MaskTable[$WeekDay]
}
}
$arr -join ','
}
function Format-Date {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[datetime]
$Date
)
# Custom date formats that we want to use
$icsDateFormat = "yyyyMMddTHHmmssZ"
$Date.ToUniversalTime().ToString($icsDateFormat)
}
function Get-Email {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]
$String,
[Parameter(Mandatory)]
[string]
$CalendarDomain
)
# TODO: make something better than this
"$($String.ToLower().Replace(" ", ".").Replace("@", ''))@$CalendarDomain"
}
function Get-Recur {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
$FullEvent
)
$Pattern = $FullEvent.GetRecurrencePattern()
[datetime]$PatternEndDate = $Pattern.PatternEndDate
#[datetime]$PatternStartDate = $Pattern.PatternStartDate
#[int]$DayOfMonth = $Pattern.DayOfMonth
[int]$DayOfWeekMask = $Pattern.DayOfWeekmask
[int]$Interval = $Pattern.Interval
[int]$RecurrenceType = $Pattern.RecurrenceType
if ($RecurrenceType = 1) {
# Weekly
return Format-String-Ics "RRULE:FREQ=WEEKLY;INTERVAL=$Interval;UNTIL=$(Format-Date $PatternEndDate);BYDAY=$(Get-Byday $DayOfWeekMask)"
}
# TODO: Support other RecurrenceTypes
return ""
}
function New-IcsEvent {
[OutputType([System.IO.FileInfo[]])]
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Low')]
param
(
[Parameter(Mandatory)]
[string]
$OutputPath,
[Parameter(Mandatory)]
[string]
$CalendarName,
[Parameter(Mandatory)]
[string]
$CalendarTimezone,
[Parameter(Mandatory)]
[string]
$CalendarDescription,
[Parameter(Mandatory)]
[string]
$CalendarDomain,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[datetime]
$Start,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[datetime]
$End,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[datetime]
$CreationTime,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[datetime]
$LastModificationTime,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string]
$Subject,
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Location,
[Parameter(ValueFromPipelineByPropertyName)]
[int]
$Duration = 60,
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Body,
[Parameter(ValueFromPipelineByPropertyName)]
[bool]
$IsRecurring,
[Parameter(ValueFromPipelineByPropertyName)]
[bool]
$AllDayEvent,
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$RequiredAttendees,
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$Organizer,
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$OptionalAttendees,
[Parameter(ValueFromPipeline)]
$FullEvent,
[ValidateSet('Free', 'Busy')]
[string]
$ShowAs = 'Busy',
[ValidateSet('Private', 'Public', 'Confidential')]
[string]
$Visibility = 'Public'
)
begin {
$calStart = @"
BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054JHP//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:$CalendarName
X-WR-TIMEZONE:$CalendarTimezone
X-WR-CALDESC:$CalendarDescription
"@
$calEnd = @"
END:VCALENDAR
"@
$fileName = $OutputPath
$calStart | Out-File -FilePath $fileName -Encoding utf8 -Force
}
end {
$calEnd | Out-File -FilePath $fileName -Encoding utf8 -Force -Append
}
# Checkout RFC 5545 for more options
process {
$Now = Get-Date
$dateOnlyFormat = "yyyyMMdd"
if ($AllDayEvent) {
$StartString = "DTSTART;VALUE=DATE:$($Start.ToString($dateOnlyFormat))"
$EndString = "DTEND;VALUE=DATE:$($End.ToString($dateOnlyFormat))"
}
else {
$StartString = "DTSTART:$(Format-Date $Start)"
$EndString = "DTEND:$(Format-Date $End)"
}
$AttendeesArray = @()
$RequiredArray = $RequiredAttendees.Split(';')
$OptionalArray = $OptionalAttendees.Split(';')
foreach ($a in $RequiredArray) {
$role = 'REQ-PARTICIPANT'
$a = $a.Trim()
if ($a) {
$s = Format-String-Ics "ATTENDEE;CN=`"$a`";ROLE=$($role):mailto:$(Get-Email $a $CalendarDomain)"
$AttendeesArray += , $s
}
}
foreach ($a in $OptionalArray) {
$role = 'OPT-PARTICIPANT'
$a = $a.Trim()
if ($a) {
$s = Format-String-Ics "ATTENDEE;CN=`"$a`";ROLE=$($role):mailto:$(Get-Email $a $CalendarDomain)"
$AttendeesArray += , $s
}
}
$AttendeesString = $AttendeesArray -join "`r`n"
$optional = ""
if ($IsRecurring) {
$recur = Get-Recur $FullEvent
if ($recur) {
$optional += "$recur`r`n"
}
}
if ($AttendeesString) {
$optional += "$AttendeesString`r`n"
}
$eventObj = @"
BEGIN:VEVENT
UID:$([guid]::NewGuid())@$CalendarDomain
CREATED:$(Format-Date $CreationTime)
DTSTAMP:$(Format-Date $Now)
LAST-MODIFIED:$(Format-Date $LastModificationTime)
CLASS:$Visibility
SEQUENCE:0
$StartString
$EndString
DESCRIPTION:$(Format-String-Ics($Body))
SUMMARY:$(Format-String-Ics($Subject))
ORGANIZER;CN=`"$Organizer`":mailto:$(Get-Email $Organizer $CalendarDomain)
LOCATION:$(Format-String-Ics($Location))
TRANSP:$(if($ShowAs -eq 'Free') {'TRANSPARENT'} else {'OPAQUE'})
$($optional)END:VEVENT
"@
if ($PSCmdlet.ShouldProcess($fileName, 'Write ICS file')) {
$eventObj | Out-File -FilePath $fileName -Encoding utf8 -Force -Append
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment