CalendarHoliday.psm1
function New-CalendarHoliday { <# .SYNOPSIS Add a Holiday apointment to users default calendar. .DESCRIPTION Add a Holiday apointment to users default calendar. takes Holiday information from CSV or manual input and uses EWS API to create an apointment with ApplicationImpersonation rights in users defaul calendar. .NOTES Author: Michael Moshkovich Last Edit: 2020/08/25 Version 1.0 - Initial release of New-CalendarHoliday Inspiered by Andrei Ghita's post: https://developermessaging.azurewebsites.net/2012/10/29/outlook-code-importing-bank-holidays-from-an-outlook-hol-based-csv-file/ .PARAMETER FilePath Specifies the path to CSV file. File must be in the following format: Country,Holiday,Date,FreeBusyStatus USA,Test US Holiday - "Free",2020/08/26,Free Brazil,Test Brazil Holiday - "OOF",2020/12/23,OOF Canada,Test Canada Holiday - "Busy",2020/01/01,Busy .PARAMETER FilterCountry Specify the Country of which to create Holidays entries. .PARAMETER Country Specify Country for manual input. .PARAMETER Holiday Specify Holiday for manual input. .PARAMETER Date Specify Date for manual input. Accepted Format: yyyy/MM/dd .PARAMETER FreeBusyStatus Specify FreeBusyStatus for manual input. Accepted values: * Free * Tentative * Busy * OOF * WorkingElsewhere * NoData .PARAMETER PrimarySmtpAddress Specify PrimarySmtpAddress to impersonate. .PARAMETER EwsUrl Specify URL to Exchange EWS WebServicesVirtualDirectory. will use Office 365 by Default ("https://outlook.office365.com/ews/exchange.asmx"). .PARAMETER Credentials Specify Impersonating user credentials. if not specified, will try to use DefaultCredentials. .INPUTS PSobject. You can pipe objects to New-CalendatHoliday. Only $_.PrimarySmtpAddress will be mapped to PrimarySmtpAddress Parameter. .OUTPUTS None. New-CalendatHoliday does not returns any output. .EXAMPLE C:\PS> New-CalendarHoliday -Country "Canada" -Holiday "Test Canada Holiday - "Busy"" -Date 2020/01/01 -FreeBusyStatus Busy -PrimaryEmailAddress john.d@contoso.com -Credentials $Cred This Example will add manualy specified Holiday in Canada to "john.d@contoso.com" using specified Credentials. .EXAMPLE C:\PS> New-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -PrimaryEmailAddress john.d@contoso.com This Example will add all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" to "john.d@contoso.com" using Default Credentials. .EXAMPLE C:\PS> Get-Mailbox -Filter "RecipientType -eq 'UserMailbox'" | New-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -Credentials $Cred This Example will add all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" to all mailboxes of Type "UserMailbox" using specified Credentials. .EXAMPLE C:\PS> Get-User -Filter "CountryOrRegion -eq 'Canada'" | Get-Mailbox -Filter "RecipientType -eq 'UserMailbox'" | New-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -Credentials $Cred This Example will add all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" to all mailboxes of Type "UserMailbox" of users with CountryOrRegion set to "Canada" using specified Credentials. .LINK https://u-btech.com/support .LINK Remove-CalendarHoliday .LINK Inspiered by Andrei Ghita's post: https://developermessaging.azurewebsites.net/2012/10/29/outlook-code-importing-bank-holidays-from-an-outlook-hol-based-csv-file/ #> [CmdletBinding()] param( [Parameter(ParameterSetName="FileSet", Mandatory=$true)] [string]$FilePath, [Parameter(ParameterSetName="FileSet", Mandatory=$true)] [String]$FilterCountry, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [string]$Country, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [string]$Holiday, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [ValidatePattern("^(\d{4})[\.\-\/](0?[1-9]|1[012])[\.\-\/](0?[1-9]|[12][0-9]|3[01])$")] [string]$Date, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [ValidateSet("Free", "Tentative", "Busy", "OOF", "WorkingElsewhere", "NoData")] [string]$FreeBusyStatus, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [Alias("SmtpAddress", "EmailAddress")] [string]$PrimarySmtpAddress, [Parameter(Mandatory=$false)] [string]$EwsUrl = "https://outlook.office365.com/ews/exchange.asmx", [Parameter(Mandatory=$false)] [PSCredential]$Credentials ) begin { # $Service.Credentials = New-Object Net.NetworkCredential('UserName', 'Password', "zone") $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) if ($Credentials) { $Service.Credentials = New-Object Net.NetworkCredential($Credentials.UserName, $Credentials.Password) } if($PsCmdlet.ParameterSetName -eq "FileSet") { $holidayList = Import-Csv -Path $FilePath } if($PsCmdlet.ParameterSetName -eq "ManualSet") { $holidayList = New-Object PSObject -Property @{ "Country" = $country "Holiday" = $holiday "Date" = $date "FreeBusyStatus" = $freeBusyStatus } $FilterCountry = $holidayList.Country } $Service.Url = New-Object -TypeName System.Uri $EwsUrl } process{ Write-Verbose "Accessing Mailbox $PrimarySmtpAddress" Write-Verbose "Importing Holidays for $FilterCountry" $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $PrimarySmtpAddress); # $Service.AutodiscoverUrl($EmailAddress, {$True}) # This is the root folder from where we want to start searching for the folder we want to delete. # Once we find it, then we will go recursively down that folder. # Other option would be to get the FolderID and start the search straight from there $CalendarFolderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, $PrimarySmtpAddress) #$RootFolderID = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root, $EmailAddress) $CalendarFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $CalendarFolderId) foreach ($item in $holidayList) { if ($item.Country -eq $FilterCountry) { Write-Verbose "Holiday name and date: `"$($item.Holiday)`", $($item.Date)" $SearchFilterCollection = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And) $SearchFilter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Subject,$item.Holiday) $Start = New-Object System.DateTime $Start = [System.DateTime]::Parse($($item.Date)) $SearchFilter2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Start, $Start) $SearchFilter3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Location,$item.Country) $SearchFilterCollection.Add($SearchFilter1) $SearchFilterCollection.Add($SearchFilter2) $SearchFilterCollection.Add($SearchFilter3) $itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(20) $itemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties) $findResults = $Service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$SearchFilterCollection,$itemView) if ($findResults.Items.Count -eq 0) { # Create Object $Appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service # Set Subject $Appointment.Subject = $item.Holiday # Set Category #$Appointment.Categories = new-object Collections.Generic.List[string] $Appointment.Categories.Add("Holiday") # Set Location $Appointment.Location = $item.Country # Set Start Time $Appointment.Start = [System.DateTime]::Parse($item.Date) # Set End Time $Appointment.End = [System.DateTime]::Parse($item.Date).AddDays(1) # Mark as all day event $Appointment.IsAllDayEvent = $true; # Show as free $Appointment.LegacyFreeBusyStatus = [Microsoft.Exchange.WebServices.Data.LegacyFreeBusyStatus]::$($item.FreeBusyStatus) #Create Appointment will save to the default Calendar # Set no Reminder $Appointment.IsReminderSet = $false # Save Appointmant $Appointment.Save([Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone) Write-Verbose "Holiday created." }else{ Write-Verbose "Holiday exists." } } } $findResults = $null $itemView = $null $SearchFilter1 = $null $SearchFilter2 = $null $SearchFilderCollection = $null $CalendarFolder = $null $CalendarFolderId = $null $Service.ImpersonatedUserId = $null } end{} } function Remove-CalendarHoliday { <# .SYNOPSIS Removes a Holiday apointment from users default calendar. .DESCRIPTION Removes a Holiday apointment from users default calendar. takes Holiday information from CSV or manual input and uses EWS API to remove an apointment with ApplicationImpersonation rights from users defaul calendar. .NOTES Author: Michael Moshkovich Last Edit: 2020/08/25 Version 1.0 - Initial release of Remove-CalendarHoliday Inspiered by Andrei Ghita's post: https://developermessaging.azurewebsites.net/2012/10/29/outlook-code-importing-bank-holidays-from-an-outlook-hol-based-csv-file/ .PARAMETER FilePath Specifies the path to CSV file. File must be in the following format: Country,Holiday,Date,FreeBusyStatus USA,Test US Holiday - "Free",2020/08/26,Free Brazil,Test Brazil Holiday - "OOF",2020/12/23,OOF Canada,Test Canada Holiday - "Busy",2020/01/01,Busy .PARAMETER FilterCountry Specify the Country of which to remove Holidays entries. .PARAMETER Country Specify Country for manual input. .PARAMETER Holiday Specify Holiday for manual input. .PARAMETER Date Specify Date for manual input. Accepted Format: yyyy/MM/dd .PARAMETER FreeBusyStatus Specify FreeBusyStatus for manual input. Accepted values: * Free * Tentative * Busy * OOF * WorkingElsewhere * NoData .PARAMETER PrimarySmtpAddress Specify PrimarySmtpAddress to impersonate. .PARAMETER EwsUrl Specify URL to Exchange EWS WebServicesVirtualDirectory. will use Office 365 by Default ("https://outlook.office365.com/ews/exchange.asmx"). .PARAMETER Credentials Specify Impersonating user credentials. if not specified, will try to use DefaultCredentials. .INPUTS PSobject. You can pipe objects to Remove-CalendatHoliday. Only $_.PrimarySmtpAddress will be mapped to PrimarySmtpAddress Parameter. .OUTPUTS None. Remove-CalendatHoliday does not returns any output. .EXAMPLE C:\PS> Remove-CalendarHoliday -Country "Canada" -Holiday "Test Canada Holiday - "Busy"" -Date 2020/01/01 -FreeBusyStatus Busy -PrimaryEmailAddress john.d@contoso.com -Credentials $Cred This Example will remove manualy specified Holiday in Canada from "john.d@contoso.com" using specified Credentials. .EXAMPLE C:\PS> Remove-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -PrimaryEmailAddress john.d@contoso.com This Example will remove all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" from "john.d@contoso.com" using Default Credentials. .EXAMPLE C:\PS> Get-Mailbox -Filter "RecipientType -eq 'UserMailbox'" | Remove-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -Credentials $Cred This Example will remove all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" from all mailboxes of Type "UserMailbox" using specified Credentials. .EXAMPLE C:\PS> Get-User -Filter "CountryOrRegion -eq 'Canada'" | Get-Mailbox -Filter "RecipientType -eq 'UserMailbox'" | Remove-CalendarHoliday -FilePath "C:\Users\user\Desktop\holidays_template.csv" -FilterCountry "Canada" -Credentials $Cred This Example will remove all Canada Holidays specified in CSV file "C:\Users\user\Desktop\holidays_template.csv" from all mailboxes of Type "UserMailbox" of users with CountryOrRegion set to "Canada" using specified Credentials. .LINK https://u-btech.com/support .LINK New-CalendarHoliday .LINK Inspiered by Andrei Ghita's post: https://developermessaging.azurewebsites.net/2012/10/29/outlook-code-importing-bank-holidays-from-an-outlook-hol-based-csv-file/ #> [CmdletBinding()] param( [Parameter(ParameterSetName="FileSet", Mandatory=$true)] [string]$FilePath, [Parameter(ParameterSetName="FileSet", Mandatory=$true)] [String]$FilterCountry, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [string]$Country, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [string]$Holiday, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [ValidatePattern("^(\d{4})[\.\-\/](0?[1-9]|1[012])[\.\-\/](0?[1-9]|[12][0-9]|3[01])$")] [string]$Date, [Parameter(ParameterSetName="ManualSet", Mandatory=$true)] [ValidateSet("Free", "Tentative", "Busy", "OOF", "WorkingElsewhere", "NoData")] [string]$FreeBusyStatus, [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [Alias("SmtpAddress", "EmailAddress")] [string]$PrimarySmtpAddress, [Parameter(Mandatory=$false)] [string]$EwsUrl = "https://outlook.office365.com/ews/exchange.asmx", [Parameter(Mandatory=$false)] [PSCredential]$Credentials ) begin { $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1) # $Service.Credentials = New-Object Net.NetworkCredential('UserName', 'Password', "zone") if ($Credentials) { $Service.Credentials = New-Object Net.NetworkCredential($Credentials.UserName, $Credentials.Password) } $cred = $Credentials if($PsCmdlet.ParameterSetName -eq "FileSet") { $holidayList = Import-Csv -Path $FilePath } if($PsCmdlet.ParameterSetName -eq "ManualSet") { $holidayList = New-Object PSObject -Property @{ "Country" = $country "Holiday" = $holiday "Date" = $date "FreeBusyStatus" = $freeBusyStatus } $FilterCountry = $holidayList.Country } $Service.Url = New-Object -TypeName System.Uri $EwsUrl } process{ Write-Verbose "Accessing Mailbox $PrimarySmtpAddress" Write-Verbose "Importing Holidays for $FilterCountry" $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $PrimarySmtpAddress); # $Service.AutodiscoverUrl($EmailAddress, {$True}) # This is the root folder from where we want to start searching for the folder we want to delete. # Once we find it, then we will go recursively down that folder. # Other option would be to get the FolderID and start the search straight from there $CalendarFolderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar, $PrimarySmtpAddress) #$RootFolderID = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root, $EmailAddress) $CalendarFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service, $CalendarFolderId) foreach ($item in $holidayList) { if ($item.Country -eq $FilterCountry) { Write-Verbose "Holiday name and date: `"$($item.Holiday)`", $($item.Date)" $SearchFilterCollection = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And) $SearchFilter1 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Subject,$item.Holiday) $Start = New-Object System.DateTime $Start = [System.DateTime]::Parse($($item.Date)) $SearchFilter2 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Start, $Start) $SearchFilter3 = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.AppointmentSchema]::Location,$item.Country) $SearchFilterCollection.Add($SearchFilter1) $SearchFilterCollection.Add($SearchFilter2) $SearchFilterCollection.Add($SearchFilter3) $itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(20) $itemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties) $findResults = $Service.FindItems([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$SearchFilterCollection,$itemView) if ($findResults.Items.Count -gt 0) { Write-Verbose "Holiday exists." foreach($findResult in $findResults.Items) { $findResult.Load() $findResult.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete) Write-Verbose "Holiday deleted." } } } } $findResults = $null $itemView = $null $SearchFilter1 = $null $SearchFilter2 = $null $SearchFilderCollection = $null $CalendarFolder = $null $CalendarFolderId = $null $Service.ImpersonatedUserId = $null } end{} } Export-ModuleMember -Function New-CalendarHoliday, Remove-CalendarHoliday |