How to pin custom app tiles on behalf of your users in Office 365

The app launcher in Office 365 is how users can quickly get to any workload no matter where they are in Office 365.  It is accessed by clicking the waffle (though I see it more as a keypad) in the upper left corner.  This image is the default tile layout for an E5 admin:

Admins can add custom tiles to the tenant that point to any URL.  These custom tiles then show up under the ALL tab for users.  Here is an example of one I added to my tenant that just points to this blog’s RSS feed (hence the icon):

You may want to not just add the tile, but also pin it to your users’ HOME tab.  Office 365 does not currently allow admins to pin tiles for users; you can only pin apps for your own account.  But that didn’t stop me from figuring out where these settings are stored and manipulating them programmatically.

App launcher settings are stored in a user’s mailbox.  This is why a user needs to have an Exchange Online mailbox in order to customize their app launcher.  The settings for the app launcher are in the PR_ROAMING_DICTIONARY property of the IPM.Configuration.Suite.Storage message at the root folder of the mailbox.  EWS has a class for working with user configuration settings that are stored in a dictionary property, so you don’t have to manually work with the binary property.  Using PowerShell and the EWS Managed API, get the value of this property (the credentials and email address of the mailbox have already been assigned to variables):

$exchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
$exchangeService = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService($exchangeVersion)
$exchangeService.Credentials = New-Object -TypeName Microsoft.Exchange.WebServices.Data.WebCredentials($Credential)
$exchangeService.Url = 'https://outlook.office365.com/ews/exchange.asmx'
$folderId= New-Object -TypeName Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Root,$MailboxName)       
$userConfig = [Microsoft.Exchange.WebServices.Data.UserConfiguration]::Bind($exchangeService, "Suite.Storage", $folderId, [Microsoft.Exchange.WebServices.Data.UserConfigurationProperties]::All)

The Dictionary property contains a hash table:

and the app launcher settings are stored in the value for a key name of Suite/AppsCustomizationDataTEST.  Because the settings are stored as JSON, let’s convert them to a custom object:

$apps = ConvertFrom-Json $userConfig.Dictionary.Get_Item("Suite/AppsCustomizationDataTEST")

You can see that the all tiles for the Home tab are in a property called PinnedApps, which are themselves stored as custom objects. Here is the first one:In order to pin a tile, you need an object for the one you want to pin.  The easiest way to do this is to manually pin a tile in your app launcher, then use EWS to get that tile object.  Pinning a tile/app adds it to the end of the Home tab as the last item in the collection so, assuming you don’t move it after that, it will be the 24th item in the collection (index 23).  I assigned that item to a variable, so this is the object that will be added to other users’ pinned apps:

The collection of pinned apps is a fixed array, so to add a new item to it, copy the existing array to a new one plus the object for the custom tile.  Then convert the app settings object back to JSON, update the dictionary hash table with the new object, and save the changed user configuration setting back to the server:

$apps.PinnedApps = $apps.PinnedApps += $myapp
$newapps = ConvertTo-Json $apps
$userConfig.Dictionary.Set_Item("Suite/AppsCustomizationDataTEST",$newapps)
$userConfig.Update()

If the user is already logged in, refresh the browser and open the app launcher to see the newly added tile:

You can modify the above code to loop through any number of mailboxes and add the custom tile object to their app launcher.  You can also manipulate the size and placement of the tile if you want, but my example is to show you how it can be added.  It should be noted that, while all of this does work, it is unsupported, so programmatically customize at your own risk.

Leave a Reply

Your email address will not be published. Required fields are marked *

*