Created
February 16, 2022 02:29
-
-
Save Maistho/df3fa5f8ea78e0cc87d130708e26772f to your computer and use it in GitHub Desktop.
Powershell scripts to export calendar events from outlook to ics
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
. ".\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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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))" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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