Automating archive folder creation in Exchange Online mailboxes

If you are using Outlook 2016 on Windows or Mac you will have noticed the recent addition of a one-click ‘Archive’ button to the ribbon. The addition of the archive button was announced at the end of February, but seems to have caught a bunch of customers by surprise and there appears to be some confusion about it’s intended purpose. If you are unfamiliar with the one-click ‘Archive’ button, here’s what it looks like:

          

The archive button is intended to make archiving email a one-click operation, but this does not archive email to an Online Archive and does not require users to have an Online Archive enabled. Instead, the button will file email to an ‘Archive’ folder in your existing mailbox. The intention here is that this button provides a single-click way to clean or declutter your inbox of messages that you have already read. It is important to understand the following about the archive button:

  • The archive button cannot be used to send email messages to the Online Archive.
  • Since the archive folder is a folder in the root of the mailbox, moving email to it will not reduce the overall size of the mailbox.

If a folder called ‘Archive’ does not already exist in the root of the mailbox, the user will be prompted to create one:

Certain organizations may feel like this creates confusion amongst their user community and would therefore like to automate the create of the ‘Archive’ folder in their user mailboxes. Fortunately, MVP (and fellow Aussie!) Glen Scales has a great solution for creating mailbox folders using PowerShell the EWS managed API. In order to use his module, you will need to download and install the EWS managed API from here. Once installed, you will need to connect to Exchange Online via remote PowerShell and import the module. It them becomes a matter of using the Create-Folder cmdlet included in the module. The module has few parameters and a lot of other functionality but we only need the following:

1
Create-Folder -MailboxName user@domain.com -NewFolderName Archive

With some minor tweaks to Glen’s code, we can easily script this process for multiple (or all) mailboxes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
function Load-EWSManagedAPI{
    param(
    )
 	Begin
	{
		## Load Managed API dll
		###CHECK FOR EWS MANAGED API, IF PRESENT IMPORT THE HIGHEST VERSION EWS DLL, ELSE EXIT
		$EWSDLL = (($(Get-ItemProperty -ErrorAction SilentlyContinue -Path Registry::$(Get-ChildItem -ErrorAction SilentlyContinue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services'|Sort-Object Name -Descending| Select-Object -First 1 -ExpandProperty Name)).'Install Directory') + "Microsoft.Exchange.WebServices.dll")
		if (Test-Path $EWSDLL)
		    {
		    Import-Module $EWSDLL
		    }
		else
		    {
		    "$(get-date -format yyyyMMddHHmmss):"
		    "This script requires the EWS Managed API 1.2 or later."
		    "Please download and install the current version of the EWS Managed API from"
		    "http://go.microsoft.com/fwlink/?LinkId=255472"
		    ""
		    "Exiting Script."
		    exit
		    }
  	}
}
function Connect-Exchange{
    param(
    	[Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
		[Parameter(Position=1, Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials
    )
 	Begin
		 {
		Load-EWSManagedAPI
## Set Exchange Version
		$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1
## Create Exchange Service Object
		$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
#Credentials
		$creds = New-Object System.Net.NetworkCredential($Credentials.UserName.ToString(),$Credentials.GetNetworkCredential().password.ToString())
		$service.Credentials = $creds
#CAS URL hardcoded for Exchange Online
		$uri=[system.URI] "https://outlook.office365.com/EWS/Exchange.asmx"
		$service.Url = $uri
        if(!$service.URL){
			throw "Error connecting to EWS"
		}
		else
		{
			return $service
		}
	}
}
function Create-Folder{
    param(
    	[Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
		[Parameter(Position=1, Mandatory=$true)] [System.Management.Automation.PSCredential]$Credentials,
		[Parameter(Position=2, Mandatory=$true)] [String]$NewFolderName
    )
 	Begin
	 {
		$service = Connect-Exchange -MailboxName $MailboxName -Credentials $Credentials
		$NewFolder = new-object Microsoft.Exchange.WebServices.Data.Folder($service)
		$NewFolder.DisplayName = $NewFolderName
        $NewFolder.FolderClass = "IPF.Note"
#Bind to the MsgFolderRoot folder
		$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
		$EWSParentFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#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") -ForegroundColor Yellow
			$NewFolder.Save($EWSParentFolder.Id)
			Write-host ("Folder Created") -ForegroundColor Green
		}
		else{
		    Write-error ("Folder already Exist with that Name")
		}
  }
}
#Define tenant credentials
$Credentials = Get-Credential
#Define mailboxes that need the archive folder created
# Get all mailboxes
$Mailboxes = Get-Mailbox -ResultSize Unlimited | Where-Object {$_.Name -notlike "DiscoverySearchMailbox*"}
#Or import a list of mailboxes from .txt
# $Mailboxes = Get-Content C:\Temp\Mailboxes.txt
#Create the folder
ForEach ($MailboxName in $Mailboxes) {
    Write-host "Processing $MailboxName" -ForegroundColor Yellow
    Create-Folder -MailboxName $MailboxName.PrimarySmtpAddress -NewFolderName Archive -Credentials $Credentials
    }

Once the ‘Archive’ folder has been created, it will become the destination for all messages that are selected when the ‘Archive’ button is clicked.

A word of caution: If you have a large number of mailboxes, you may run into throttling issues if you attempt to do this on all mailboxes at the same time so it is definitely worth considering a phased rollout in larger environments.

Glen has some great stuff on his blog so be sure to check it out here.