Downloading files from SharePoint is a common use case, when we are integrating 3rd party systems with SharePoint. In my previous articles, I have been explaining how you can upload files to SharePoint using PNP module. In this article, I want to show you how you can achieve download files from SharePoint using Graph API.
In the beginning, we will create an Entra ID Enterprise Application in Entra ID, grant the created Enterprise Application the permission to interact with selected sites. At the end, I will share a PowerShell script to download files from the SharePoint using Graph API.
What do I need to download files to SharePoint using Graph API?
To downlaod a file to SharePoint using Graph API, you need the following prerequisites fulfilled:
- You need your global administrator in your organization to grant the sites.selected permission for an Entra ID Enterprise Application
Please follow this article below. I have explained how to do it there:
How to configure Azure App registration for MS Graph | SPO Scripts
Your Sites.Selected App registration shall have following permission (at least):

How to Download Files from SharePoint using Graph API?
As we have created an app registration and gave it the permission to write to a selected site, we can use to download files from SharePoint using Graph API. In my example, I want to download an Excel file from the SharePoint Library Shared Documents. Make sure, that you have adjusted the parameters and have the client secret handy, which we have created in the previous steps.
Before you run the code, change the value of there parameters:
$Tenant = “m365x323732” -> Name of the tenant. You can fetch it from the URL of your SharePoint: https://m365x16735261.sharepoint.com
$AppID = “e0b8aefa-cb52-4bda-93a0-7d87120bcdbb” -> App ID, which you previously created in Entra ID
$SiteID = “e35cee33-6d10-4e2c-a83b-496a26062ad3” -> ID of the Site, where you want to download the file from. In my example it would be https://m365x323732.sharepoint.com/sites/SalesAndMarketing/_api/site/id
$LibraryURL = “https://m365x323732.sharepoint.com/sites/SalesAndMarketing/Shared%20Documents” -> URL to the SharePoint Library, where the file is located, which you want to download.
$Path = “C:\Users\Serkar\Desktop\DG-2000 Product Specification.docx” – Path to where the file should be downloaded to
$FileName = “DG-2000 Product Specification.docx” -> Name of the file
When you run the code, you will be asked to provide the client secret for your app registration.

Param ( $Tenant = "m365x323732", $AppID = "e0b8aefa-cb52-4bda-93a0-7d87120bcdbb", $SiteID = "e35cee33-6d10-4e2c-a83b-496a26062ad3", $LibraryURL = "https://m365x323732.sharepoint.com/sites/SalesAndMarketing/Shared%20Documents", $Path = "C:\Users\Serkar\Desktop\DG-2000 Product Specification.docx", $FileName = "DG-2000 Product Specification.docx" ) $AppCredential = Get-Credential($AppID) #region authorize $Scope = "https://graph.microsoft.com/.default" $Body = @{ client_id = $AppCredential.UserName client_secret = $AppCredential.GetNetworkCredential().password scope = $Scope grant_type = 'client_credentials' } $GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token" $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body $Access_token = $AuthorizationRequest.Access_token $Header = @{ Authorization = $AuthorizationRequest.access_token "Content-Type"= "application/json" } #endregion #region get drives $GraphUrl = "https://graph.microsoft.com/v1.0/sites/$SiteID/drives" $BodyJSON = $Body | ConvertTo-Json -Compress $Result = Invoke-RestMethod -Uri $GraphUrl -Method 'GET' -Headers $Header -ContentType "application/json" $DriveID = $Result.value| Where-Object {$_.webURL -eq $LibraryURL } | Select-Object id -ExpandProperty id If ($DriveID -eq $null){ Throw "SharePoint Library under $LibraryURL could not be found." } #endregion #region download file $Url = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/$($FileName)" $Response = Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data' Invoke-WebRequest -Uri $Response.'@microsoft.graph.downloadUrl' -OutFile $Path #endregion
At the end you will receive the following response as example
As you can see, the file was downloaded successfully from the SharePoint Library


Further Reference
You might be also interested in following articles, which are related to MS Graph API:
Security of app registration in Entra ID | SPO Scripts
Create SharePoint list items using Graph API (PowerShell) (sposcripts.com)
How to get SharePoint List Items with Graph API (PowerShell) | SPO Scripts
Hi,
The code works for me except the last part where it uses Invoke-WebRequest to download the file.
I get authenticated successfully and if I run variables such as $DriveID and $Response, it works fine and I get the output for $Response.’@microsoft.graph.downloadUrl’ as well. However, as soon as the code reaches to the last part, I get the below error:
Invoke-WebRequest : URL Accessed : mytenant.sharepoint.com
Your IP Address :
Username :
Support Information :
Access Denied (authentication_failed)
Your credentials could not be authenticated: “Credentials are missing.”. You will not be permitted access until your credentials can be verified.
This is typically caused by an incorrect username and/or password, but could also be caused by network problems.
For assistance, contact your network support team.
At line:1 char:1
+ Invoke-WebRequest -Uri $Response.’@microsoft.graph.downloadUrl’ -OutF …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Hi, it looks like that your app registration is lacking the read permissions. I have reworked the article, please check (For me it worked now in a fresh tenant with new app registrations).
I followed the article, created app registration, approved by Global admin, configured the read permissions on the site via PnP, and documented all information needed. Basically i get get this error below, but i can’t seem to find the solution. Can you help me solve this? Also, if you know or are familiar with my use case, any help would be appreciated! The full script i used is below the error code. Thanks in advance!
Things i tried:
– Remove the %20 and do spaces.
– Also tried to remove it
– English or Dutch doesnt matter
This is the URL when reaching the file (stripped the customer part):
This is the URL that is in my browser:
https://.sharepoint.com/sites/workplace_community/Gedeelde%20documenten/Forms/AllItems.aspx
Exception:
Line |
42 | Throw “SharePoint Library under $LibraryURL could not be found.”
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| SharePoint Library under https://sharepoint.com/sites/workplace_community/Gedeelde%20documenten/ could not be found.
See the script i used below:
Param (
$Tenant = “”,
$AppID = “”,
$SiteID = “”,
$LibraryURL = “https://.sharepoint.com/sites/workplace_community/Gedeelde%20documenten/”,
$Path = “C:\Users\\Downloads\Detection_Office365.ps1”,
$FileName = “Detection_Office365.ps1”
)
$AppCredential = Get-Credential($AppID)
#region authorize
$Scope = “https://graph.microsoft.com/.default”
$Body = @{
client_id = $AppCredential.UserName
client_secret = $AppCredential.GetNetworkCredential().password
scope = $Scope
grant_type = ‘client_credentials’
}
$GraphUrl = “https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token”
$AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method “Post” -Body $Body
$Access_token = $AuthorizationRequest.Access_token
$Header = @{
Authorization = $AuthorizationRequest.access_token
“Content-Type”= “application/json”
}
#endregion
#region get drives
$GraphUrl = “https://graph.microsoft.com/v1.0/sites/$SiteID/drives”
$BodyJSON = $Body | ConvertTo-Json -Compress
$Result = Invoke-RestMethod -Uri $GraphUrl -Method ‘GET’ -Headers $Header -ContentType “application/json”
$DriveID = $Result.value| Where-Object {$_.webURL -eq $LibraryURL } | Select-Object id -ExpandProperty id
If ($DriveID -eq $null){
Throw “SharePoint Library under $LibraryURL could not be found.”
}
#endregion
#region download file
$Url = “https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/$($FileName)”
$Response = Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType ‘multipart/form-data’
Invoke-WebRequest -Uri $Response.’@microsoft.graph.downloadUrl’ -OutFile $Path
#endregion
Dear Aaron, I recreated your setup and it was working for me.
Param (
$Tenant = "m365x16735261",
$AppID = "13b20636-5165-402e-aedc-5e05eeb4a57f",
$SiteID = "2dfd508f-98ab-46ad-839b-f0435730c79a",
$LibraryURL = "https://m365x16735261.sharepoint.com/sites/workplace_community/Gedeelde%20documenten",
$Path = "C:\Users\Serka\OneDrive\Desktop\Detection_Office365.ps1",
$FileName = "Detection_Office365.ps1"
)
$AppCredential = Get-Credential($AppID)
#region authorize
$Scope = "https://graph.microsoft.com/.default"
$Body = @{
client_id = $AppCredential.UserName
client_secret = $AppCredential.GetNetworkCredential().password
scope = $Scope
grant_type = 'client_credentials'
}
$GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token"
$AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body
$Access_token = $AuthorizationRequest.Access_token
$Header = @{
Authorization = $AuthorizationRequest.access_token
"Content-Type"= "application/json"
}
#endregion
#region get drives
$GraphUrl = "https://graph.microsoft.com/v1.0/sites/$SiteID/drives"
$BodyJSON = $Body | ConvertTo-Json -Compress
$Result = Invoke-RestMethod -Uri $GraphUrl -Method 'GET' -Headers $Header -ContentType "application/json"
$DriveID = $Result.value| Where-Object {$_.webURL -eq $LibraryURL } | Select-Object id -ExpandProperty id
If ($DriveID -eq $null){
Throw "SharePoint Library under $LibraryURL could not be found."
}
#endregion
#region download file
$Url = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/$($FileName)"
$Response = Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data'
Invoke-WebRequest -Uri $Response.'@microsoft.graph.downloadUrl' -OutFile $Path
#endregion
Try to remove the last / after Gedeelde%20documenten.
If I try it with / I get the same error as you.
Hi, i really like the website and i think you do a good job.
Since i am learning to work with MSGraph and PnP modules i got some key questions that are not listed in the tutorials:
1. How to get the site id, as $SiteID? In the manuals you don’t list how to get this drive id that you need to use for the end script.
2. $Tenant is this the Tenant ID? Tenant name? like contoso.com or what do you mean with that value?
Hey Aaron, I am glad that you enjoy the content.
You can obtain the siteID, by calling following URL:
The tenant variable, which is used in the parameter section, shall utilize the name of the tenant. You can fetch it from the SharePoint URL prefix. For my demo tenant it is:
https://m365x323732.sharepoint.com/
Let me add that to the article.