Download files to SharePoint using Graph API

How to download files from SharePoint using Graph API (PowerShell)

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
  • You need to install PNP PowerShell module, to grant the Enterprise Application the permission to download files from the specific SharePoint Site

How to create the Enterprise Application to Download Files from SharePoint?

In the beginning it is import to understand, which permission the Enterprise Application needs so that we can download files from SharePoint using Graph API.

  1. Browse to Entra ID Portal
  2. Create an App Registration with Sites.Selected permissions
  3. Create and note down credentials for the App Registration


I will explain how to do it step-by-step.

Browse to Entra ID Portal

Open https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade in your browser.

If you have the global admin rights, I recommend authenticating with that account, so that you can directly grant your enterprise application the permission. Otherwise, you need to reauthenticate or ask your administrator to grant your enterprise application the permissions.

Create an App Registration with Sites.Selected permissions

Browse to the App Registration blade

Screenshot of the App registrations shortcut

Click on new registration

Screenshot of new enterprise app registration

Define for the App a meaningful name, which follows your organization standards (Different admins should recognize what the purpose for this app is) and register it.

Screenshot on how to create the App registration in MS Azure

Browse to API permissions to grant your app registration the permission to download files from SharePoint using Graph API.

Screenshot of configuration blade for App API permissions

Click on Add a permission

Screenshot of how to add the API permission to the App registration

Now choose Microsoft Graph

Chose of Microsoft Graph Permission

If you want your application to work in the background (Without any user authentication), you need to choose Application permission. As my intention is to show you how to automate the process, I am sharing with you the application permission way.

Screenshot of Application permission path of API permission

Now search for Sites.Selected and click on add permissions.

Graph Permissions

If you are signed in with a user account with global administrator privileges, you can grant administrator admin consent for your tenant. In other case, you either need to sign in with the global administrator account or you have to ask your administrator to grant your app registration the permission.

Screenshot of grant admin consent for tenant of the app registration

Create Secret for the App Registration

To authenticate with your enterprise application, you need to either download a certificate or create a secret for your enterprise application. In this case, I am creating a secret.

Browse to Certificates & secrets

Screenshot of Certificates & secrets blade

Screenshot on how to create a client secret

For the description, I think it makes sense to define which application is going to use your client secret. For the duration, I would go with the recommendation of Microsoft, as you might have lost this application out of sight in 24 months, which is the maximum duration for a client secret.

Screenshot of creation of app secret

Now note down, what you see under value, you can only see it now.

Screenshot of app secret

With that last step, your Enterprise application has the right permissions on Entra ID. In the next step you need to grant your enterprise Application the permission to write into the specific SharePoint site.

How to grant the App Registration Read Permissions for a Selected SharePoint Site?

In order to grant the app registration the permission, you need to ensure that PNP Module is installed on your client and that you are allowed to use it.

If you have not yet installed it, check the following documentation:

Connect to SharePoint with PowerShell | SharePoint Online (sposcripts.com)

If both conditions are applying, you can use this code to grant your App registration right permission to read in the site.

You can get your App ID by browsing to the overview page of your app registration.

Screenshot of Application (client) ID

Import-Module PnP.PowerShell

$AppID = "e0b8aefa-cb52-4bda-93a0-7d87120bcdbb"
$AppRegistrationName = "SP_Sites.Selected_SalesandMarketing"

$DisplayNameofSitePermission = "Enterprise Application $AppRegistrationName"
$SiteURL = "https://m365x323732.sharepoint.com/sites/SalesAndMarketing"


Connect-PnPOnline -Url $SiteURL -Interactive
Grant-PnPAzureADAppSitePermission -AppId $AppID -DisplayName $DisplayNameofSitePermission -Site $SiteURL -Permissions Read

You will be asked to authenticate.

Screenshot of authentication prompt

At the end, your app registration has read permissions.

Screenshot of granting AAD Permissions for SharePoint Site

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.

Screenshot of credential prompt
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

Screenshot of the downloaded file from SharePoint using Graph API
Result of download files from SharePoint using Graph

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

5 thoughts on “How to download files from SharePoint using Graph API (PowerShell)”

  1. 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

    1. 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

  2. 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?

    1. Hey Aaron, I am glad that you enjoy the content.

      You can obtain the siteID, by calling following URL:

      https://m365x323732.sharepoint.com/sites/SalesAndMarketing/_api/site/id

      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.

Leave a Comment