Skip to content

Commit 3cabc5e

Browse files
feat: Allow custom paths for OneDrive shortcuts (KelvinTegelaar#2010)
Enhance the `New-CIPPOneDriveShortCut` function to support custom URLs that point to specific document libraries and subfolders in SharePoint. This update allows users to create shortcuts not just for top-level sites but also for specific libraries and folders. Fixes KelvinTegelaar/CIPP#5892
2 parents 0b2f2bd + 2e2e721 commit 3cabc5e

1 file changed

Lines changed: 85 additions & 19 deletions

File tree

‎Modules/CIPPCore/Public/New-CIPPOneDriveShortCut.ps1‎

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,96 @@ function New-CIPPOneDriveShortCut {
1111
)
1212
Write-Host "Received $Username and $UserId. We're using $URL and $TenantFilter"
1313
try {
14-
$SiteInfo = New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/sites/' -tenantid $TenantFilter -asapp $true | Where-Object -Property weburl -EQ $URL
15-
$ListItemUniqueId = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/sites/$($SiteInfo.id)/drive?`$select=SharepointIds" -tenantid $TenantFilter -asapp $true).SharePointIds
16-
$body = [PSCustomObject]@{
17-
name = 'Documents'
18-
remoteItem = @{
19-
sharepointIds = @{
20-
listId = $($ListItemUniqueId.listid)
21-
listItemUniqueId = 'root'
22-
siteId = $($ListItemUniqueId.siteId)
23-
siteUrl = $($ListItemUniqueId.siteUrl)
24-
webId = $($ListItemUniqueId.webId)
14+
# Unwrap SharePoint browser URLs — e.g. AllItems.aspx?id=... or onedrive.aspx?id=...
15+
# The `id` query parameter holds the server-relative path to the folder, URL-encoded.
16+
if ($URL -match '[?&]id=([^&]+)') {
17+
$ServerRelativePath = [Uri]::UnescapeDataString($matches[1])
18+
$ParsedUri = [System.Uri]$URL
19+
$URL = "$($ParsedUri.Scheme)://$($ParsedUri.Host)$ServerRelativePath"
20+
Write-Host "Resolved browser URL to: $URL"
21+
}
22+
23+
# Find site by prefix match (longest match wins — handles subsites correctly)
24+
$SiteInfo = (New-GraphGetRequest -uri 'https://graph.microsoft.com/beta/sites/' -tenantid $TenantFilter -asapp $true) |
25+
Where-Object { $URL -like "$($_.weburl.TrimEnd('/'))/*" -or $URL -eq $_.weburl.TrimEnd('/') } |
26+
Sort-Object { $_.weburl.Length } -Descending |
27+
Select-Object -First 1
28+
29+
if (-not $SiteInfo) {
30+
throw "Could not find a SharePoint site matching URL: $URL"
31+
}
32+
33+
# Extract whatever comes after the site URL (library name + optional folder path)
34+
$RelativePath = $URL.Substring($SiteInfo.weburl.TrimEnd('/').Length).TrimStart('/')
35+
36+
if ([string]::IsNullOrWhiteSpace($RelativePath)) {
37+
# ── Root shortcut (original behaviour) ──────────────────────────────
38+
$SPIds = (New-GraphGetRequest -uri "https://graph.microsoft.com/beta/sites/$($SiteInfo.id)/drive?`$select=SharepointIds" -tenantid $TenantFilter -asapp $true).SharePointIds
39+
$body = [PSCustomObject]@{
40+
name = 'Documents'
41+
remoteItem = @{
42+
sharepointIds = @{
43+
listId = $SPIds.listid
44+
listItemUniqueId = 'root'
45+
siteId = $SPIds.siteId
46+
siteUrl = $SPIds.siteUrl
47+
webId = $SPIds.webId
48+
}
2549
}
50+
'@microsoft.graph.conflictBehavior' = 'rename'
51+
} | ConvertTo-Json -Depth 10
52+
$ShortcutDisplayName = $SiteInfo.displayName
53+
} else {
54+
# ── Subfolder shortcut ───────────────────────────────────────────────
55+
# Split "SharedDocuments/Folder123" into library name and optional subfolder
56+
$PathParts = $RelativePath -split '/'
57+
$LibraryName = [Uri]::UnescapeDataString($PathParts[0])
58+
$FolderPath = if ($PathParts.Count -gt 1) {
59+
($PathParts[1..($PathParts.Count - 1)] | ForEach-Object { [Uri]::UnescapeDataString($_) }) -join '/'
60+
} else { $null }
61+
62+
# Find the drive (document library) whose name matches the first path segment
63+
$Drives = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/sites/$($SiteInfo.id)/drives?`$select=id,name,webUrl" -tenantid $TenantFilter -asapp $true
64+
$Drive = $Drives | Where-Object {
65+
$_.name -eq $LibraryName -or
66+
[Uri]::UnescapeDataString($_.webUrl.TrimEnd('/').Split('/')[-1]) -eq $LibraryName
67+
} | Select-Object -First 1
68+
69+
# Fall back to the default drive when no name match is found
70+
if (-not $Drive) {
71+
$Drive = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/sites/$($SiteInfo.id)/drive?`$select=id,name" -tenantid $TenantFilter -asapp $true
2672
}
27-
'@microsoft.graph.conflictBehavior' = 'rename'
28-
} | ConvertTo-Json -Depth 10
29-
New-GraphPOSTRequest -method POST "https://graph.microsoft.com/beta/users/$Username/drive/root/children" -body $Body -tenantid $TenantFilter -asapp $true
30-
Write-LogMessage -API $APIName -headers $Headers -message "Created OneDrive shortcut called $($SiteInfo.displayName) for $($Username)" -Sev 'info'
31-
return "Successfully created OneDrive Shortcut for $Username called $($SiteInfo.displayName) "
73+
74+
# Resolve the target driveItem — subfolder or library root
75+
if ($FolderPath) {
76+
$EncodedFolderPath = ($FolderPath -split '/' | ForEach-Object { [Uri]::EscapeDataString($_) }) -join '/'
77+
$FolderItem = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/drives/$($Drive.id)/root:/$($EncodedFolderPath)?`$select=id,name,parentReference" -tenantid $TenantFilter -asapp $true
78+
$DisplayName = $FolderItem.name
79+
} else {
80+
$FolderItem = New-GraphGetRequest -uri "https://graph.microsoft.com/beta/drives/$($Drive.id)/root?`$select=id,name" -tenantid $TenantFilter -asapp $true
81+
# Graph returns name='root' for a drive's root item — use the drive (library) name instead
82+
$DisplayName = $Drive.name
83+
}
84+
85+
# POST body for subfolder uses driveItem.id + drive.id (not sharepointIds)
86+
$body = [PSCustomObject]@{
87+
name = $DisplayName
88+
remoteItem = @{
89+
id = $FolderItem.id
90+
parentReference = @{ driveId = $Drive.id }
91+
}
92+
'@microsoft.graph.conflictBehavior' = 'rename'
93+
} | ConvertTo-Json -Depth 10
94+
$ShortcutDisplayName = "$($SiteInfo.displayName) / $DisplayName"
95+
}
96+
97+
$null = New-GraphPOSTRequest -method POST "https://graph.microsoft.com/beta/users/$Username/drive/root/children" -body $Body -tenantid $TenantFilter -asapp $true
98+
Write-LogMessage -API $APIName -headers $Headers -message "Created OneDrive shortcut called $ShortcutDisplayName for $Username" -Sev 'info'
99+
return "Successfully created OneDrive Shortcut for $Username called $ShortcutDisplayName"
32100
} catch {
33101
$ErrorMessage = Get-CippException -Exception $_
34-
$Result = "Could not add Onedrive shortcut to $Username : $($ErrorMessage.NormalizedError)"
102+
$Result = "Could not add OneDrive shortcut to $Username : $($ErrorMessage.NormalizedError)"
35103
Write-LogMessage -headers $Headers -API $APIName -message $Result -Sev 'Error' -LogData $ErrorMessage
36104
throw $Result
37105
}
38106
}
39-
40-

0 commit comments

Comments
 (0)