Module.psm1
#requires -module Microsoft.Graph.Sites,Microsoft.Graph.Users,Microsoft.Graph.Groups,Microsoft.Graph.Files using namespace Microsoft.Graph.PowerShell.Models using namespace Microsoft.Graph.PowerShell.Runtime using namespace System.Management.Automation using namespace System.Collections.Generic Update-FormatData -PrependPath $PSScriptRoot\Formats\*.Format.ps1xml filter Get-JMgDrive { <# .SYNOPSIS A Universal method of getting the drive of a resource (site, onedrive, user, email, url, etc.) by piping it to this command .EXAMPLE Get-JMGDrive -Root .EXAMPLE 'user@principalname.com' | Get-JMGDrive .EXAMPLE 'https://mysite.sharepoint.com' | Get-JMGDrive .EXAMPLE 'https://mysite.sharepoint.com/sites/OurSite' | Get-JMGDrive .EXAMPLE Get-MgSite -Property "siteCollection,webUrl" -Filter "siteCollection/root ne null" | Get-JMGDrive .EXAMPLE Get-MgSite 'SiteSearchKeyword' #> [OutputType('MicrosoftGraphDrive1[]')] [CmdletBinding(DefaultParameterSetName = 'InputObject')] param( #Retrieve a drive by user UPN, sharepoint site URI, or a user/onedrive/site/team object [Parameter(Position = 0, ValueFromPipeline, ParameterSetName = 'InputObject')]$InputObject, #Specify this to get the root Sharepoint Site [Parameter(Mandatory, ParameterSetName = 'Root')][Switch]$Root ) if ($null -eq $InputObject) { #Fetch the "my" drive by default Write-Verbose 'No input specified, fetching the /me drive.' return [MicrosoftGraphDrive1[]](Invoke-MgGraphRequest -Method Get 'v1.0/me/drives').value } if ($root) { return Get-MgDrive } switch ($InputObject.GetType().Name) { 'Uri' { Get-MgSiteByUri $Uri | Get-JMgDrive } 'MailAddress' { Get-MgUser -UserId $InputObject | Where-Object { $PSItem } | Get-JMgDrive } 'MicrosoftGraphSite1' { Get-MgSiteDrive -SiteId $InputObject.Id } 'MicrosoftGraphUser1' { try { Get-MgUserDrive -UserId $InputObject.Id -ErrorAction stop } catch [RestException] { if ($PSItem -match 'Access Denied') { $PSItem.ErrorDetails = "You don't have access to this user's OneDrive. This is the default setting in O365 for privacy. To access these files as someone other than the user, you must create an access link: https://docs.microsoft.com/en-us/microsoft-365/admin/add-users/remove-former-employee-step-5?view=o365-worldwide" } Write-Error -ErrorRecord $PSItem; return } } 'MicrosoftGraphGroup1' { Get-MgGroupDrive -GroupId $InputObject.Id } 'MicrosoftGraphTeam1' { Get-MgGroupDrive -GroupId $InputObject.Id } default { try { $uri = [Uri]::new($InputObject) return $Uri | Get-JMgDrive } catch [MethodInvocationException] {} $upn = $InputObject -as [mailaddress] if ($upn) { return $upn | Get-JMgDrive } # Last resort is a keyword search of Sharepoint Sites $siteIds = (Get-MgSite -Search ([String]$InputObject)).id if (-not $siteIds) { throw "Site Search returned no results for $siteIds" } $siteIds | Get-JMgDrive } } } filter Get-JMgSiteByUri { param( [Uri]$Uri ) $siteId = $Uri.Host, $Uri.AbsolutePath -join ':' Get-MgSite -SiteId $siteId } filter Get-JMgDriveItem { [CmdletBinding()] param( #The Id of the drive. You can pipe from Get-JMg [Parameter(ValueFromPipeline)][MicrosoftGraphDrive1]$Drive, #The path to the file. If not specified, it gets the root folder [String]$Path ) if (-not $Drive) { Write-Verbose 'No ID specified, fetching the contents of the /me drive.' $Drive = Get-JMgDrive } if (-not $Drive) { Write-Error 'No drive found.' return } $DriveId = $Drive.Id if (-not $Path) { return Get-MgDriveRoot -DriveId $DriveId } else { try { [MicrosoftGraphDriveItem1](Invoke-MgGraphRequest -Method GET "v1.0/drives/$DriveId/root:/$Path" -ErrorAction stop).Value } catch { if ($PSItem.exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { $PSItem.ErrorDetails = "The file or folder '$Path' does not exist in drive $($Drive.Name). Paths should be specified in folder/folder/file.txt format" } Write-Error -ErrorRecord $PSItem return } } } filter Get-JMgDriveChildItem { [CmdletBinding()] param( #The Id of the drive. You can pipe from Get-JMg [Parameter(ValueFromPipeline)][MicrosoftGraphDrive1]$Drive, #The path to the file. If not specified, it gets the root folder [String]$Path ) if (-not $Drive) { Write-Verbose 'No ID specified, fetching the contents of the /me drive.' $Drive = Get-JMgDrive } if (-not $Drive) { Write-Error '-Drive parameter was not supplied and your account does not have a default OneDrive' return } $DriveId = $Drive.Id $DrivePath = $Path ? "root:/${Path}:/children" : 'root/children' try { [MicrosoftGraphDriveItem1[]](Invoke-MgGraphRequest -Method GET "v1.0/drives/$DriveId/$DrivePath" -ErrorAction stop).Value } catch { if ($PSItem.exception.Response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) { $PSItem.ErrorDetails = "The file or folder '$Path' does not exist in drive $($Drive.Name). Paths should be specified in folder/folder/file.txt format" } Write-Error -ErrorRecord $PSItem return } } function Save-JmgDriveItem { [CmdletBinding(SupportsShouldProcess)] param( #Where to save the file. Defaults to your current Directory [String]$Path, #Overwrite Files [Switch]$Force, [Parameter(Mandatory, ValueFromPipeline)][MicrosoftGraphDriveItem1]$DriveItem ) begin { $jobs = [List[Job2]]@() } process { $dstPath = $Path if (-not $dstPath) { if (-not $DriveItem.Name) { Write-Error 'The drive item supplied does not have a filename. Please specify -Path with the full path to save.' } $dstPath = Join-Path $PWD $DriveItem.Name } $existingItem = Get-Item $dstPath -ErrorAction SilentlyContinue if ($ExistingItem -is [IO.DirectoryInfo]) { $dstPath = Join-Path $dstPath $DriveItem.Name $existingItem = Get-Item $dstPath -ErrorAction SilentlyContinue } if ($ExistingItem -and -not $Force) { Write-Error "The file '$dstPath' already exists. Use -Force to overwrite." return } $downloadUriProperty = '@microsoft.graph.downloadUrl' if (-not $DriveItem.AdditionalProperties.ContainsKey($downloadUriProperty)) { Write-Error "$($DriveItem.Name) is either a folder or cannot be downloaded." return } [uri]$downloadUri = $DriveItem.AdditionalProperties[$downloadUriProperty] if ($PSCmdlet.ShouldProcess($dstPath, "Download file $($DriveItem.Name)")) { $job = Start-ThreadJob -Name "Download-$($DriveItem.Name)" { Invoke-RestMethod -Uri $USING:downloadUri -OutFile $USING:dstPath } $jobs.Add($job) } } end { if ($jobs.count -gt 0) { Receive-Job $jobs -Wait -AutoRemoveJob } } } function Copy-JmgDriveItem { <# .SYNOPSIS #> [CmdletBinding(SupportsShouldProcess)] param( #Where to save the file. Defaults to your current Directory [String]$Path, #Overwrite Files [Switch]$Force, [Parameter(Mandatory, ValueFromPipeline)][MicrosoftGraphDriveItem1]$DriveItem ) begin { $jobs = [List[Job2]]@() } process { $dstPath = $Path if (-not $dstPath) { if (-not $DriveItem.Name) { Write-Error 'The drive item supplied does not have a filename. Please specify -Path with the full path to save.' } $dstPath = Join-Path $PWD $DriveItem.Name } $existingItem = Get-Item $dstPath -ErrorAction SilentlyContinue if ($ExistingItem -is [IO.DirectoryInfo]) { $dstPath = Join-Path $dstPath $DriveItem.Name $existingItem = Get-Item $dstPath -ErrorAction SilentlyContinue } if ($ExistingItem -and -not $Force) { Write-Error "The file '$dstPath' already exists. Use -Force to overwrite." return } $downloadUriProperty = '@microsoft.graph.downloadUrl' if (-not $DriveItem.AdditionalProperties.ContainsKey($downloadUriProperty)) { Write-Error "$($DriveItem.Name) is either a folder or cannot be downloaded." return } [uri]$downloadUri = $DriveItem.AdditionalProperties[$downloadUriProperty] if ($PSCmdlet.ShouldProcess($dstPath, "Download file $($DriveItem.Name)")) { $job = Start-ThreadJob -Name "Download-$($DriveItem.Name)" { Invoke-RestMethod -Uri $USING:downloadUri -OutFile $USING:dstPath } $jobs.Add($job) } } end { if ($jobs.count -gt 0) { Receive-Job $jobs -Wait -AutoRemoveJob } } } |