PowerShell OneDrive Automation with Graph API

PowerShell OneDrive Automation with Graph API

PowerShell Tips Editor 2 min read
PowerShell OneDrive Automation with Graph API

When a project closes, files need to move from team workstations to a central OneDrive archive — a process that takes an admin 20 minutes per project if done manually. Automating PowerShell OneDrive automation through the Microsoft Graph API makes it a zero-touch script: authenticate once, list files, upload the archive, share a link with the client, and log the operation. This post covers the complete workflow from OAuth token acquisition through file operations on OneDrive.

Authenticate with Graph API

Graph API authentication uses OAuth 2.0. For unattended scripts, use the client credentials flow with an Azure AD app registration. You need a tenant ID, client ID, and client secret from your app registration, which must have the Files.ReadWrite.All application permission:

$tenantId     = "your-tenant-id"
$clientId     = "your-client-id"
$clientSecret = "your-client-secret"

$tokenParams = @{
    Uri    = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    Method = "POST"
    Body   = @{
        client_id     = $clientId
        client_secret = $clientSecret
        scope         = "https://graph.microsoft.com/.default"
        grant_type    = "client_credentials"
    }
}
$token = (Invoke-RestMethod @tokenParams).access_token
$headers = @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" }
Write-Host "Authentication successful"

List OneDrive Files

List files in a user’s OneDrive root or a specific folder path. Replace me with a UPN like users/[email protected] when accessing another user’s drive from a service account:

$userId = "[email protected]"

# List root items
$listUri = "https://graph.microsoft.com/v1.0/users/$userId/drive/root/children"
$items   = (Invoke-RestMethod -Uri $listUri -Headers $headers).value

$items | Select-Object name,
    @{N='SizeMB';    E={ [Math]::Round($_.size / 1MB, 2) }},
    @{N='Modified';  E={ $_.lastModifiedDateTime }},
    @{N='Type';      E={ if ($_.folder) { 'Folder' } else { 'File' } }} |
    Format-Table -AutoSize

# List files in a specific folder
$folderPath = "Projects/2026"
$folderUri  = "https://graph.microsoft.com/v1.0/users/$userId/drive/root:/$folderPath`:/children"
$folderItems = (Invoke-RestMethod -Uri $folderUri -Headers $headers).value

Upload a File to OneDrive

For files under 4 MB, use the simple upload endpoint. For larger files, use the upload session API with chunked transfer:

$userId    = "[email protected]"
$localFile = "C:\Projects\FinalReport.xlsx"
$remoteFolder = "Archives/2026"
$fileName  = [System.IO.Path]::GetFileName($localFile)
$uploadUri = "https://graph.microsoft.com/v1.0/users/$userId/drive/root:/$remoteFolder/$fileName`:/content"

$fileBytes = [System.IO.File]::ReadAllBytes($localFile)
$uploadHeaders = @{
    Authorization  = "Bearer $token"
    "Content-Type" = "application/octet-stream"
}

$uploaded = Invoke-RestMethod -Uri $uploadUri -Method PUT -Headers $uploadHeaders -Body $fileBytes
Write-Host "Uploaded: $($uploaded.name) — ID: $($uploaded.id)"

Download a File

Download a file by its item ID or path. The Graph API returns a redirect to the download URL, which PowerShell follows automatically:

$userId     = "[email protected]"
$remotePath = "Archives/2026/FinalReport.xlsx"
$localDest  = "C:\Downloads\FinalReport.xlsx"

# Get the download URL
$itemUri  = "https://graph.microsoft.com/v1.0/users/$userId/drive/root:/$remotePath"
$item     = Invoke-RestMethod -Uri $itemUri -Headers $headers
$downloadUrl = $item."@microsoft.graph.downloadUrl"

Invoke-WebRequest -Uri $downloadUrl -OutFile $localDest
Write-Host "Downloaded to $localDest ($([Math]::Round((Get-Item $localDest).Length / 1KB)) KB)"

Create a Shared Link

Generate a shareable link for a file — either view-only or editable, either anonymous or organization-scoped:

$userId    = "[email protected]"
$itemId    = "item-id-from-upload-or-list"  # use $uploaded.id from upload step
$shareUri  = "https://graph.microsoft.com/v1.0/users/$userId/drive/items/$itemId/createLink"

$shareBody = @{
    type  = "view"    # "view" or "edit"
    scope = "organization"  # "anonymous" or "organization"
} | ConvertTo-Json

$shareResult = Invoke-RestMethod -Uri $shareUri -Method POST -Headers $headers -Body $shareBody
Write-Host "Share link: $($shareResult.link.webUrl)"

Manage OneDrive for Multiple Users

Iterate across multiple users to perform bulk operations — for example, listing large files across a department’s OneDrives:

$users = @("[email protected]", "[email protected]", "[email protected]")

$largeFiles = foreach ($userId in $users) {
    $listUri = "https://graph.microsoft.com/v1.0/users/$userId/drive/root/children?`$top=100"
    $items   = (Invoke-RestMethod -Uri $listUri -Headers $headers).value
    $items | Where-Object { $_.size -gt 100MB } | ForEach-Object {
        [PSCustomObject]@{
            User    = $userId
            Name    = $_.name
            SizeMB  = [Math]::Round($_.size / 1MB, 1)
            Modified = $_.lastModifiedDateTime
        }
    }
}
$largeFiles | Sort-Object SizeMB -Descending | Format-Table -AutoSize

Common Errors and Fixes

  • Files.ReadWrite permission scope required in app registration. The application permission Files.ReadWrite.All is needed to read and write files across all users. Files.ReadWrite (without All) only covers the signed-in user’s drive. Grant admin consent for application permissions in the Azure portal after adding them.
  • Content-Type header required for upload requests. PUT requests to the upload endpoint without "Content-Type" = "application/octet-stream" will fail with a 400 error or upload corrupted data. Always set the Content-Type explicitly for binary file uploads.

Related Cmdlets / See Also

Wrapping Up

OneDrive automation through the Microsoft Graph API gives PowerShell full file management capability without installing any extra modules. Authenticate with client credentials, use the simple upload endpoint for files under 4 MB, and build multi-user operations by iterating over UPNs. Store your client secret securely — never hard-code it in scripts.

Send-Item -To