One of the more common questions on EWS I get is around how to create a folder so this post will cover the basics you need to know around creating folders using EWS in Mailboxes or Public Folders.
What you need to create a Folder
To create a folder you first need a unique name for the new folder eg if you going to create a folder called "SubFolder1" under the Inbox another folder with the name Subfolder1 can't already exists as a SubFolder of the Inbox (anywhere else in the Mailbox is okay) if it does you will get an error. So when writing a script to create a folder its first good to includes a search to see if the folder already exists. eg
The second piece of information you need is what type of folder your going to create, in Exchange folders have a default ItemType property (in EWS this the FolderClass in Mapi its PR_CONTAINER_CLASS) . Eg when you create a folder is you specify the Folder Class of this Folder is for Appointment the Client will treat that folder as a Calender Folder. In EWS once this FolderClass is set the service will return a different FolderType for the following Folder
The last piece of information you need when creating a folder is you need know the ewsId of the Parent folder. If your creating a folder within a WellKnown Folder like the Inbox,Calendar,MailboxRoot etc then you can just use the FolderId overload eg for the Inbox
If this is not a WellKnownFolder or a PublicFolder then you need to first find the EWSFolderId, for this I generally recommend you great a path for the parent folder like \Inbox\ParentFolder and then use a Function like this to find the FolderId eg
or for Public Folders you need to use something like
I have a module that combines all these that I've blogged before here https://github.com/gscales/Powershell-Scripts/blob/master/CreateFolder.ps1 I've added a new cmdlet in this module to now allow you to create folder within a public folder eg
Create-PublicFolder -Mailboxname mailbox@domain.com -NewFolderName test -ParentPublicFolderPath '\Folder1\Folder2'
What you need to create a Folder
To create a folder you first need a unique name for the new folder eg if you going to create a folder called "SubFolder1" under the Inbox another folder with the name Subfolder1 can't already exists as a SubFolder of the Inbox (anywhere else in the Mailbox is okay) if it does you will get an error. So when writing a script to create a folder its first good to includes a search to see if the folder already exists. eg
#Define Folder Veiw Really only want to return one object $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) #Define a Search folder that is going to do a search based on the DisplayName of the folder $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$NewFolderName) #Do the Search $findFolderResults = $service.FindFolders($EWSParentFolder.Id,$SfSearchFilter,$fvFolderView) if ($findFolderResults.TotalCount -eq 0){ Write-host ("Folder Doesn't Exist") $NewFolder.Save($EWSParentFolder.Id) Write-host ("Folder Created") } else{ Write-error ("Folder already Exist with that Name") }
The second piece of information you need is what type of folder your going to create, in Exchange folders have a default ItemType property (in EWS this the FolderClass in Mapi its PR_CONTAINER_CLASS) . Eg when you create a folder is you specify the Folder Class of this Folder is for Appointment the Client will treat that folder as a Calender Folder. In EWS once this FolderClass is set the service will return a different FolderType for the following Folder
- Mail Folders - IPF.Note - EWS Managed API Type Folder
- Calendar Folders - IPF.Appointment - EWS Managed API Type CalendarFolder
- Contact Folders - IPF.Contact - EWS Managed API Type ContactFolder
- Task Folders - IPF.Task - EWS Managed API Type TaskFolder
The last piece of information you need when creating a folder is you need know the ewsId of the Parent folder. If your creating a folder within a WellKnown Folder like the Inbox,Calendar,MailboxRoot etc then you can just use the FolderId overload eg for the Inbox
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName) $NewFolder.Save($folderid)
If this is not a WellKnownFolder or a PublicFolder then you need to first find the EWSFolderId, for this I generally recommend you great a path for the parent folder like \Inbox\ParentFolder and then use a Function like this to find the FolderId eg
function FolderIdFromPath{ param ( $FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )", $SmtpAddress = "$( throw 'Folder Path is a mandatory Parameter' )" ) process{ ## Find and Bind to Folder based on Path #Define the path to search should be seperated with \ #Bind to the MSGFolder Root $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$SmtpAddress) $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid) #Split the Search path into an array $fldArray = $FolderPath.Split("\") #Loop through the Split Array and do a Search for each level of folder for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { #Perform search based on the displayname of each folder level $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint]) $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView) if ($findFolderResults.TotalCount -gt 0){ foreach($folder in $findFolderResults.Folders){ $tfTargetFolder = $folder } } else{ "Error Folder Not Found" $tfTargetFolder = $null break } } if($tfTargetFolder -ne $null){ return $tfTargetFolder.Id.UniqueId.ToString() } else{ throw "Folder not found" } } }
or for Public Folders you need to use something like
function PublicFolderIdFromPath{ param ( [Parameter(Position=0, Mandatory=$true)] [Microsoft.Exchange.WebServices.Data.ExchangeService]$service, [Parameter(Position=1, Mandatory=$true)] [String]$FolderPath, [Parameter(Position=2, Mandatory=$true)] [String]$SmtpAddress ) process{ ## Find and Bind to Folder based on Path #Define the path to search should be seperated with \ #Bind to the MSGFolder Root $folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::PublicFoldersRoot) $psPropset= new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties) $PR_REPLICA_LIST = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x6698,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary); $psPropset.Add($PR_REPLICA_LIST) $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid,$psPropset) $PR_REPLICA_LIST_Value = $null if($tfTargetFolder.TryGetProperty($PR_REPLICA_LIST,[ref]$PR_REPLICA_LIST_Value)){ $GuidAsString = [System.Text.Encoding]::ASCII.GetString($PR_REPLICA_LIST_Value, 0, 36); $HeaderAddress = new-object System.Net.Mail.MailAddress($service.HttpHeaders["X-AnchorMailbox"]) $pfHeader = $GuidAsString + "@" + $HeaderAddress.Host write-host ("Root Public Folder Routing Information Header : " + $pfHeader ) $service.HttpHeaders.Add("X-PublicFolderMailbox", $pfHeader) } #Split the Search path into an array $fldArray = $FolderPath.Split("\") #Loop through the Split Array and do a Search for each level of folder for ($lint = 1; $lint -lt $fldArray.Length; $lint++) { #Perform search based on the displayname of each folder level $fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1) $fvFolderView.PropertySet = $psPropset $SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint]) $findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView) if ($findFolderResults.TotalCount -gt 0){ foreach($folder in $findFolderResults.Folders){ $tfTargetFolder = $folder } } else{ "Error Folder Not Found" $tfTargetFolder = $null break } } if($tfTargetFolder -ne $null){ $PR_REPLICA_LIST_Value = $null if($tfTargetFolder.TryGetProperty($PR_REPLICA_LIST,[ref]$PR_REPLICA_LIST_Value)){ $GuidAsString = [System.Text.Encoding]::ASCII.GetString($PR_REPLICA_LIST_Value, 0, 36); $HeaderAddress = new-object System.Net.Mail.MailAddress($service.HttpHeaders["X-AnchorMailbox"]) $pfHeader = $GuidAsString + "@" + $HeaderAddress.Host write-host ("Target Public Folder Routing Information Header : " + $pfHeader ) Get-PublicFolderContentRoutingHeader -service $service -Credentials $Credentials -MailboxName $SmtpAddress -pfAddress $pfHeader } return $tfTargetFolder.Id.UniqueId.ToString() } else{ throw "Folder not found" } } }
I have a module that combines all these that I've blogged before here https://github.com/gscales/Powershell-Scripts/blob/master/CreateFolder.ps1 I've added a new cmdlet in this module to now allow you to create folder within a public folder eg
Create-PublicFolder -Mailboxname mailbox@domain.com -NewFolderName test -ParentPublicFolderPath '\Folder1\Folder2'