Within an organization I had need to bulk load behind the scenes a number of calendar items on all employee’s accounts. This script had multiple sources with the original idea coming from http://www.mikepfeiffer.net/2011/01/creating-calendar-items-with-powershell-and-the-ews-managed-api/
To get this working requires a few things.
1) Exchange 2010 with SP1 (currently running under SP2)
2) The Exchange EMS shell installed
3) The Exchange Web Services API to be installed (http://www.microsoft.com/en-us/download/details.aspx?id=28952)
4) Elevated permissions within the Exchange environment.
Beyond your normal administration rights is the requirement for the ability to Impersonate all users within the organization. Because of the level of access required to make these changes please verify with the organization that there are no legal issues with attempting this.
#Path to your holiday file $inputfile = "D:\Data\HolidayCalendar2012-2013.csv" #Path to the EWS DLL file $Webservices = "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll" #Where are the users $baseOU = "OU=My Employees,DC=liquidobject,DC=com" #Need to update each year $myyears = "2012-2013" $myuniquebody = "My Unique Calendar Automation Message" + $myyears #The above body entry was the easiest way for users to see that the items were loaded by the IT department and also provides the one place where we can go back and modify/delete calendar entries later. #If we need to run the once per year everyone run, set this to true $YearlyRun = $false #Testing mode options $testmode = $false $testuser = "Testuser99" ####################### End Options ######################## if(!(Get-Module | where {$_.Name -eq "ActiveDirectory"})){Import-Module ActiveDirectory} if(!(Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.E2010" -ErrorAction SilentlyContinue)){Add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010} if(!(Get-PSSnapin -Name "Microsoft.Exchange.Management.PowerShell.support" -ErrorAction SilentlyContinue)){Add-pssnapin Microsoft.Exchange.Management.PowerShell.support} if(!(Test-Path $Webservices)) { Write-Host "`n`nExchange Web Services API is required and is not installed`nhttp://www.microsoft.com/en-us/download/details.aspx?id=28952`n" Exit } if(!(Test-Path $inputfile)) { Write-Host "`n`nInput file of: $inputfile is missing, cannot proceed without this file.`n`n" Exit } Write-Host "`nEWS Web Services - Loading" Add-Type -Path $Webservices Write-Host "EWS Web Services API - Loaded`n" function New-CalendarItem { [CmdletBinding()] param( [Parameter(Position=1, Mandatory=$true)]$CalendarUser, [Parameter(Position=2, Mandatory=$true)]$Subject, [Parameter(Position=3, Mandatory=$true)]$Date ) $sid = (Get-ADUser -Identity $CalendarUser).SID $user = [ADSI]"LDAP://<SID=$sid>" $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1) $service.AutodiscoverUrl($user.Properties.mail) $Impersonate = (Get-Mailbox -Identity $calendaruser).PrimarySMTPAddress $ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId -ArgumentList ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress),$Impersonate $service.ImpersonatedUserId = $ImpersonatedUserId $appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service $appointment.Subject = $Subject $startdate = Get-Date($Date) $appointment.Start = $startdate $enddate = (Get-Date($Date)).adddays(1) $appointment.End = $enddate $appointment.body = $myuniquebody $appointment.LegacyFreeBusyStatus = "Free" $appointment.IsAllDayEvent = $true $appointment.IsReminderSet = $False $appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone) # Write-Host "New Entry: " $CalendarUser " - " $Subject " - " $Date $appointment = $null sleep 0.3 } $mydata = Import-Csv $inputfile Write-Host "Preparing to process" $mydata.count "calendar entries`n" #Everyone, we need this once per year if($YearlyRun) { $myusers = Get-ADuser -Filter {(mail -like "*") -and (ObjectClass -eq "user") -and (Enabled -eq $true)} -SearchBase $baseOU -Properties created | Select-Object samaccountname, created } else { #Normally grab just the last 7 days of users for a weekly run. $mydays = (Get-Date).Adddays(-7) $myusers = Get-ADuser -Filter {(mail -like "*") -and (ObjectClass -eq "user") -and (Created -gt $mydays) -and (Enabled -eq $true)} -SearchBase $baseOU -Properties created | Select-Object samaccountname, created } #sorting for easy troubleshooting $myusers = $myusers | Sort-Object samaccountname #For testing lock to a single user if($testmode) { $myusers2 = @{"samAccountName"=$testuser} } $today = Get-Date $mycount = $myusers.count $currentpos = 1 foreach($i in $myusers) { Write-Host "Loading $currentpos of $mycount for:" $i.samaccountname foreach($entry in $mydata) { if($today -lt $entry.date) { #Write-Host $entry.date " - " $entry.Subject New-CalendarItem -CalendarUser $i.samaccountname -Subject $entry.Subject -date $entry.date sleep 0.2 } else { if($YearlyRun) #only load past dates on the yearly run { New-CalendarItem -CalendarUser $i.samaccountname -Subject $entry.Subject -date $entry.date sleep 0.2 } } sleep 0.1 } $currentpos++ sleep 1.5 }
All items created are all-day events with availability as as free with no reminders to annoy the staff. This is designed for an annual run and then adjustment of the “$YearlyRun” variable for weekly loads on all new employees.
The CSV file is in the following form:
Subject,Date
New Years Day, 2013/1/1
April Fools, 2013/4/1
Be careful on the characters used in the Subject entries as extra ” or ‘ characters can cause lots of headaches.