Private/_KritOneDriveResolver.ps1
|
function Get-KritGraphProp { <# .SYNOPSIS Internal helper. Safely fetches a property/key from a Graph response object that may be a Hashtable, IDictionary or PSCustomObject, returning $null when the key is absent — even under Set-StrictMode -Version Latest. .NOTES Microsoft.Graph.Authentication / Invoke-MgGraphRequest returns Hashtables. StrictMode forbids direct $obj.SomeMissingKey access, so this helper is the canonical safe accessor across every OneDrive cmdlet in this module. #> [CmdletBinding()] param( [Parameter(Mandatory)][AllowNull()]$Object, [Parameter(Mandatory)][string]$Name ) if ($null -eq $Object) { return $null } if ($Object -is [System.Collections.IDictionary]) { if ($Object.Contains($Name)) { return $Object[$Name] } return $null } $p = $Object.PSObject.Properties[$Name] if ($p) { return $p.Value } return $null } function Resolve-KritOneDriveDriveItem { <# .SYNOPSIS Internal helper. Resolves a local OneDrive-for-Business sync path to its cloud Microsoft Graph DriveItem and ensures a delegated Graph session. .DESCRIPTION Used by the public OneDrive sharing-link cmdlets (Get/Add/Set/Remove + New-KritOneDriveShareLink). Encapsulates: - Microsoft.Graph.Authentication import + Connect-MgGraph (delegated) - HKCU OneDrive Business1 sync-root lookup - Path mapping → /me/drive/root:/<encoded-relative-path> - Returning the DriveItem object (id / parentReference.driveId / etc) NOT exported. Kept private so the public surface stays narrow and the path/auth logic stays in one place. .NOTES CONTRACT inputs: - LocalPath : path that must exist under HKCU OneDrive sync root - UseDeviceCode : force device-code flow (headless) - Scopes : Graph scopes to request (caller-specified) outputs: - PSCustomObject : @{ Item, RelativePath, OdRoot, ResolvedLocal } sideEffects: - Connect-MgGraph (delegated, cached) when scopes missing/changed - No DriveItem mutation invariants: - Throws when path missing OR outside sync root OR DriveItem 404 #> [CmdletBinding()] [OutputType([pscustomobject])] param( [Parameter(Mandatory)][string]$LocalPath, [Parameter(Mandatory)][string[]]$Scopes, [switch]$UseDeviceCode ) if (-not (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication)) { Write-Verbose "Installing Microsoft.Graph.Authentication ..." Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop } Import-Module Microsoft.Graph.Authentication -ErrorAction Stop $resolvedLocal = (Resolve-Path -LiteralPath $LocalPath -ErrorAction Stop).Path $odBusinessRoot = $null try { $odBusinessRoot = (Get-ItemProperty -Path 'HKCU:\SOFTWARE\Microsoft\OneDrive\Accounts\Business1' -Name UserFolder -ErrorAction Stop).UserFolder } catch { throw "OneDrive for Business sync root not found in HKCU:\SOFTWARE\Microsoft\OneDrive\Accounts\Business1\UserFolder — verify OneDrive sync client signed in." } $resolvedLocal = $resolvedLocal -replace '\\','/' -replace '/$','' $odBusinessRoot = $odBusinessRoot -replace '\\','/' -replace '/$','' if (-not $resolvedLocal.StartsWith($odBusinessRoot, [StringComparison]::OrdinalIgnoreCase)) { throw "Path '$resolvedLocal' is not under the OneDrive sync root '$odBusinessRoot' — only OneDrive-synced items can be managed." } $relativePath = $resolvedLocal.Substring($odBusinessRoot.Length).TrimStart('/') Write-Verbose "Local path: $resolvedLocal" Write-Verbose "OneDrive root: $odBusinessRoot" Write-Verbose "Cloud-relative: $relativePath" $ctx = Get-MgContext $needConnect = $true if ($ctx -and $ctx.Account) { $missingScope = $Scopes | Where-Object { $ctx.Scopes -notcontains $_ } if (-not $missingScope) { Write-Verbose "Already connected as $($ctx.Account)" $needConnect = $false } } if ($needConnect) { Write-Verbose "Connecting to Microsoft Graph (delegated, scopes: $($Scopes -join ', '))" if ($UseDeviceCode) { Connect-MgGraph -Scopes $Scopes -UseDeviceCode -NoWelcome -ErrorAction Stop } else { try { Connect-MgGraph -Scopes $Scopes -NoWelcome -ErrorAction Stop } catch { if ($_.Exception.Message -match 'window handle') { Write-Verbose "Browser flow needs a window handle; falling back to device code." Connect-MgGraph -Scopes $Scopes -UseDeviceCode -NoWelcome -ErrorAction Stop } else { throw } } } } $encodedPath = ($relativePath -split '/' | ForEach-Object { [uri]::EscapeDataString($_) }) -join '/' $itemUri = "/v1.0/me/drive/root:/$encodedPath" Write-Verbose "Looking up: $itemUri" $item = Invoke-MgGraphRequest -Method GET -Uri $itemUri -ErrorAction Stop [pscustomobject]@{ Item = $item ItemId = $item.id DriveId = $item.parentReference.driveId ItemName = $item.name IsFolder = [bool]$item.folder RelativePath = $relativePath OdRoot = $odBusinessRoot ResolvedLocal = $resolvedLocal } } |