- Overview
- What's New
- Installation
- Prerequisites
- National cloud support
- Usage
- Single-user shortcut management
- Organization-scale management
- Desired-state plan files
- Reporting and audit output
- Advanced Graph operations
- Troubleshooting
- Licensing
odscex is a PowerShell module that provides CLI access to managing SharePoint shortcuts in OneDrive. It supports both single-user shortcut operations and organization-scale desired-state assignments for groups, CSV targets, filtered users, or all users.
The expanded organization management surface is intended for repeatable administrative runs. You can resolve users, validate access, apply shortcuts idempotently, remove shortcuts, export reports, and define shortcuts in a plan file for scheduled or CI/CD-driven deployments.
Check out CHANGELOG.md to review the details of all releases.
You can get latest release of the odscex module on the PowerShell Gallery
$InstallParameters = @{
Name = 'odscex'
}
Install-Module @InstallParametersTo use this module you need a Microsoft Entra application registration that can authenticate to Microsoft Graph. At a minimum, collect or configure:
- Tenant ID
- Client ID / Application ID
- Client secret or certificate
- Microsoft Graph application permissions for the operations you will run
- Admin consent for those permissions
Broad organization-scale deployments commonly require permissions such as Files.ReadWrite.All, Sites.ReadWrite.All, and User.Read.All. Targeting users by group also requires permission to read group membership. Use the least-privilege model your tenant supports, and validate access before a broad rollout.
odscex can authenticate and send Microsoft Graph requests to multiple Microsoft cloud environments. Use Connect-odscex -Cloud instead of manually setting endpoints.
| Cloud | Microsoft Graph endpoint | Notes |
|---|---|---|
Global |
https://graph.microsoft.com |
Default worldwide Microsoft Graph service. |
GCC |
https://graph.microsoft.com |
Microsoft 365 GCC uses worldwide Microsoft Graph endpoints. |
GCCHigh |
https://graph.microsoft.us |
Microsoft Graph for US Government L4 / GCC High. |
DoD |
https://dod-graph.microsoft.us |
Microsoft Graph for US Government L5 / DoD. |
China |
https://microsoftgraph.chinacloudapi.cn |
Microsoft Graph China operated by 21Vianet. |
Example GCC connection:
$ClientSecretParameters = @{
String = 'client-secret'
AsPlainText = $true
Force = $true
}
$ConnectParameters = @{
Cloud = 'GCC'
TenantId = '00000000-0000-0000-0000-000000000000'
ClientId = '00000000-0000-0000-0000-000000000000'
ClientSecret = ConvertTo-SecureString @ClientSecretParameters
}
Connect-odscex @ConnectParametersExample GCC High connection:
$CertificateParameters = @{
Path = 'Cert:\CurrentUser\My\0000000000000000000000000000000000000000'
}
$Certificate = Get-Item @CertificateParameters
$ConnectParameters = @{
Cloud = 'GCCHigh'
TenantId = '00000000-0000-0000-0000-000000000000'
ClientId = '00000000-0000-0000-0000-000000000000'
ClientCertificate = $Certificate
}
Connect-odscex @ConnectParametersAfter connection, every module command uses a token scoped to the selected cloud's Graph resource and sends requests to that cloud's Graph endpoint until Disconnect-odscex is called or another Connect-odscex call selects a different cloud. Advanced callers can use -GraphEndpoint to override the Graph root endpoint when required.
Connect with a client secret:
$ClientSecretParameters = @{
String = 'client-secret'
AsPlainText = $true
Force = $true
}
$ConnectParameters = @{
TenantId = '00000000-0000-0000-0000-000000000000'
ClientId = '00000000-0000-0000-0000-000000000000'
ClientSecret = ConvertTo-SecureString @ClientSecretParameters
}
Connect-odscex @ConnectParametersConnect with a certificate:
$CertificateParameters = @{
Path = 'Cert:\CurrentUser\My\0000000000000000000000000000000000000000'
}
$Certificate = Get-Item @CertificateParameters
$ConnectParameters = @{
TenantId = '00000000-0000-0000-0000-000000000000'
ClientId = '00000000-0000-0000-0000-000000000000'
ClientCertificate = $Certificate
}
Connect-odscex @ConnectParametersDisconnect when finished:
Disconnect-odscexFor full command-level help, run:
$HelpParameters = @{
Name = '<commandName>'
Full = $true
}
Get-Help @HelpParametersFor additional examples, see USAGE.md.
$DriveParameters = @{
UserPrincipalName = 'user@contoso.com'
}
Get-odscexDrive @DriveParameters$ShortcutParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
UserPrincipalName = 'user@contoso.com'
}
New-odscex @ShortcutParameters$ShortcutParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
FolderPath = 'Department/Policies'
ShortcutName = 'Department Policies'
UserPrincipalName = 'user@contoso.com'
}
New-odscex @ShortcutParameters$ShortcutParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
RelativePath = 'Company/Shortcuts'
ShortcutName = 'Working Documents'
UserPrincipalName = 'user@contoso.com'
}
New-odscex @ShortcutParametersWhen Microsoft Graph returns StatusCode: 404 for a resource such as
users/<user>/drive/root/children or users/<user>/drive/root, the shortcut target
may have resolved successfully but the destination OneDrive could not be opened.
Check that the UserPrincipalName is spelled correctly, the user's OneDrive has been
provisioned at least once, and the account has a SharePoint/OneDrive license. If the
user's sign-in name recently changed, retry with -UserObjectId so Graph does not
depend on the UPN alias. For target-side 404 errors, verify that -Uri is the site URL
(not a library or folder URL), -DocumentLibrary is the library display name, and
-FolderPath is relative to the library root. For example, if the browser URL is
https://contoso.sharepoint.com/Shared%20Documents/Forms/AllItems.aspx, use the site
URL https://contoso.sharepoint.com, the library display name Documents, and a
folder path such as 2025-06-25 or Department/Policies. You can validate access
before creating the shortcut:
Test-odscexPermission -Uri 'https://contoso.sharepoint.com/sites/WorkingSite' -DocumentLibrary 'Documents' -UserPrincipalName 'user@contoso.com'
Get-odscexDrive -UserPrincipalName 'user@contoso.com'Set-odscexShortcutState is the recommended command for repeatable automation. If the shortcut already points to the requested SharePoint target, the command reports Compliant instead of creating a duplicate.
$ShortcutStateParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
UserPrincipalName = 'user@contoso.com'
State = 'Present'
ConflictAction = 'Skip'
}
Set-odscexShortcutState @ShortcutStateParameters$ShortcutStateParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
UserPrincipalName = 'user@contoso.com'
State = 'Present'
ConflictAction = 'Replace'
}
Set-odscexShortcutState @ShortcutStateParameters$RemoveParameters = @{
ShortcutName = 'Working Documents'
UserPrincipalName = 'user@contoso.com'
PassThru = $true
}
Remove-odscex @RemoveParametersUse Get-odscexTargetUser to resolve users, then apply a desired shortcut state with Invoke-odscexShortcutAssignment or Invoke-odscexApply.
$PermissionParameters = @{
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
UserPrincipalName = 'pilot.user@contoso.com'
}
Test-odscexPermission @PermissionParameters$TargetUserParameters = @{
GroupId = '00000000-0000-0000-0000-000000000000'
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
State = 'Present'
ConflictAction = 'Skip'
ReportPath = '.\odscex-group-results.csv'
}
Invoke-odscexShortcutAssignment @AssignmentParametersThe CSV should contain UserPrincipalName, UserObjectId, or Id columns.
$TargetUserParameters = @{
CsvPath = '.\users.csv'
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
ReportPath = '.\odscex-csv-results.json'
OutputFormat = 'Json'
}
Invoke-odscexShortcutAssignment @AssignmentParameters$TargetUserParameters = @{
Filter = 'accountEnabled eq true'
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
State = 'Present'
}
Invoke-odscexShortcutAssignment @AssignmentParameters$TargetUserParameters = @{
AllUsers = $true
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/Company'
DocumentLibrary = 'Documents'
ShortcutName = 'Company Documents'
State = 'Present'
ReportPath = '.\odscex-all-users.csv'
}
Invoke-odscexShortcutAssignment @AssignmentParameters$TargetUserParameters = @{
GroupId = '00000000-0000-0000-0000-000000000000'
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
State = 'Absent'
ReportPath = '.\odscex-remove-results.csv'
}
Invoke-odscexShortcutAssignment @AssignmentParameters$TargetUserParameters = @{
CsvPath = '.\pilot-users.csv'
}
$Users = Get-odscexTargetUser @TargetUserParameters
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
State = 'Present'
WhatIf = $true
}
Invoke-odscexShortcutAssignment @AssignmentParametersIf a large run stops after processing some users, use -ResumeFrom to continue at a zero-based user index.
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
State = 'Present'
ResumeFrom = 250
ReportPath = '.\odscex-resumed-results.csv'
}
Invoke-odscexShortcutAssignment @AssignmentParametersPlan files let you describe expected shortcuts once and reapply them on a schedule. Invoke-odscexPlan and Invoke-odscexApply support JSON and PowerShell data files.
{
"shortcuts": [
{
"name": "HR Policies",
"siteUrl": "https://contoso.sharepoint.com/sites/HR",
"library": "Documents",
"folderPath": "Policies",
"oneDrivePath": "Company",
"state": "Present",
"target": {
"groupId": "00000000-0000-0000-0000-000000000000"
}
},
{
"name": "Company Handbook",
"siteUrl": "https://contoso.sharepoint.com/sites/Company",
"library": "Documents",
"state": "Present",
"target": {
"allUsers": true
}
}
]
}Review the plan:
$PlanParameters = @{
Path = '.\shortcuts.json'
}
Invoke-odscexPlan @PlanParametersApply the plan and export a report:
$ApplyParameters = @{
Path = '.\shortcuts.json'
ReportPath = '.\shortcut-apply.csv'
}
Invoke-odscexApply @ApplyParametersPreview the plan without applying changes:
$ApplyParameters = @{
Path = '.\shortcuts.json'
WhatIf = $true
}
Invoke-odscexApply @ApplyParametersOrganization-scale commands return structured result objects and can also write reports. The report format can be Csv, Json, or Clixml.
$AssignmentParameters = @{
User = $Users
Uri = 'https://contoso.sharepoint.com/sites/WorkingSite'
DocumentLibrary = 'Documents'
ShortcutName = 'Working Documents'
ReportPath = '.\shortcut-audit.clixml'
OutputFormat = 'Clixml'
}
Invoke-odscexShortcutAssignment @AssignmentParametersCommon statuses include:
Created- shortcut was created.Compliant- shortcut already pointed to the requested target.AlreadyAbsent- shortcut removal was requested and no matching shortcut existed.Removed- shortcut was removed.SkippedConflict- a same-named item existed and-ConflictAction Skipwas used.Failed- the user assignment failed and the error was captured in the result object.
Invoke-odscexApiRequest follows @odata.nextLink when callers request all pages and retries transient Microsoft Graph responses such as 429, 500, 502, 503, and 504. Advanced callers can also submit JSON batches of up to 20 Microsoft Graph subrequests.
$BatchParameters = @{
Requests = @(
@{ id = 'drive'; method = 'GET'; url = '/users/user@contoso.com/drive' }
@{ id = 'site'; method = 'GET'; url = '/sites/contoso.sharepoint.com:/sites/WorkingSite' }
)
}
Invoke-odscexGraphBatch @BatchParametersFor admin-focused guidance on common setup and runtime issues, see Troubleshooting. It covers authentication failures, permission errors, missing OneDrive provisioning, shortcut target resolution, relative path behavior, throttling, reporting, and national cloud endpoint checks.
odscex is licensed under the MIT license.