Introduction : la jungle des identifiants Exchange
Si vous administrez Exchange Online au quotidien, vous avez forcement rencontre ce probleme frustrant : chaque composant de l'ecosysteme Microsoft utilise son propre format d'identifiant pour designer une meme boite aux lettres ou un meme dossier. PowerShell, MAPI, EWS, Microsoft Graph et OWA manipulent tous des IDs differents, souvent incompatibles entre eux et rarement documentes correctement.
Avec la depreciation programmee d'EWS (Exchange Web Services), cette problematique devient critique. De nombreux scripts d'administration et outils tiers reposaient sur EWS pour convertir ou consommer ces identifiants. Aujourd'hui, il faut basculer vers Microsoft Graph et sa methode translateExchangeIds, mais cette derniere ne couvre pas tous les scenarios, notamment la conversion directe des storeId obtenus via PowerShell.
Cet article se concentre sur les identifiants de dossiers (folder identifiers) dans une perspective d'administration IT Pro, ou PowerShell reste l'outil central. Nous allons detailler la chaine complete de conversion et fournir des scripts prets a l'emploi.
Bon a savoir
storeId vers entryId et entryId vers restId restent identiques.Comprendre la chaine de conversion des identifiants
Avant de plonger dans le code, il est essentiel de visualiser le parcours d'un identifiant a travers les differentes couches de l'ecosysteme Exchange. Voici la sequence typique :
| Identifiant | Source / Consommateur | Usage principal |
|---|---|---|
| storeId (FolderId) | Get-ExOMailboxFolderStatistics | Point de depart en PowerShell |
| eDiscoveryId | Content Search / targeted collections | Recherche ciblee KeyQL |
| entryId | Clients MAPI | Etape intermediaire vers Graph |
| RestId | APIs Graph Outlook | Operations via Graph SDK |
| OWAId | Outlook Web Access | Navigation directe dans OWA |
Le flux logique est donc le suivant : on part du storeId fourni par PowerShell, on le convertit en entryId (format MAPI), puis la methode Graph translateExchangeIds prend le relais pour produire le RestId, qui peut lui-meme etre transforme en OWAId.
Attention
immutableEntryId et restImmutableEntryId ne sont pas supportes pour les dossiers par la methode translateExchangeIds. Ne comptez pas dessus dans vos scripts de gestion de dossiers.Recuperer le storeId d'un dossier avec PowerShell
Le point de depart de toute la chaine est la propriete FolderId retournee par la cmdlet Get-ExOMailboxFolderStatistics. Prenons l'exemple d'un dossier nomme "Xbox" dans une boite aux lettres.
1Get-ExOMailboxFolderStatistics vasil | ? {$_.Name -eq "Xbox"} | select Name,FolderId2 3Name FolderId4---- --------5Xbox LgAAAAChKSJAhlnUTIHtKSso30ThAQBIPfDMxyP/RYhY8M8xmAPVAAS8XYoAAAABCette valeur encodee en Base64 est notre storeId. C'est la matiere premiere de toutes les conversions qui suivent. Vous pouvez obtenir le meme resultat via Get-MailboxFolderStatistics ou Get-MailboxFolder selon votre contexte.
Convertir le storeId pour l'eDiscovery (targeted collections)
Un cas d'usage tres concret du storeId concerne les recherches eDiscovery et Content Search via la fonctionnalite des targeted collections. Cette approche permet de restreindre une recherche a un dossier precis grace au mot-cle folderId du langage de requete KeyQL.
Malheureusement, Microsoft a retire une partie de la documentation officielle a ce sujet. Pour scoper une recherche sur un dossier unique, vous devez d'abord transformer le storeId au format attendu par eDiscovery. Voici le script de conversion :
1$folderId = [Convert]::FromBase64String($folderId)2 3$encoding = [System.Text.Encoding]::GetEncoding("us-ascii")4$nibbler = $encoding.GetBytes("0123456789ABCDEF")5 6$indexIdBytes = New-Object byte[] 48; $indexIdIdx = 07$folderId | select -skip 23 -first 24 | % { $indexIdBytes[$indexIdIdx++] = $nibbler[$_ -shr 4]; $indexIdBytes[$indexIdIdx++] = $nibbler[$_ -band 0x0F] }8 9return $encoding.GetString($indexIdBytes)Detail de la transformation
Ce script effectue plusieurs operations bas niveau :
- Decodage Base64 du
storeIdvers un tableau d'octets. - Extraction de 24 octets a partir de la position 23 (le coeur de l'identifiant).
- Conversion hexadecimale de chaque octet en deux caracteres ASCII via l'operation
-shr 4(nibble haut) et-band 0x0F(nibble bas).
Dans notre exemple, la transformation donne :
1#storeId2LgAAAAChKSJAhlnUTIHtKSso30ThAQBIPfDMxyP/RYhY8M8xmAPVAAS8XYoAAAAB3 4#eDiscovery folderId5483DF0CCC723FF458858F0CF319803D50004BC5D8A000000Cette valeur folderId peut ensuite etre injectee dans une requete KeyQL pour scoper la recherche eDiscovery sur le dossier Xbox uniquement. Le nombre d'elements retournes correspond exactement au contenu du dossier tel qu'affiche dans Outlook.

Astuce
folderId de KeyQL supporte egalement les exclusions, ce qui permet de couvrir un ensemble de dossiers tout en excluant certains sous-dossiers. Pratique pour des collectes ciblees complexes en investigation.Convertir le storeId en entryId (format MAPI)
Pour exploiter les cmdlets du Graph SDK for PowerShell ou appeler directement les methodes de l'API Graph, vous avez besoin du RestId. Or, la methode translateExchangeIds ne sait pas convertir directement un storeId PowerShell. Il faut donc passer par une etape intermediaire : la generation d'un entryId.
Si les dossiers par defaut (Inbox, RecoverableItemsDeletions, etc.) peuvent etre adresses via leurs well-known names, les dossiers crees par l'utilisateur exigent imperativement un RestId.
La fonction ci-dessous transforme un storeId en entryId :
1function FolderIdToEntryId {2 3 param([Parameter(Mandatory=$true)]$folderId)4 5 # convert from base64 to bytes6 $folderIdBytes = [Convert]::FromBase64String($folderId)7 8 # convert byte array to string, remove '-' and ignore first byte9 $folderIdHexString = [System.BitConverter]::ToString($folderIdBytes).Replace('-','')10 $folderIdHexStringLength = $folderIdHexString.Length11 12 # get hex entry id string by removing first and last byte13 $entryIdHexString = $folderIdHexString.SubString(2,($folderIdHexStringLength-4))14 15 # convert to byte array - two chars represents one byte16 $entryIdBytes = [byte[]]::new($entryIdHexString.Length / 2)17 18 For($i=0; $i -lt $entryIdHexString.Length; $i+=2){19 $entryIdTwoChars = $entryIdHexString.Substring($i, 2)20 $entryIdBytes[$i/2] = [convert]::ToByte($entryIdTwoChars, 16)21 }22 23 # convert bytes to base64 string24 $entryIdBase64 = [Convert]::ToBase64String($entryIdBytes)25 26 # count how many '=' contains base64 entry id27 $equalCharCount = $entryIdBase64.Length - $entryIdBase64.Replace('=','').Length28 29 # trim '=', replace '/' with '-', replace '+' with '_' and add number of '=' at the end30 $entryId = $entryIdBase64.TrimEnd('=').Replace('/','_').Replace('+','-')+$equalCharCount31 32 return $entryId33}Points cles de la fonction
- On retire le premier et le dernier octet de la representation hexadecimale du
storeId, car ils contiennent des metadonnees non pertinentes pour l'entryId. - Le resultat est reencode en Base64 URL-safe : les
/deviennent-, les+deviennent_, et le nombre de caracteres de padding=est ajoute a la fin.
Cette normalisation URL-safe est essentielle car l'API Graph n'accepte pas les caracteres Base64 standards dans les parametres d'URL.
Obtenir le RestId via translateExchangeIds
Une fois l'entryId genere, la methode Graph translateExchangeIds peut produire le RestId (ainsi que d'autres formats supportes). C'est cet identifiant qui alimente les APIs Outlook de Graph, par exemple pour lister ou manipuler le contenu d'un dossier.
Une derniere transformation est ensuite necessaire pour deriver l'OWAId a partir du RestId, si vous souhaitez ouvrir directement le dossier dans Outlook Web Access.
Bon a savoir
translateExchangeIds accepte des lots (batches) pouvant contenir jusqu'a 1000 identifiants par appel. Exploitez cette capacite pour minimiser le nombre de requetes Graph et eviter le throttling sur des boites volumineuses.Script complet : rapport des IDs de dossiers d'une boite aux lettres
En combinant l'ensemble de ces conversions, il est possible de generer un rapport exhaustif de tous les dossiers d'une boite aux lettres accompagnes de leurs differents identifiants. Le script necessite les modules Exchange Online et Graph installes, et fonctionne en permissions deleguees.
Prerequis et permissions
- Module PowerShell Exchange Online Management.
- Module Microsoft Graph PowerShell.
- Permission Graph
User.ReadBasic.Allpour resoudre les boites aux lettres. - Permission
Mail.ReadBasic.Sharedpour les scenarios delegues avec le bouton "Open in Graph". - Cote Exchange, seule la cmdlet
Get-ExOMailboxFolderStatisticsest requise.
Execution du script
Cibler une boite via son adresse SMTP
Le parametre obligatoire -Mailbox accepte l'adresse SMTP principale (a condition qu'elle corresponde a l'UPN, contrainte imposee par le design de Graph).
1.\Mailbox_Folder_IDs.ps1 -Mailbox user@domain.comCibler une boite via son GUID
Vous pouvez egalement fournir l'ExternalDirectoryObjectId (GUID) de l'utilisateur.
1.\Mailbox_Folder_IDs.ps1 -Mailbox 4ebd5057-4d61-4ca0-beb7-df3f1ebd1aa7Inclure les dossiers non-IPM
Le switch -IncludeNonIPM traite egalement les dossiers de l'arborescence non-IPM (dossiers systeme non accessibles a l'utilisateur).
1.\Mailbox_Folder_IDs.ps1 -Mailbox user@domain.com -IncludeNonIPMImportant
-IncludeNonIPM que si vous en avez reellement besoin, sous peine de generer des rapports gigantesques et de multiplier les appels Graph.Fonctionnement interne
Le script s'appuie sur une fonction de connectivite basique (lignes 11-42) qui verifie la presence de la permission User.ReadBasic.All. La verification d'erreurs etant minimale, n'hesitez pas a remplacer cette portion par votre propre methode de connexion aux services.
Une fois connecte, le workflow est le suivant :
- Recuperation de tous les dossiers de la boite ciblee (avec ou sans arborescence non-IPM).
- Enrichissement local avec les valeurs
eDiscoveryIdetEntryId, calculees sans appel supplementaire. - Appel a
translateExchangeIdspar lots de 1000entryIdpour obtenir lesRestId. - Stockage des
RestIddans une hashtable pour enrichir la sortie finale.

Colonnes du rapport CSV
Le fichier CSV genere dans le repertoire de travail contient les colonnes suivantes :
- Name : le nom du dossier.
- FolderType : le type de dossier.
- Identity : le chemin du dossier (la boite est designee par l'identifiant fourni en entree, donc attendez-vous a voir des GUID si vous en avez fourni un).
- FolderId : la valeur
storeId. - eDiscoveryId : la valeur exploitable pour les targeted collections eDiscovery.
- EntryId : l'identifiant MAPI.
- RestId : l'identifiant utilise par l'API Graph.
- OWAId : l'identifiant utilise par OWA.
Rapport HTML interactif et boutons d'action
En complement du CSV, le script produit un fichier HTML presentant les memes donnees dans un tableau triable. Deux boutons d'action y sont integres :
- Open in Graph : ouvre le Graph Explorer avec une requete pre-remplie pour recuperer le dossier concerne.
- Open in OWA : navigue directement vers le dossier dans Outlook Web Access.

Attention
ExternalDirectoryObjectId, ce bouton echouera.Support des boites partagees
Bonus interessant : le lien Open in OWA fonctionne egalement pour toute boite aux lettres sur laquelle vous disposez de la permission Full Access, a condition de selectionner un dossier accessible. Pour le bouton Open in Graph, les scenarios delegues necessitent la permission Mail.ReadBasic.Shared.
Conclusion : anticiper la fin d'EWS
La depreciation d'EWS rend indispensable la maitrise de ces conversions d'identifiants pour tout administrateur ou ingenieur cloud manipulant Exchange Online. En comprenant la chaine storeId vers entryId vers RestId vers OWAId, vous conservez la capacite de scripter des operations avancees via PowerShell et Microsoft Graph sans dependre d'une API en fin de vie.
Astuce
La version actuelle du script ne supporte que les permissions deleguees, ce qui reste suffisant pour couvrir toutes les boites accessibles a l'utilisateur authentifie. L'ajout du support des permissions applicatives constitue une evolution naturelle pour les scenarios de gestion a grande echelle.



