Home OneDrive shortcuts - root clutter vs the Shortcuts folder
Post
Cancel

OneDrive shortcuts - root clutter vs the Shortcuts folder

Open someone’s OneDrive root and count the clutter. A dozen SharePoint library shortcuts sitting next to Apps, Attachments, and whatever else landed there over the years. No grouping. Just a flat mess of remote folders that all look equally important, and every “Add shortcut to OneDrive” click made it worse.

There’s now an optional Shortcuts folder for those links. Same “Add shortcut to OneDrive” feature, without dumping another one into the root landfill if you’d rather keep them together.

Desktop View Pictured: your OneDrive root after a year of “just add one more shortcut”.

Fine for clicking around. Less fine when you need it for fifty users and every Graph example still posts to drive/root/children.

What the SharePoint button actually calls

A HAR from Add shortcut to OneDrive is pretty clear. The create isn’t aimed at the drive root. It posts to a special folder on the user’s OneDrive host:

1
POST https://{tenant}-my.sharepoint.com/_api/v2.1/drives/me/special/shortcuts/children

The body is the usual remoteItem shape (sharepointIds for a library root, or id + parentReference.driveId for a folder). Destination is what matters: special/shortcuts, which creates or reuses a Shortcuts folder. Response parentReference.path looks like /drives/.../root:/Shortcuts.

What Graph docs tell you to do

Go looking for a documented shortcuts special folder and you come up empty. Graph lists things like documents, photos, approot, recordings. No shortcuts.

The documented way to add a shared folder is still root:

1
POST https://graph.microsoft.com/v1.0/users/{upn}/drive/root/children
1
2
3
4
5
6
7
8
{
  "name": "Documents",
  "remoteItem": {
    "id": "{driveItemId}",
    "parentReference": { "driveId": "{driveId}" }
  },
  "@microsoft.graph.conflictBehavior": "rename"
}

(Or the older sharepointIds / listItemUniqueId: "root" variant for a site library.)

OneDrive’s own docs even say a shared folder can only be added to the root of a user’s drive. Root still works. Shortcuts is what you want when you don’t want another shortcut in the root.

The path that matches the UI

Graph will take the same remoteItem body against the special folder the UI uses. Grab sharepointIds from GET /sites/{siteId}/drive?$select=sharePointIds (sharepointIds), then:

1
2
POST https://graph.microsoft.com/v1.0/users/{upn}/drive/special/shortcuts/children
Content-Type: application/json
1
2
3
4
5
6
7
8
9
10
11
12
13
{
  "name": "Documents",
  "remoteItem": {
    "sharepointIds": {
      "listId": "{listId}",
      "listItemUniqueId": "root",
      "siteId": "{siteId}",
      "siteUrl": "https://{tenant}.sharepoint.com/sites/{site}",
      "webId": "{webId}"
    }
  },
  "@microsoft.graph.conflictBehavior": "rename"
}

Same payload as drive/root/children, different parent. Successful creates land under /root:/Shortcuts.

Gotchas

If Add shortcut to OneDrive is disabled for the tenant (DisableAddToOneDrive / Set-SPOTenant -DisableAddShortcutsToOneDrive $true), creates fail with a useless Operation not supported. Root or Shortcuts, same error. Existing shortcuts keep working. Check that setting before you dig into payloads.

Graph returns 409 nestedDescendantShortcutExists if a shortcut to that same library or folder already exists anywhere in the user’s OneDrive. Root, Shortcuts folder, nested somewhere else: doesn’t matter. Delete the old one first, or target something else.

special/shortcuts still isn’t on the official special-folder list. It works on Graph the same way the SPO _api/v2.1 call does. You’re matching the product UI, not a documented Graph feature.

Migrate

Root already full of clutter? Move the shortcuts. List the existing remoteItem links with the right Prefer header, resolve the Shortcuts folder, then PATCH each item’s parentReference. Same driveItem, new parent.

The Prefer: Include-Feature=AddToOneDrive header matters on the list. Without it, shortcuts often don’t show up in /children at all.

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
$UserUpn = 'user@contoso.com'

Connect-MgGraph -Scopes 'Files.ReadWrite.All' -NoWelcome

$headers = @{ Prefer = 'Include-Feature=AddToOneDrive' }
$base    = "https://graph.microsoft.com/v1.0/users/$UserUpn/drive"

# Shortcuts sitting in the OneDrive root
$listParams = @{
    Method  = 'GET'
    Headers = $headers
    Uri     = "$base/root/children?`$select=id,name,remoteItem,parentReference"
}
$rootKids = Invoke-MgGraphRequest @listParams

$toMigrate = $rootKids.value | Where-Object {
    $_.remoteItem -and
    $_.parentReference.path -notmatch '/Shortcuts(/|$)'
}

# Prefer special/shortcuts
$shortcutsFolder = Invoke-MgGraphRequest -Method GET -Uri "$base/special/shortcuts?`$select=id"

foreach ($item in $toMigrate) {
    Write-Host "Migrating: $($item.name)"

    $moveParams = @{
        Method      = 'PATCH'
        Uri         = "$base/items/$($item.id)"
        Body        = (@{ parentReference = @{ id = $shortcutsFolder.id } } | ConvertTo-Json)
        ContentType = 'application/json'
    }
    $moved = Invoke-MgGraphRequest @moveParams

    Write-Host "  -> $($moved.parentReference.path)/$($moved.name)"
}

Run it once per user, or wrap it and loop your UPNs.

Oh yes, did I mention you can patch the shortcut location?

Resolve the Shortcuts folder (GET .../drive/special/shortcuts?$select=id), then:

1
2
PATCH https://graph.microsoft.com/v1.0/users/{upn}/drive/items/{shortcutItemId}
Content-Type: application/json
1
2
3
4
5
{
  "parentReference": {
    "id": "{shortcutsFolderId}"
  }
}

Oh, and CIPP

Don’t want to hand-roll it? CIPP is picking up a Migrate OneDrive root shortcuts to the Shortcuts folder standard (list root shortcuts, PATCH them into Shortcuts per user), plus a Shortcut location choice on Add OneDrive Shortcut.

This post is licensed under CC BY 4.0 by the author.