<# Author: Kulverstukas Website: http://9v.lt Date: 2015-12-01 Last update: 2017-02-24 Description: Office365 calendar synchronisation tool for AD users. This is a modified script (v2) to work with Office365, because the original (v1) only works with Exchange (confirmed with "Exchange 2010"). Office365 calendar is very sensitive to any kind of inaccuracies, so this script is fine-tuned to work, any modification could break it. For modification reference an unofficial script from Microsoft was used: https://gallery.technet.microsoft.com/office/Create-Appointments-for-779b55ad Original script can be found here (see last update for more details): http://9v.lt/blog/synchronizing-exchange-calendars-with-the-web/ ---- Reads the main list of unique URLs and emails from one URL which contains a CSV in the format of email,url. Then the script reads contents of each URL and checks the contained appointments within the specified email calendar. Script filters out 10 appointments for that day and hour and checks if a ICalUid exists - if it does, then it must be the same appointment. Then the script proceeds to check the integrity - if the subject or location has changed, then that appointment is updated. #> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 #---------- control vars ------------- $debugMode = $true # set this to $true to see output $ewsManagedApiPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll" $mainUrl = "http://localhost/somefolder/exportUrls" $adminUser = "superadmin@someorganization.onmicrosoft.com" $passwd = "randompass" #------------------------------------- # prepare to engage if (!(Get-Item -Path $ewsManagedApiPath -ErrorAction SilentlyContinue)) { throw "EWS Managed API could not be found at '$($ewsManagedApiPath)'" } [void][Reflection.Assembly]::LoadFile($ewsManagedApiPath) # create the callback to validate the redirection URL $validateRedirectionUrlCallback = { param ([string]$Url) if ($Url -eq "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml") { return $true } else { return $false } } # create Service Object if ($debugMode) { Write-Host "Authenticating with '$($adminUser)'" } $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2) $service.Credentials = New-Object System.Net.NetworkCredential($adminUser, $passwd, ""); $service.AutodiscoverUrl("superadmin@someorganization.onmicrosoft.com", $validateRedirectionUrlCallback) # download the CSV file and try to parse it try { $wc = New-Object system.Net.WebClient; $wc.Encoding = [System.Text.Encoding]::UTF8 $urls = $wc.downloadString($mainUrl) $csvUrls = ConvertFrom-CSV -Delim ',' $urls } catch { throw } foreach ($dest in $csvUrls) { if ($debugMode) { Write-Host "Processing $($dest.email)" } $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $dest.email) $rootCalId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, $dest.email) $urls = $wc.downloadString($dest.url) $csvCal = ConvertFrom-CSV $urls # start creating some appointments if (($csvCal.length+0) -ne 0) { # for some reason if the array is empty, then it won't show a number, this way it shows 0 if it's empty try { # get the calendar view $start = [DateTime]($csvCal[0].StartDate + " " + $csvCal[0].StartTime) $end = $start.AddDays(182).ToString("yyyy.MM.dd 00:00") $calView = New-Object Microsoft.Exchange.WebServices.Data.CalendarView($start, $end) $foundItems = $service.Findappointments($rootCalId, $calView) # check for nonexistent items and remove them foreach ($item in $foundItems.Items) { if ($item.Subject -like "ORG - *") { # don't remove wrong appointments :P $exists = $false foreach ($calItem in $csvCal) { $csvStart = ($calItem.StartDate + " " + $calItem.StartTime) $csvEnd = ($calItem.EndDate + " " + $calItem.EndTime) if (($item.Start -eq $csvStart) -and ($item.End -eq $csvEnd)) { $exists = $true break } } if (!$exists) { # this calendar item wasn't found in CSV, delete pl0x $item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete) if ($debugMode) { Write-Host ("Removed $($item.ICalUid) @ "+$item.Start.ToString("yyyy.MM.dd_HH:mm")+" - $($item.Subject)") -ForegroundColor cyan } } } } # update or create calendar items foreach ($calItem in $csvCal) { $startDate = [DateTime]($calItem.StartDate + " " + $calItem.StartTime) $endDate = [DateTime]($calItem.EndDate + " " + $calItem.EndTime) $exists = $false foreach ($item in $foundItems.Items) { if (($item.Subject -like "ORG - *") -and ($item.Start -eq $startDate) -and ($item.End -eq $endDate) -and ($item.ICalUid -eq $calItem.ICalUid)) { if ($debugMode) { Write-Host ("Found $($item.ICalUid) @ "+$item.Start.ToString("yyyy.MM.dd_HH:mm")+" - $($item.Subject)") -ForegroundColor yellow } if (!($item.Subject -eq $calItem.Subject) -or !($item.Location -eq $calItem.Location)) { if ($debugMode) { Write-Host ("Mismatch in $($item.ICalUid) @ "+$item.Start.ToString("yyyy.MM.dd_HH:mm")+" - $($item.Subject)") -ForegroundColor magenta } $item.Subject = $calItem.Subject $item.Location = $calItem.Location $item.Body = $calItem.Body $item.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite, [Microsoft.Exchange.WebServices.Data.SendInvitationsOrCancellationsMode]::SendToNone) if ($debugMode) { Write-Host ("Updated $($item.ICalUid) @ "+$item.Start.ToString("yyyy.MM.dd_HH:mm")+" - $($item.Subject)") -ForegroundColor green } } $exists = $true } } if (!$exists) { $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment($service) $appointment.Subject = $calItem.Subject $appointment.Location = $calItem.Location $appointment.Body = $calItem.Body $appointment.ICalUid = $calItem.ICalUid $appointment.IsReminderSet = $false $appointment.Start = [DateTime]($calItem.StartDate + " " + $calItem.StartTime) $appointment.End = [DateTime]($calItem.EndDate + " " + $calItem.EndTime) $appointment.Save($rootCalId, [Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone) if ($debugMode) { Write-Host ("Created $($appointment.ICalUid) @ "+$appointment.Start.ToString("yyyy.MM.dd_HH:mm")+" - $($appointment.Subject)") -ForegroundColor green } } } } catch { if ($debugMode) { Write-Host ("Error processing $($dest.email): "+$_.Exception.Message) -ForegroundColor red } } } }