@@ -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