Batch create mailbox folders with GRAPH API

I had a need to create specific folders to a list of mailboxes in Microsoft 365. I briefly looked into Powershell Exchange Online module. It has a “New-MailboxFolder” cmdlet, unfortunately that one is limited to be used “in your own mailbox” only.

I have come across with the GRAPH API multiple times this year, so that was the next thing to check. Luckily, it had what I needed. I ended up using “Create MailFolder” API. This was my first time using GRAPH API by myself. Alex’s blog post “Using the Microsoft Graph API with PowerShell” in the Adam the Automator blog was a great help in getting the access token and using it for my purpose.

## Details for mailboxes and folders needed. Obviously you can get these as parameters if need be. For AdHoc code I use "Get-Clipboard" quite often. Here a fixed array as something to start with.

$Mailboxes = @("mailbox1@your.domain","mailbox2@your.domain")
$Folders = @("FolderName1","FolderName2")

## Before next step aquire access token, if you need help this blog has a great example For simplicity, I have used here an application secret.

# Define AppId, secret and scope, your tenant name and endpoint URL
$AppId = ## Your AppId ##
$AppSecret = ## Your AppSecret ##
$Scope = ""
$TenantName = ## Your TenantName ##

$Url = "$TenantName/oauth2/v2.0/token"

# Add System.Web for urlencode
Add-Type -AssemblyName System.Web

# Create body
$Body = @{
    client_id = $AppId
	client_secret = $AppSecret
	scope = $Scope
	grant_type = 'client_credentials'

# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'
    # Create string by joining bodylist with '&'
    Body = $Body
    Uri = $Url

# Request the token!
$Request = Invoke-RestMethod @PostSplat

# Create header
$Header = @{
    Authorization = "$($Request.token_type) $($Request.access_token)"

## Thanks, now we have token ready for use. Lets use GRAPH's mailFolders API

## Loop through mailbxoes

foreach($mailbox in $Mailboxes) {

    $Uri = "$mailbox/mailFolders"

    ## Fetch all mailbox folder names, until no more pages are provided
    $Mailboxfolders = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"
    $MailboxfoldersList = $Mailboxfolders.Value.Displayname
    $NextPage = $Mailboxfolders.'@Odata.NextLink'
    While($NextPage -ne $Null) {
        $Mailboxfolders = Invoke-RestMethod -Uri $NextPage -Headers $Header -Method Get -ContentType "application/json"
        $MailboxfoldersList += $Mailboxfolders.Value.Displayname
        $NextPage = $Mailboxfolders.'@Odata.NextLink'

    ## Now a loop for all the folders

    foreach($Folder in $Folders) {

$Body = @"
"displayName": "$Folder"

        ## Output current operation

        Write-Host "Mailbox: $mailbox`nMailboxfolders: $($MailboxfoldersList)`nFolder wanted: $folder"

        if($($MailboxfoldersList) -contains $folder) {
            write-host "$folder folder already found at mailbox $mailbox.`n"

        else {
            $Newfolder = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Post -Body $Body -ContentType "application/json"
            write-host "Created new folder: $($Newfolder.displayName) to mailbox $mailbox!`n"

Using GRAPH API seems very handy for multiple cases regarding email. One popular use case is replacing legacyauth based integrations. This is even a more obvious way to go now since Microsoft has announced that OAuth2 for IMAP would not be supported without a user account. Remember to scope permissions with an ApplicationAccessPolicy when using GRAPH API for email access.