Category: Files

  • How to Get SharePoint Files with MSGraph with PowerShell

    With the newest MSGraph module one has the capability to get SharePoint files with MSGraph. I recently figured out, how to fetch them in a recursed manner. In this article I want to share with you how to get SharePoint files with MSGraph. You will need to get the files with MSGraph, if you are thinking about to do following things:

    • Read/ Write/ Delete Metadata of SharePoint Files
    • Obtain DriveItem IDs, if you want to upload files to specific folders
    • Download specific SharePoint files
    • Assess the storage of your SharePoint libraries

    What do you need to get SharePoint Files with MSGraph

    You need to fulfil two requirements, so that you can get SharePoint files with MSGraph recursedly.

    1. You need to configure an Azure App registration with read permission to the SharePoint sites, where you want to obtain the files from. If you have never done this, follow my article, where I explain how to configure your Azure App registration for MSGraph access.
      How to configure Azure App registration for MS Graph | SPO Scripts
    2. Install the MSGraph module for PowerShell
      Install the Microsoft Graph PowerShell SDK | Microsoft Learn

    Demo Setup

    In my demo setup I have a SharePoint Library, where I have stored some files and folders. I am showcasing you, so that you can understand what you can expect from the solution. I have also some files in a folder, which will show that the solution is able to fetch folder items.

    Get SharePoint Files with MSGraph
    SharePoint Subfolder

    I have created an Azure App registration with the Sites Read All permission. The authentication will be done with a self signed certificate.

    Azure App Registration with MSGraph permission

    Azure App Registration with certificate for authentication

    How to Get all SharePoint Files with MSGraph in PowerShell (recursively)

    Below you can find the PowerShell script to get all SharePoint files. I have created the function List-MgDriveItem in order to list the drive items of a drive. A drive item is either a file or a folder within a SharePoint Library You can adjust the function, if you want to have different attributes.

    function List-MgDriveItem
    {
        [CmdletBinding()]
        param (
            [Parameter()]
            $Drive,
            [Parameter()]
            $DriveItem
        )
    
        Try
        {
            if ($DriveItem.folder)
            {
                # Recurse in the folder to get the childitems of the folder
                $ChildItems = (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $DriveItem.Id  -ExpandProperty Children ).Children
    
                if ($ChildItems.Count -gt 0)
                {
                    <# Action to perform if the condition is true #>
                    $ChildItems | ForEach-Object { 
                        List-MgDriveItem -Drive $Drive -DriveItem (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $($_.Id) ) 
                    }
                }
    
            }
    
                $Result = New-Object psobject -Property @{
                DriveItemName = $DriveItem.name;
                DriveitemSize   = $DriveItem.Size
            }
            return $Result
        }
        catch
        {
            Return $Error[0]
        }
    
    }

    A sample output of a folder driveitem looks like this:

    PS C:\Users\Serka\OneDrive\Desktop\PS> $driveitem | Select-Object *
    
    webUrl               : https://m365x04995906.sharepoint.com/sites/Remoteliving/Shared%20Documents/General
    id                   : 01NLC4VWP4YR6WIO6O6JFIFTARZGEQZMAL
    createdBy            : {[user, System.Collections.Hashtable], [application, System.Collections.Hashtable]}
    lastModifiedBy       : {[user, System.Collections.Hashtable], [application, System.Collections.Hashtable]}
    lastModifiedDateTime : 31.07.2023 12:17:50
    name                 : General
    folder               : {[childCount, 3]}
    size                 : 4505145
    fileSystemInfo       : {[lastModifiedDateTime, 31.07.2023 12:17:50], [createdDateTime, 31.07.2023 12:17:50]}
    createdDateTime      : 31.07.2023 12:17:50
    parentReference      : {[id, 01NLC4VWN6Y2GOVW7725BZO354PWSELRRZ], [path, /drives/b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4/root:], [driveType, documentLibrary], [driveId, b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4]…}
    eTag                 : "{647DC4FC-CE3B-4AF2-82CC-11C9890CB00B},2"
    cTag                 : "c:{647DC4FC-CE3B-4AF2-82CC-11C9890CB00B},0"
    shared               : {[scope, users]}

    A sample output of a file driveitem looks like this:

    PS C:\Users\Serka\OneDrive\Desktop\PS> $driveitem | select-object * |Format-list
    
    createdDateTime              : 19.08.2023 20:56:39
    cTag                         : "c:{6C722880-DCC4-45F4-B64C-96805B32D473},2"
    lastModifiedBy               : {[user, System.Collections.Hashtable]}
    @microsoft.graph.downloadUrl : https://m365x04995906.sharepoint.com/sites/Remoteliving/_layouts/15/download.aspx?UniqueId=6c722880-dcc4-45f4-b64c-96805b32d473&Translate=false&tempauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAvbTM2NXgwNDk5NTkwNi5zaGFyZXBvaW50LmNvbUAxZjc5NWU5NS1jMDZiLTQxMDktOTI0ZS0zNTY5ZmRkZjQ5OWYiLCJpc3MiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAiLCJuYmYiOiIxNjk0OTc0NTMzIiwiZXhwIjoiMTY5NDk3ODEzMyIsImVuZHBvaW50dXJsIjoib0dEUzI3bnFpOEtUSHd5NHRaQ1NYVzE1Mk1KVGFMVkpZZmh0VUhBTVUrUT0iLCJlbmRwb2ludHVybExlbmd0aCI6IjE0MyIsImlzbG9vcGJhY2siOiJUcnVlIiwiY2lkIjoiVWxYbUJaczF1MHU4d3ZpSTFwTGhudz09IiwidmVyIjoiaGFzaGVkcHJvb2Z0b2tlbiIsInNpdGVpZCI6Ik1HRTNORGsxT1RBdE9EYzNPQzAwTnpjNUxUZ3dPR0V0TTJKaVlqbGlZekZoTldVeCIsImFwcF9kaXNwbGF5bmFtZSI6IlNQX1NlbnNpdGl2aXR5X0xhYmVscyIsIm5hbWVpZCI6IjkwNzQ3OTI1LTU1MGMtNDY1OC04NTBmLWIxOWQyNmVkMWE0M0AxZjc5NWU5NS1jMDZiLTQxMDktOTI0ZS0zNTY5ZmRkZjQ5OWYiLCJyb2xlcyI6ImFsbHNpdGVzLndyaXRlIiwidHQiOiIxIiwiaXBhZGRyIjoiMjAuMTkwLjE5MC4xMDMifQ.knKrUliPCxeMF4NQ2-3_FSAkEYRBD5nlTOvAVurT_gQ&ApiVersion=2.0
    file                         : {[mimeType, image/png], [hashes, System.Collections.Hashtable]}
    photo                        : {}
    name                         : 2023-06-25 00_18_51-Alfahosting.de® • Meine Rechnungen and 2 more pages - Personal - Microsoft​ Edge.png
    fileSystemInfo               : {[lastModifiedDateTime, 19.08.2023 20:56:39], [createdDateTime, 19.08.2023 20:56:39]}
    id                           : 01NLC4VWMAFBZGZRG46RC3MTEWQBNTFVDT
    shared                       : {[scope, users]}
    image                        : {[width, 868], [height, 406]}
    createdBy                    : {[user, System.Collections.Hashtable]}
    size                         : 23175
    eTag                         : "{6C722880-DCC4-45F4-B64C-96805B32D473},1"
    lastModifiedDateTime         : 19.08.2023 20:56:39
    webUrl                       : https://m365x04995906.sharepoint.com/sites/Remoteliving/Shared%20Documents/2023-06-25%2000_18_51-Alfahosting.de%C2%AE%20%E2%80%A2%20Meine%20Rechnungen%20and%202%20more%20pages%20-%20Personal%20-%20Microsoft%E2%80%8B%20Edge.png
    parentReference              : {[id, 01NLC4VWN6Y2GOVW7725BZO354PWSELRRZ], [path, /drives/b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4/root:], [driveType, documentLibrary], [driveId, b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4]…}

    Here you can find the PowerShell script to get all SharePoint files with MSGraph. Keep in mind that you replace the param block with the values from your tenant, app etc.

    Param (
        $AppID = "90747925-550c-4658-850f-b19d26ed1a43", # Replace with the App ID of your App registration
        $Tenant = "1f795e95-c06b-4109-924e-3569fddf499f", # Replace with the Tenant ID
        $SiteID = "0a749590-8778-4779-808a-3bbb9bc1a5e1", # Replace it with the SiteID of your SharePoint Site https://m365x04995906.sharepoint.com/sites/Remoteliving/_api/site/id
        $LibraryName = "Documents", # Replace with the list name,
        $CertificatePath = "C:\Users\Serka\OneDrive\Desktop\PS\SPSitesReadAll.pfx", # Replace with the path to your certificate,
        $CertificatePassword = "XXX" # Replace with the password of your certificate
    )
    
    function List-MgDriveItem {
        [CmdletBinding()]
        param (
            [Parameter()]
            $Drive,
            [Parameter()]
            $DriveItem
        )
        
        if ($DriveItem.folder) {
            <# Action to perform if the condition is true #>
            Write-Output "Folder $($DriveItem.Name), size $($DriveItem.Size) `n"
    
            # Recurse in the folder to get the childitems of the folder
            $ChildItems = $null
            $ChildItems = (Get-MgDriveItem -DriveId $Drive.id -DriveItemId $DriveItem.id  -ExpandProperty Children ).Children
    
            if ($ChildItems.Count -gt 0) {
                <# Action to perform if the condition is true #>
                $ChildItems | ForEach-Object { 
                    List-MgDriveItem -Drive $Drive -DriveItem (Get-MgDriveItem -DriveId $Drive.ID -DriveItemId $($_.Id) ) 
                }
            }
            
        }   
        elseif ($null -ne $DriveItem.file) {
            <# Action when this condition is true #>
            Write-Output "File $($DriveItem.Name), size $($DriveItem.Size) `n"
        }
    }
    
    #region prerequistes
    Import-Module microsoft.graph.authentication
    
    # Load the certificate from file
    $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertificatePath, $CertificatePassword)
    
    # Authenticate
    Connect-MgGraph -ClientID $AppID -TenantId $Tenant -CertificateThumbprint $Certificate.Thumbprint
    #endregion
    
    #region main
    # Get drive
    $Drive = Get-MgSite -SiteId $SiteID -Property Drives -ExpandProperty Drives | Select-Object Drives -ExpandProperty Drives | Where-Object { $_.Name -eq $LibraryName }
    
    # Get drive item
    $DriveItems = Invoke-MgGraphRequest -Uri "v1.0/drives/$($Drive.ID)/root/children"
    
    foreach ($DriveItem in $DriveItems.Value) {
    
        #Get All drivitems below root
        List-MgDriveItem -Drive $Drive -DriveItem $DriveItem
    }
    
    #
    #endregion

    As you can see below, the script populates the space ocupation for each SharePoint file/ folder.

    Output of get SharePoint files for MSGraph

    How to get DriveItemIDs for SharePoint Files?

    I have modified the script, so that you can get all driveItemIDs for all SharePoint Files in your library. Below you can find the script with certificate based authentication.

    Param 
    (
        $AppId = "90747925-550c-4658-850f-b19d26ed1a43", # Replace with the App Id of your App registration
        $Tenant = "1f795e95-c06b-4109-924e-3569fddf499f", # Replace with the Tenant prefix
        $SiteId = "0a749590-8778-4779-808a-3bbb9bc1a5e1", # Replace it with the SiteId of your SharePoint Site https://m365x04995906.sharepoint.com/sites/Remoteliving/_api/site/Id
        $LibraryName = "Documents", # Replace with the list name,
        $CertificatePath = "C:\Users\Serka\OneDrive\Desktop\PS\SPSitesReadAll.pfx", # Replace with the path to your certificate,
        $CertificatePasswordPath = "C:\Users\Serka\OneDrive\Desktop\PS\SPSitesReadAll.key" # Replace with the path to the password of your certificate,
    )
    
    function List-MgDriveItem
    {
        [CmdletBinding()]
        param (
            [Parameter()]
            $Drive,
            [Parameter()]
            $DriveItem
        )
    
        Try
        {
            if ($DriveItem.folder)
            {
                # Recurse in the folder to get the childitems of the folder
                $ChildItems = (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $DriveItem.Id  -ExpandProperty Children ).Children
    
                if ($ChildItems.Count -gt 0)
                {
                    <# Action to perform if the condition is true #>
                    $ChildItems | ForEach-Object { 
                        List-MgDriveItem -Drive $Drive -DriveItem (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $($_.Id) ) 
                    }
                }
    
            }
    
                $Result = New-Object psobject -Property @{
                DriveItemName = $DriveItem.name;
                DriveitemId   = $DriveItem.Id;
                DriveId       = $Drive.Id;
                DriveItemURL  = $DriveItem.webUrl
            }
            return $Result
        }
        catch
        {
            Return $Error[0]
        }
    
    }
    
    
    # Load required Modules
    Import-Module Microsoft.Graph.Authentication, Microsoft.Graph.Files
    
    # Load the certificate from file
    $CertificatePassword = (Import-Clixml -Path $CertificatePasswordPath).getnetworkcredential().Password
    $Certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertificatePath, $CertificatePassword)
    
    # Authenticate
    Connect-MgGraph -ClientId $AppId -TenantId $Tenant -CertificateThumbprint $Certificate.Thumbprint -NoWelcome
     
    #region prepare export
    
    # Get drive
    $Drive = Get-MgSite -SiteId $SiteId -Property Drives -ExpandProperty Drives | Select-Object Drives -ExpandProperty Drives | Where-Object { $_.Name -eq $LibraryName }
    
    # Get drive item
    $DriveItems = Invoke-MgGraphRequest -Uri "v1.0/drives/$($Drive.Id)/root/children"
    
    $ItemExportRaw = New-Object System.Collections.Generic.List[object]
    
    foreach ($DriveItem in $DriveItems.Value)
    {
        #Get All drivitems below root
        $ItemExportRaw.Add( $(List-MgDriveItem -Drive $Drive -DriveItem $DriveItem))
    }
    
    $ItemExport = New-Object System.Collections.Generic.List[object]
    
    
    $ItemExportRaw |ForEach-Object {
    
            $_ | ForEach-Object {$ItemExport.add($_)}    
    }
    
    $ItemExport
    #endregion

    If you want to use app secrets instead of certificate based authentication, you can use following script

    Param 
    (
        $AppId = "13b20636-5165-402e-aedc-5e05eeb4a57f", # Replace with the App Id of your App registration
        $Tenant = "b7b0953d-d866-4606-9199-7642f06a0b2e", # Replace with the Tenant prefix
        $SiteId = "2dfd508f-98ab-46ad-839b-f0435730c79a", # Replace it with the SiteId of your SharePoint Site https://m365x04995906.sharepoint.com/sites/Remoteliving/_api/site/Id
        $LibraryName = "Documenten" # Replace with the list name,
    )
    
    function List-MgDriveItem
    {
        [CmdletBinding()]
        param (
            [Parameter()]
            $Drive,
            [Parameter()]
            $DriveItem
        )
    
        Try
        {
            if ($DriveItem.folder)
            {
                # Recurse in the folder to get the childitems of the folder
                $ChildItems = (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $DriveItem.Id  -ExpandProperty Children ).Children
    
                if ($ChildItems.Count -gt 0)
                {
                    <# Action to perform if the condition is true #>
                    $ChildItems | ForEach-Object { 
                        List-MgDriveItem -Drive $Drive -DriveItem (Get-MgDriveItem -DriveId $Drive.Id -DriveItemId $($_.Id) ) 
                    }
                }
    
            }
    
                $Result = New-Object psobject -Property @{
                DriveItemName = $DriveItem.name;
                DriveitemId   = $DriveItem.Id;
                DriveId       = $Drive.Id;
                DriveItemURL  = $DriveItem.webUrl
            }
            return $Result
        }
        catch
        {
            Return $Error[0]
        }
    
    }
    
    
    # Load required Modules
    Import-Module Microsoft.Graph.Authentication, Microsoft.Graph.Files
    
    # Complie Client Credential
    $ClientSecretCredential = Get-Credential($AppID)
    
    # Authenticate
    Connect-MgGraph -TenantId $Tenant -ClientSecretCredential $ClientSecretCredential -NoWelcome
    
    #region prepare export
    
    # Get drive
    $Drive = Get-MgSite -SiteId $SiteId -Property Drives -ExpandProperty Drives | Select-Object Drives -ExpandProperty Drives | Where-Object { $_.Name -eq $LibraryName }
    
    # Get drive item
    $DriveItems = Invoke-MgGraphRequest -Uri "v1.0/drives/$($Drive.Id)/root/children"
    
    $ItemExportRaw = New-Object System.Collections.Generic.List[object]
    
    foreach ($DriveItem in $DriveItems.Value)
    {
        #Get All drivitems below root
        $ItemExportRaw.Add( $(List-MgDriveItem -Drive $Drive -DriveItem $DriveItem))
    }
    
    $ItemExport = New-Object System.Collections.Generic.List[object]
    
    
    $ItemExportRaw |ForEach-Object {
    
            $_ | ForEach-Object {$ItemExport.add($_)}    
    }
    
    $ItemExport
    #endregion

    Once the script is exectued, all driveitems are shown like this:

    Output of all driveitems

    Further Reference

    Here you can obtain the permission levels, which are required for your Azure App Registration:
    Microsoft Graph permissions reference – Microsoft Graph | Microsoft Learn

    Here you can find the cmdlet map for MS Graph module

    Find Azure AD and MSOnline cmdlets in Microsoft Graph PowerShell | Microsoft Learn

  • How To Download a SharePoint Library Using Graph API (PowerShell)

    SharePoint is a powerful platform for managing and collaborating on documents, but downloading files from SharePoint libraries can be a tricky, especially when it comes to deal with large files or a large amount of documents. In this post, I want to show you a straightforward guide to download a SharePoint Library using PowerShell with Microsoft’s Graph API for SharePoint administrators, developers or system integrators. This tutorial will help you streamline your automation in terms of downloading SharePoint libraries. At the end, you’ll find a ready to use PowerShell script, which you can use to download the SharePoint Library using Graph API.

    If you want to download a SharePoint library using Graph API, there are certain prerequisites you must fulfil. To successfully download files from SharePoint using Graph API, you need to ensure the following:

    What do I need to download files to SharePoint using Graph API?

    • You have the sites.selected permission for an Azure Enterprise Application. Your global administrator in your organization can consent this permission.
    • You have installed the PNP PowerShell module to allow the Enterprise Application permission to download files from the specific SharePoint Site. Without this module, the Enterprise Application will not be able to access and download the files. Learn here how you can install the PNP PowerShell module:
      Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    By fulfilling these prerequisites, you can easily download SharePoint libraries using Graph API and streamline your document management process.

    How to create the Enterprise Application to Download a SharePoint Library using Graph?

    1. Go to the Azure Active Directory Portal to create an App Registration with Sites.Selected permissions. This allows the Enterprise Application to access and download files from SharePoint.
    2. Create a credential object for the App registration.
    3. Once you have created the credentials for the App Registration, make sure to note down the credentials for future use. These credentials will be required when you are running the PowerShell script to download a SharePoint Library using Graph.

    By following these simple steps, you can create an Enterprise Application with the necessary permissions to download files from SharePoint using Graph API. You will get to know in the upcoming passage how to create the Enterprise Application step-by-step.

    Browse to Azure Active Directory 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 in Azure Active Directory Portal.

    Screenshot of the App registration Azure Active Directory Tenant overview

    Click on “New registration” to create a new App Registration.

    Screenshot of new enterprise app registration

    Define a meaningful name for the App that follows your organization’s standards, so that different administrators can easily recognize the purpose of the App. In this case I am using the name SP_Sites.Selected_Retail name, so that Azure Active directory administrators can recognize that the App Registration will have the permission Sites.Selected for the SharePoint Site Retail

    Browse to the “API Permissions” to grant your App Registraion the permission to download the SharePoint Library using Graph API.

    Screenshot of API Permissions link

    Click on “Add a permission”

    Screenshot of API Permissions overview to add a permission

    As we want to go with Microsoft Graph, please choose “Microsoft Graph”.

    Screenshot of the selection for APIs

    To download a SharePoint library using Graph API, it’s crucial to choose the right permission level. If you want your application to run in the background without requiring user authentication, you’ll need to select the Application permission option. In this tutorial on how to download a SharePoint library using Graph API, we’ll focus on the application permission method to automate the process of downloading files from SharePoint.

    Screenshot of API permission types

    Now add the sites.selected permission, which allows you to access contents of a single SharePoint site.

    Screenshot of Microsoft Graph Permissions for SharePoint Sites

    To grant admin consent for your tenant, you must be signed in with a user account that has global administrator privileges. However, if you don’t have these privileges ask your administrator with the global administrator role to consent permissions for the App Registration.

    Screenshot of granting of admin consent for current tenant

    Create a secret for the App Registration

    Now as you have configured the App permissions, you have to ensure, that you can authenticate with your App Registration. To configure the authentication, click on “Certificates & secrets”.

    Screenshot of certificates & secrets of App Registrations

    Now you can either upload certificates, create client secrets or create federated credentials. In this tutorial I will show you how to work with client secrets.

    Screenshot of Selection for App Registration Authentication

    Now you need to define a credential name. I choose the client and the IP Address to recognize, which Server/ Application will use the client secret.

    Screenshot of Add a client secret

    As you have added a client secret, make sure that you store the value for the secret as you are only able to see it, when you create it.

    Screenshot of exposed client secret value

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

    To grant permission to your app registration, it’s essential to ensure that the PNP Module is installed on your client and that you have permission to use it. If you haven’t already installed the PNP Module, check out the documentation: Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    Once you have confirmed that both conditions are met, you can use the following code to grant your app registration the necessary permissions to read from the site. You can find your app ID on the overview page of your app registration.

    Take the App ID from the overview page of your App registration and run the code below.

    Screenshot of App ID
    $AppID = "333d169e-7f2d-417c-b349-8498b2248802"
    $AppRegistrationName = "SP_Sites.Selected_Retail"
    $SiteURL = "https://m365x69801090.sharepoint.com/sites/Retail"
    
    Import-Module PnP.PowerShell
    
    $DisplayNameofSitePermission = "Enterprise Application $AppRegistrationName"
    
    
    Connect-PnPOnline -Url $SiteURL -Interactive
    Grant-PnPAzureADAppSitePermission -AppId $AppID -DisplayName $DisplayNameofSitePermission -Site $SiteURL -Permissions Read

    Now login with your account.

    Screenshot of interactive authentication prompt

    This is how it looks like, when the permission was granted successfully for the App Registration.

    Screenshot of granted site permission

    How to Download Files from SharePoint using Graph API?

    Now that we’ve created an app registration and granted it permission to write to a selected site, we can use it to download a SharePoint Library using Graph API. In this example, we’ll download the SharePoint Library Documents from the Retail SharePoint site. Before running the code, make sure you have adjusted the parameters and have the client secret ready, which we created in the previous steps.

    You can get the SiteID by browsing to the siteID page

    <siteurl>/_api/site/id

    For my example:

    https://m365x69801090.sharepoint.com/sites/Retail/_api/site/id

    When you run the code, you’ll be prompted to enter the client secret for your app registration.

    Screenshot of SharePoint Site ID

    Once you have replaced the parameters with your actual values, you can download aSharePoint Library with Graph using PowerShell. You will get an authentication prompt, where you have to enter the client secret for the App Registration.

    Screenshot of authentication promt for app registration

    # Script to download a SharePoint Library using Graph
    # Author: Serkar Aydin - Serkar@workplace-automation.com
    # Accept input parameters
    Param (
        $Tenant = "m365x69801090",
        $AppID = "333d169e-7f2d-417c-b349-8498b2248802",
        $SiteID = "74667e94-9fcf-41ab-8e2f-0dfaf0294de8",
        $LibraryURL = "https://m365x69801090.sharepoint.com/sites/Retail/Shared%20Documents",
        $Path = "C:\Users\Serkar\Desktop\Retail"
    )
    
    Function DownloadDriveItem {
    
        param(
            $DriveItem,
            $URL,
            $Header,
            $Path
            
        )
        
        #if there is no downloadurl, it is a folder
        If (!$DriveItem. '@microsoft.graph.downloadUrl') {
        
            Write-Output "Downloading the folder $($DriveItem.weburl)"
        
            #Create a folder for the SharePoint folder
            $FolderPath = "$Path\$($DriveItem.name)"
            New-Item -ItemType Directory -Path $FolderPath | Out-Null
    
            $Url  = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/$($DriveItem.ID)/children"
            $Response =  Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data' 
    
            $Response.value | ForEach-Object {
    
                DownloadDriveItem -DriveItem $_ -URL $Url -Header $Header -Path $FolderPath
    
            }
    
        }
    
        #Else it is a file
        Else{
        
            Write-Output "Downloading the file $($DriveItem.weburl)"
            Invoke-WebRequest -Uri $DriveItem.'@microsoft.graph.downloadUrl' -OutFile "$Path\$($DriveItem.name)"
        }
    }
    
    
    # Prompt for application credentials
    $AppCredential = Get-Credential($AppID)
    
    #region authorize
    
    # Set the scope for the authorization request
    $Scope = "https://graph.microsoft.com/.default"
    
    # Build the body of the authorization request
    $Body = @{
        client_id = $AppCredential.UserName
        client_secret = $AppCredential.GetNetworkCredential().password
        scope = $Scope
        grant_type = 'client_credentials'
    }
    
    # Build the URL for the authorization request
    $GraphUrl = "https://login.microsoftonline.com/$($Tenant).onmicrosoft.com/oauth2/v2.0/token"
    
    # Send the authorization request and retrieve the access token
    $AuthorizationRequest = Invoke-RestMethod -Uri $GraphUrl -Method "Post" -Body $Body
    $Access_token = $AuthorizationRequest.Access_token
    
    # Build the header for API requests
    $Header = @{
        Authorization = $AuthorizationRequest.access_token
        "Content-Type"= "application/json"
    }
    
    #endregion
    
    #region get drives
    
    # Build the URL to retrieve the list of drives in the SharePoint site
    $GraphUrl = "https://graph.microsoft.com/v1.0/sites/$SiteID/drives"
    
    # Convert the body of the authorization request to JSON and send the API request
    $BodyJSON = $Body | ConvertTo-Json -Compress
    $Result = Invoke-RestMethod -Uri $GraphUrl -Method 'GET' -Headers $Header -ContentType "application/json"
    
    # Find the ID of the specified SharePoint library
    $DriveID = $Result.value| Where-Object {$_.webURL -eq $LibraryURL } | Select-Object id -ExpandProperty id
    
    # If the SharePoint library cannot be found, throw an error
    If ($DriveID -eq $null){
        Throw "SharePoint Library under $LibraryURL could not be found."
    }
    
    #endregion
    
    #region create folder. If there is already one, replace it with the new folder
    
    Try {
    
        New-Item -ItemType Directory -Path $Path -ErrorAction Stop | Out-Null
    }
    Catch {
    
            Remove-Item $Path -Force -Recurse
            New-Item -ItemType Directory -Path $Path -Force | Out-Null
    }
    #endregion
    
    #region download library
    
    $Url  = "https://graph.microsoft.com/v1.0/drives/$DriveID/root/children"
    $Response =  Invoke-RestMethod -Uri $Url -Headers $Header -Method Get -ContentType 'multipart/form-data' 
    
    $Response.value | ForEach-Object {
    
        DownloadDriveItem -DriveItem $_ -URL $Url -Header $Header -Path $Path
    
    }
    
    #endregion
    Screenshot of the process "Download a SharePoint Library using Graph"

    As you can see, I was able to download a SharePoint Library using Graph API. All folders and subfolders are created on my local C drive. Result of Download a SharePoint Library using Graph 1

    Result of Download a SharePoint Library using Graph 1
    Result of Download a SharePoint Library using Graph File share 1
    Result of Download a SharePoint Library using Graph 2
    Result of Download a SharePoint Library using Graph File share 2
    Result of Download a SharePoint Library using Graph 3
    Result of Download a SharePoint Library using Graph File share 3

    Further Reference

    You might want to download single files from SharePoint with Graph API? Check this out:
    How to download files from SharePoint using Graph API (PowerShell) (workplace-automation.com/)

    Learn how to access SharePoint via Graph in PowerShell: Access SharePoint via Graph API in PowerShell

    Learn how to upload files to SharePoint using Graph (PowerShell):
    How to Upload Files to SharePoint using Graph API PowerShell (workplace-automation.com/)

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

    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:

    Your Sites.Selected App registration shall have following permission (at least):

    Screenshot of app registration with sites selected permissions

    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) (workplace-automation.com/)

    How to get SharePoint List Items with Graph API (PowerShell) | SPO Scripts

  • How to upload files to SharePoint using Graph API (PowerShell)

    How to upload files to SharePoint using Graph API (PowerShell)

    Uploading files to 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 upload files to SharePoint using Graph API.

    Note: This method works for files, where the size is maximum 4 MB.

    In the beginning, we will create an Azure Enterprise Application in Azure Active Directory. Then we will grant the created Enterprise Application the permission to interact with selected sites. At the end, I will share a PowerShell script to upload files to the SharePoint using Graph API.

    What do I need to upload files to SharePoint using Graph API?

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

    How to create the Enterprise Application to upload Files to SharePoint?

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

    1. Browse to Azure Active Directory 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 Azure Active Directory 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 upload files to 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.

    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 upload 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 Azure Active Directory. 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 Write 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 (workplace-automation.com/)

    If both conditions are applying, you can use this code to grant your App registration right permission to write 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 Write

    You will be asked to authenticate.

    Screenshot of authentication prompt

    At the end, your app registration has write permissions.

    Screenshot of App registration to write permissions to SharePoint Site

    How to Upload Files to 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 upload files to SharePoint using Graph API. In my example, I want to upload a picture to 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.

    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\security.png"
    )
    
    $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 upload file
    
    $FileName = $Path.Split("\")[-1]
    $Url  = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/$($FileName):/content"
    
    Invoke-RestMethod -Uri $Url -Headers $Header -Method Put -InFile $Path -ContentType 'multipart/form-data' -Verbose
    #endregion 

    At the end you will receive the following response as example

    VERBOSE: PUT with -1-byte payload
    VERBOSE: received -1-byte response of content type application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
    
    
    @odata.context               : https://graph.microsoft.com/v1.0/$metadata#drives('b%21M-5c4xBtLE6oO0lqJgYq04f6JqJ_iTlEhBdXmkuqxRI4cWqlVo8-QKlAJO6KoBgT')/items/$entity
    @microsoft.graph.downloadUrl : https://m365x323732.sharepoint.com/sites/SalesAndMarketing/_layouts/15/download.aspx?UniqueId=9f900d6c-d023-41cc-8839-61f079916c03&Translate=fals
                                   e&tempauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAvbTM2NXgzMjM3MzIuc2hhcmVwb2ludC5jb21AOG
                                   I5Y2ZmMzQtNjg4YS00YTkzLThhZDMtMjNjYTQ3ZWU2ZTcyIiwiaXNzIjoiMDAwMDAwMDMtMDAwMC0wZmYxLWNlMDAtMDAwMDAwMDAwMDAwIiwibmJmIjoiMTY3MjgyMjg4MSIsImV4cCI6IjE
                                   2NzI4MjY0ODEiLCJlbmRwb2ludHVybCI6IjYrUUtpd1NldjBJdksyUFkwS2wyV0YveWNLSnYxQkdPc1RFb1pWclRyems9IiwiZW5kcG9pbnR1cmxMZW5ndGgiOiIxNDYiLCJpc2xvb3BiYWNr
                                   IjoiVHJ1ZSIsImNpZCI6IlpUSTFaakE0WTJJdE5XUTRPUzAwTldRd0xXSmtPVEV0WlRnMFpEUmhZVFJrTW1VMyIsInZlciI6Imhhc2hlZHByb29mdG9rZW4iLCJzaXRlaWQiOiJaVE0xWTJWb
                                   E16TXRObVF4TUMwMFpUSmpMV0U0TTJJdE5EazJZVEkyTURZeVlXUXoiLCJhcHBfZGlzcGxheW5hbWUiOiJTUF9TaXRlcy5TZWxlY3RlZF9TYWxlc2FuZE1hcmtldGluZyIsIm5hbWVpZCI6Im
                                   UwYjhhZWZhLWNiNTItNGJkYS05M2EwLTdkODcxMjBiY2RiYkA4YjljZmYzNC02ODhhLTRhOTMtOGFkMy0yM2NhNDdlZTZlNzIiLCJyb2xlcyI6InNlbGVjdGVkc2l0ZXMiLCJ0dCI6IjEiLCJ
                                   1c2VQZXJzaXN0ZW50Q29va2llIjpudWxsLCJpcGFkZHIiOiIyMC4xOTAuMTkwLjEwMSJ9.c0kydmY4eUFWS2lvWmJkTG1yTjJGbmV0SkVHVHRtdjNZWHppbm1SKytTRT0&ApiVersion=2.0
    createdDateTime              : 2023-01-04T09:01:21Z
    eTag                         : "{9F900D6C-D023-41CC-8839-61F079916C03},1"
    id                           : 01P5GC6MTMBWIJ6I6QZRAYQOLB6B4ZC3AD
    lastModifiedDateTime         : 2023-01-04T09:01:21Z
    name                         : security.png
    webUrl                       : https://m365x323732.sharepoint.com/sites/SalesAndMarketing/Shared%20Documents/security.png
    cTag                         : "c:{9F900D6C-D023-41CC-8839-61F079916C03},2"
    size                         : 129678
    createdBy                    : @{application=; user=}
    lastModifiedBy               : @{application=; user=}
    parentReference              : @{driveType=documentLibrary; driveId=b!M-5c4xBtLE6oO0lqJgYq04f6JqJ_iTlEhBdXmkuqxRI4cWqlVo8-QKlAJO6KoBgT; id=01P5GC6MV6Y2GOVW7725BZO354PWSELRRZ; 
                                   path=/drives/b!M-5c4xBtLE6oO0lqJgYq04f6JqJ_iTlEhBdXmkuqxRI4cWqlVo8-QKlAJO6KoBgT/root:}
    file                         : @{mimeType=image/png; hashes=}
    fileSystemInfo               : @{createdDateTime=2023-01-04T09:01:21Z; lastModifiedDateTime=2023-01-04T09:01:21Z}
    image                        : 
    shared                       : @{scope=users}
    
    
    
    Screenshot of the response

    As you can see, the file was uploaded successfully to the SharePoint Library

    Screenshot of the uploaded files to SharePoint using Graph API
     Screenshot of the picture in the SharePoint Library

    How to Upload Files to a Folder in SharePoint using Graph API?

    In order to upload files to folders, you just need to make sure that your URI contains the folder structure after root:/.
    In my example below, I uploaded apicture to the Subfolder folder.

    $Url = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/General/Subfolder/$($FileName):/content"
    
    Param (
        $Tenant = "m365x04995906",
        $AppID = "3669592a-9085-4f09-8c03-2b2223aa002c",
        $SiteID = "0a749590-8778-4779-808a-3bbb9bc1a5e1",
        $LibraryURL = "https://m365x04995906.sharepoint.com/sites/Remoteliving/Shared%20Documents",
        $Path = "C:\Users\Serka\OneDrive\Desktop\pngs\00003.jpg",
        $Folder = "General/Subfolder"
    )
    
    #$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 upload file
    
    $FileName = $Path.Split("\")[-1]
    $Url = "https://graph.microsoft.com/v1.0/drives/$DriveID/items/root:/$Folder/$($FileName):/content"
    
    Invoke-RestMethod -Uri $Url -Headers $Header -Method Put -InFile $Path -ContentType 'multipart/form-data' -Verbose
    #endregion 

    Once executed, you’ll get back following response:

    VERBOSE: HTTP/1.1 PUT with 232877-byte payload
    VERBOSE: received -byte response of content type application/json
    VERBOSE: Content encoding: utf-8
    
    @odata.context               : https://graph.microsoft.com/v1.0/$metadata#drives('b%21kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4')/items/$entity
    @microsoft.graph.downloadUrl : https://m365x04995906.sharepoint.com/sites/Remoteliving/_layouts/15/download.aspx?UniqueId=2c3c2e98-5be4-4ce2-8a81-2c0d4bab00b4&Translate=false&tempauth=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAvbTM2NXgwNDk5NTkwNi5zaGFyZXBvaW50LmNv 
                                   bUAxZjc5NWU5NS1jMDZiLTQxMDktOTI0ZS0zNTY5ZmRkZjQ5OWYiLCJpc3MiOiIwMDAwMDAwMy0wMDAwLTBmZjEtY2UwMC0wMDAwMDAwMDAwMDAiLCJuYmYiOiIxNjk3NDQ0MDk5IiwiZXhwIjoiMTY5NzQ0NzY5OSIsImVuZHBvaW50dXJsIjoiWjA4ZkI5NTdNQklCckgzRUg4VHp1N0RuT3lTWVhCMHQ4VE5zZTNvNmMvMD0iLCJlbmRwb2ludHVybExlbmd0aCI6IjE0MyIsImlzbG 
                                   9vcGJhY2siOiJUcnVlIiwiY2lkIjoiWktRTFZjNHhUMEt2RlF2YSsxMjZJZz09IiwidmVyIjoiaGFzaGVkcHJvb2Z0b2tlbiIsInNpdGVpZCI6Ik1HRTNORGsxT1RBdE9EYzNPQzAwTnpjNUxUZ3dPR0V0TTJKaVlqbGlZekZoTldVeCIsImFwcF9kaXNwbGF5bmFtZSI6IlNQU2l0ZXNfUmVhZFdyaXRlQWxsIiwibmFtZWlkIjoiMzY2OTU5MmEtOTA4NS00ZjA5LThjMDMtMmIyMjIz 
                                   YWEwMDJjQDFmNzk1ZTk1LWMwNmItNDEwOS05MjRlLTM1NjlmZGRmNDk5ZiIsInJvbGVzIjoiYWxsc2l0ZXMud3JpdGUiLCJ0dCI6IjEiLCJpcGFkZHIiOiIyMC4xOTAuMTkwLjk5In0.2EMXEM184-nAFJnbOJy_PM8q5YKvOK66IoqggTX0g_E&ApiVersion=2.0
    createdDateTime              : 16.10.2023 08:14:59
    eTag                         : "{2C3C2E98-5BE4-4CE2-8A81-2C0D4BAB00B4},1"
    id                           : 01NLC4VWMYFY6CZZC34JGIVAJMBVF2WAFU
    lastModifiedDateTime         : 16.10.2023 08:14:59
    name                         : 00003.jpg
    webUrl                       : https://m365x04995906.sharepoint.com/sites/Remoteliving/Shared%20Documents/General/Subfolder/00003.jpg
    cTag                         : "c:{2C3C2E98-5BE4-4CE2-8A81-2C0D4BAB00B4},2"
    size                         : 232877
    createdBy                    : @{application=; user=}
    lastModifiedBy               : @{application=; user=}
    parentReference              : @{driveType=documentLibrary; driveId=b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4; id=01NLC4VWOFX4E7TTHLCNB256DE3BDS7OML; name=Subfolder; path=/drives/b!kJV0CniHeUeAiju7m8Gl4ZmfCOoRAXJNrYB9wjbkfZ-Vmuw3EELGQ7bZlNIfSaf4/root:/General/Subfolder;
                                   siteId=0a749590-8778-4779-808a-3bbb9bc1a5e1}
    file                         : @{mimeType=image/jpeg; hashes=}
    fileSystemInfo               : @{createdDateTime=16.10.2023 08:14:59; lastModifiedDateTime=16.10.2023 08:14:59}
    image                        : 
    shared                       : @{scope=users}

    Further Reference

    You might be also interested in following articles, which are related to MS Graph API:

    Security of app registration in Azure Active Directory | SPO Scripts

    Create SharePoint list items using Graph API (PowerShell) (workplace-automation.com/)

    How to get SharePoint List Items with Graph API (PowerShell) | SPO Scripts

  • Upload documents to SharePoint (PNP.PowerShell)

    Upload documents to SharePoint (PNP.PowerShell)

    Since SharePoint is not the only system you are using in your company, it might help to know how to upload documents to SharePoint from different systems programmatically. In this article, I want to show you what you can upload all types of files to SharePoint and what to consider, when you want to upload documents to SharePoint with PowerShell.

    Prerequisites

    In order to upload documents to SharePoint programmatically, you have to ensure, that these prerequisites are met.

    Before you run the scripts

    In the following script, I have used a credential file, which you also should generate. Why? Because the purpose of this script, is to run in the background, without a credential prompt, so you can use it in a scheduled task. Don’t forget to change the parameters to your corresponding scenario.

    Explanation of credential handling

    PowerShell Scripts

    I have provided a script to upload a single file and one to upload multiple files to the library.

    Upload single document to SharePoint with PowerShell

    With this script you can upload a single document to SharePoint with a PowerShell script. The script is ready to be used for a scheduled task, but before you plan it, run it with your service account.

    Before:

    SharePoint library before upload documents to SharePoint proceeded

    Param(
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales/SubSite",
        $LibraryName = "Documents",
        $CredentialPath = "C:\Users\Serkar\Desktop\devmod.key",
        $FilePath = "C:\Users\Serkar\Desktop\Projectscope.xlsx"
    )
    
    Function Export-CredentialFile 
    {
        param(
        $Username,
        $Path
        )
        While ($Username -eq "" -or $null -eq $Username)
        {
            $Username = Read-Host "Please enter your username (john.doe@domain.de)"
        }
        
        While ($Path -eq ""-or $null -eq $Path)
        {
            $Path = Read-Host "Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
        }
        else
        {
            Import-Clixml -Path $Path
        }
    }
    
    
    $Credential = Import-CredentialFile -Path $CredentialPath 
    
    If ($Credential -eq $null)
    {
        $Username = Read-Host "Please enter your username (john.doe@domain.de)"
        Export-CredentialFile -Path $CredentialPath -Username $Username
        $Credential = Import-CredentialFile $CredentialPath
    }
    
    #Connect to site
    Connect-PnPOnline -Url $SiteUrl -Credentials $Credential
    
    #upload documents to SharePoint
    Add-PnPFile -Path $FilePath -Folder $LibraryName

    After:

    SharePoint library after upload documents to SharePoint proceeded

    Upload multiple documents to SharePoint with PowerShell

    In order to upload multiple documents to SharePoint, I recommend putting the files in a dedicated folder, so the script can iterate through all documents in the folder.

    Screenshot of a folder, where all documents are located in

    Before:

    SharePoint library before upload documents to SharePoint proceeded
    Param(
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales/SubSite",
        $LibraryName = "Shared Documents",
        $CredentialPath = "C:\Users\Serkar\Desktop\devmod.key",
        $FolderPath = "C:\Users\Serkar\Desktop\Projects"
    )
    
    Function Export-CredentialFile 
    {
        param(
        $Username,
        $Path
        )
        While ($Username -eq "" -or $null -eq $Username)
        {
            $Username = Read-Host "Please enter your username (john.doe@domain.de)"
        }
        
        While ($Path -eq ""-or $null -eq $Path)
        {
            $Path = Read-Host "Where should the credentials be exported to?"
        }
        $ParentPath = Split-Path $Path
        If ((Test-Path $ParentPath) -eq $false)
        {
            New-Item -ItemType Directory -Path $ParentPath
        }
        $Credential = Get-Credential($Username)
        $Credential | Export-Clixml -Path $Path
        Return $Credential
    }
    Function Import-CredentialFile ($Path)
    {
        if (! (Test-Path $Path))
        {
            Write-Host "Could not find the credential object at $Path. Please export your credentials first"
        }
        else
        {
            Import-Clixml -Path $Path
        }
    }
    
    
    $Credential = Import-CredentialFile -Path $CredentialPath 
    
    If ($Credential -eq $null)
    {
        $Username = Read-Host "Please enter your username (john.doe@domain.de)"
        Export-CredentialFile -Path $CredentialPath -Username $Username
        $Credential = Import-CredentialFile $CredentialPath
    }
    
    #Connect to site
    Connect-PnPOnline -Url $SiteUrl -Credentials $Credential
    
    
    #upload documents to SharePoint
    Get-ChildItem -Path $FolderPath |ForEach-Object {
        Add-PnPFile -Path $_.FullName -Folder $LibraryName
    
    }

    After:

    SharePoint library after upload documents to SharePoint proceeded

    Troubleshooting

    Access denied

    Access denied error message
    Add-PnPFile -Path $FilePath -Folder $LibraryName
    Add-PnPFile : Access denied.
    At line:1 char:1
    + Add-PnPFile -Path $FilePath -Folder $LibraryName
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : WriteError: (:) [Add-PnPFile], ServerUnauthorizedAccessException
        + FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.Files.AddFile

    If you experience this issue, custom scripts are not allowed on this site. You have to enable it by connecting to the tenant with a SharePoint Administrator and enabling custom scripts:

    $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales"
    $TenantUrl = "https://devmodernworkplace-admin.sharepoint.com/"
    
    Connect-PnPOnline $TenantUrl -interactive
    Set-PnPTenantSite $SiteUrl -DenyAddAndCustomizePages:$false
    Disconnect-PnPOnline

    If you want to read more about custom site scripts, check out this article: Allow or prevent custom script – SharePoint in Microsoft 365 | Microsoft Docs

    Further Readin

    If you want to upload files with Graph, check out the recent blog post:

    How to Upload Files to SharePoint using Graph API PowerShell (workplace-automation.com/)

  • SharePoint Downloader | Comfortable file download in 2 steps

    Downloading SharePoint files might be necessary in areas, where we have lack of good network bandwith, if you want to keep working e.g. when you travel by train or plane. Downloading sharepoint files can be time consuming if you have to do it one by one. In this article, you will learn, how you can do it easily in the GUI, but also how you can download your SharePoint files with a so called SharePoint Downloader – a PowerShell script. If you are only interested in the SharePoint downloader, click here.

    Download SharePoint files in the GUI

    Before we start doing this with PowerShell, I want to show you how you download the file in the GUI. GUI stands for graphical user interface. I will show you how to download the file Opportunity2 from the SharePoint Library Opportunitylibrary. This is absolutely sufficient in our daily work routine.

    In the first step we open a SharePoint library:

    Screenshot of a SharePoint Library, where we want to download a SharePoint file

    Here we can mark a file and then download – looks easy right?

    Screenshot of a marked file to download the file

    As you can see the file is downloaded and we can keep working offline – but hey don’t forget to upload it later to SharePoint again :).

    Downloaded SharePoint file

    Download SharePoint files with PowerShell

    Scenario

    If you need to download multiple SharePoint files – this makes sense, if you need the files in another system and you need to export the files periodically, you can do it with PNP.PowerShell. In the following scenario, I will show you how to download multiple files by the example of the Library “Opportunitylibrary”, which contains two documents.

    Opportunity library with two documents I want to download

    Authentication and parameters

    In the beginning, we start with connecting to the site. If you are a beginner, you might be interested in a detailed description, which you can find here: Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    I have exported the credential object previously to reuse it, which you also can do. This is strongly recommended if the script shall ran in the background e.g. when you want to make use it in task scheduler. Do not expose the credentials in blank text – this is very bad practise and is a security problem. If you want to make use of saved credentials like I did, check the article: Use credentials in PowerShell | SPO Scripts

    Before you run the scripts, change the parameters to your siteurl, your path, your list and your credential file. The parameters I have provided won’t work, if you don’t change them.

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Invoices",
        $CredPath = "C:\Users\Serkar\Desktop\devmod.key"
    )
    
    $Credential = Import-Clixml -Path  $CredPath
    Connect-PnPOnline -Url $SiteUrl -Credential $Credential

    For interactive PowerShell sessions, you can also choose this authentication mechanism. Interactive means, that a person is running the script and can interact with the PowerShell terminal. The opposite would be if you would schedule the task in the task scheduler and the task would run – let’s say once a day. For this purpose the script would ask the terminal, but since there is nobody responsing the authentication request, the script would be halted and no file would be downloaded.

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Invoices"
    )
    
    
    Connect-PnPOnline -Url $SiteUrl -Interactive

    Download all SharePoint files from a library with PowerShell

    If you want to download all files of a library, you have to run following script. The script iterates through all files in your SharePoint library defined in the parameter block and it will download all the files to the downloadpath you have provided in the parameter block.

    Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile 
            }

    As you can see, I could download SharePoint files with PowerShell.

    The result of download SharePoint files

    Download specific SharePoint files from a library with PowerShell

    If you want to download only specific files, you can use following script. This script is using the out-gridview cmdlet to give you the possibility to choose only specific files.

    Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile 
                }

    A grid will pop up, where you can select the documents, which you want to download. I have marked the second file and clicked on OK. If we check the folder, we can see, that the file have been downloaded!

    Screenshot of the grid view for the SharePoint files, which you can download

    Et voilà – the file, which I have marked is downloaded!

    SharePoint Downloader

    In this section I provide you my ready to run scripts. I call them SharePoint Downloader – a small tool with big impact 🙂

    SharePoint Downloader for automation: Ready-to-run script for automation

    With this script you can download SharePoint files to your specified folder. It’s purpose is for automation, means that it can run in the background with azure automation triggered by a runhook or with task scheduler triggered by a time schedule. Please change the parameters of the SharePoint Downloader and ensure, that the credential object is exported to the CredPath before scheduling the script. If you forgot how to do it, check out this article: Use credentials in PowerShell | SPO Scripts

    param
    (
        $SiteUrl = "https://devmodernworkplace.sharepoint.com/sites/Sales",
        $DownloadPath = "C:\temp",
        $ListName = "Opportunitylibrary",
        $CredPath = "C:\Users\Serkar\Desktop\devmod.key",
        $Force = $true
    )
    
    $Credential = Import-Clixml -Path  $CredPath
    Connect-PnPOnline -Url $SiteUrl -Credential $Credential
    
    #create folder if it does not exist
    If (!(Test-Path $DownloadPath))
    {
        New-Item -ItemType Directory $DownloadPath
    }
    
    If ($Force)
    {
        Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile -Force
            }
    }
    Else
    {
        Get-PnPListItem -List $ListName  | 
            ForEach-Object { 
                Get-PnPFile -Url $_.FieldValues.FileRef -Filename $_.FieldValues.FileLeafRef  -Path $DownloadPath -AsFile 
            }
    }
    

    SharePoint Downloader for interactive: Ready-to-run script for interactive sessions

    This script can be used for interactive sessions, so you need personally to start the script and not the task scheduler or azure automation. You will be asked for each parameter, when starting the script.

    param
    (
        [Parameter(
        Mandatory=$true,
        ValueFromPipelineByPropertyName=$true,
        Position=0, 
        HelpMessage="Please provide the url in this format: https://tenant.sharepoint.com/sites/sitename")
        ]
        $SiteUrl,
        [Parameter(Mandatory=$true,
        ValueFromPipelineByPropertyName=$true,
        Position=1,
        HelpMessage="Please provide path, where the downloaded files should be located at.")
        ]
        $DownloadPath,
        [switch][Parameter(Mandatory=$false,
        ValueFromPipelineByPropertyName=$true,
        Position=2,
        HelpMessage="Use -Force paramater to overwrite existing files.")
        ]
        $Force = $true
    
    )
    
    Connect-PnPOnline -Url $SiteUrl -Interactive
    
    #create folder if it does not exist
    
    If (!(Test-Path $DownloadPath))
    {
        New-Item -ItemType Directory $DownloadPath
    }
    
    
    $ListName = Get-PnPList | Out-GridView -PassThru | Select-Object Title -ExpandProperty Title
    
    If ($Force)
    {
        Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile -Force -WarningAction Stop
                    Write-Host "Downloaded $($_.FileName) Green to $DownloadPath" -ForegroundColor Green 
                }
    }
    Else
    {
        Get-PnPListItem -List $ListName | 
        Select-Object Id,@{N="FileName";E={$_.FieldValues.FileLeafRef}}, @{N="Link";E={$_.FieldValues.FileRef}} |
            Out-GridView -PassThru |
                ForEach-Object { 
                    Get-PnPFile -Url $_.Link -Filename $_.FileName  -Path $DownloadPath -AsFile -WarningAction Stop
                    Write-Host "Downloaded $($_.FileName) Green to $DownloadPath" -ForegroundColor Green 
                }
    }

    Conclusio

    With PNP.PowerShell we can download SharePoint files easily. In this article I have described, how you can download single and multiple SharePoint files with the GUI and with PowerShell. I have provided you the SharePoint Downloader – a script, which downloads all files in SharePoint library interactively or automated.

    References

    Here is the official reference from microsoft about the main cmdlet get-pnpfile: Get-PnPFile (PnP.Powershell) | Microsoft Docs

    Geschäft Foto erstellt von yanalya – de.freepik.com

  • How to restore SharePoint files with PowerShell

    Shit happens — sometimes we are not that concentrated when we are working. Somebody calls us without an appointment, and we were cleaning up our document libraries and by mistake we delete multiple files from SharePoint libraries. In this article, you will learn, how to restore SharePoint files from the diverse recycle bins in SharePoint. Like every time you will learn it for two ways: manually and automated. These instructions do also work for SharePoint items.

    Restore SharePoint files manually from the first-stage recycle bin

    Let’s assume, that you have a library with two documents in it.

    Screenshot of the document library Opportunitylibrary

    One unfocused moment, and you have deleted the file Opportunity2.docx without intention.

    Screenshot of SharePoint library, where the document have been removed by accident

    As a site member, you have in fact the possibility to check your recycle bin to restore the file. In order to do this, click on the settings gear in the upper right corner and click on site contents.

    SharePoint settings gear
    Settings contents setting

    Now click on Recycle Bin.

    Screenshot of link to the recycle bin

    As you can see, I can see the removed file from John dodo, which is my test user with member access. Now mark the file, you want to restore and click on Restore.

    Screenshot of restoring the file from the SharePoint recycle bin

    Thats it, your file is back at the place, where you have deleted it:

    Screenshot of a SharePoint library, where the opportunity2.docx could be restored succesfully

    If you cannot see the file here, ask your Site collection administrator, to check the second-stage recycle bin (site collection recycle bin), which’s content will be retained for 93 days, unless the limit of the site collection storage is not exceeded or the item have not been removed manually by a site collection administrator. In the following, I will describe how to restore from the second-stage recycle bin.

    Restore SharePoint files manually from the second-stage recycle bin

    Now we assume, that we have removed the file opportunity2.docx accidentially, but we need to restore it. We already have checked the site recycle bin, but we could not find the file in it.

    As you see, there is no opportunity2.docx file:

    Screenshot of a SharePoint site recycle bin, where the file opportunit2.docx could not be found.

    In this case, we need someone, who has the site collection administrator role, because we have to check the site collection settings. In the view as a member, the site collection settings look like this — so there is no option to visit the site collection recycle bin and therefore no option to restore SharePoint files.

    Screenshot of the site settings from a user, which has no site owner permission

    In order to show you the site settings as an owner, I have promoted John Dodo to Site Owner:

    Screenshot of group membership permissions

    As you can see, I cannot see more as a site owner:

    Screenshot of site settings as a site owner

    Therefore, I will switch the user to one with site collection admin rights and we will see way more settings:

    Screenshot of site settings as a site collection admin

    Maybe you have noticed, that you can see Recycle Bin setting below Site collection Administration

    Screenshot of the recycle bin setting in the site settings

    This is the place, where we can see the files, which are removed, but also can be restored from users without the site collection administrator permission. As you can see, there is a hint to the second-stage recycle bin. Click on the hint.

    Screenshot of first stage recycle bin, with the hint to the second stage recycle bin

    This is the place, where we can see the files, which are in the second-stage recycle bin. We should see the opportunity2.docx file here:

    Screenshot of the second stage recycle bin

    Restore SharePoint files automatically from the second-stage recycle bin

    Prerequisites

    The prerequisite is, that you have to use a user, which has site collection administrator permissions.

    Screenshot of site collection administrators

    Description

    Previously we saw, that you can restore files easily by hand, but imagine, that you have removed more than 100 files, this might be a bit clicky time-consuming. So let’s check what we can do with PNP PowerShell.

    I am using John Dodo, who is currently a site collection administrator. John Dodo has removed more than 100 files by accident and I want to restore SharePoint files removed by John Dodo. This is currently not possible in GUI.

    Screenshot of removed items in the recycle bin

    In order to restore SharePoint files, I am using the PowerShell Module PNP.PowerShell.

    I am connecting to the sales site. If you feel unsafe with connecting, check out the post Connect to SharePoint with PowerShell | SharePoint Online (workplace-automation.com/)

    Connect-PnPOnline https://devmodernworkplace.sharepoint.com/sites/Sales/ -Interactive

    After connecting, I can see the items in the recycle bin. I have removed more than 100 files. You can do it with the cmdlet:

    Get-PNPRecycleBinItem
    Screenshot of the removed files in PowerShell

    When you pass the ID, you can also get seperate recycle bin items:

    
    Get-PnPRecycleBinItem -Identity "eaf270e4-08f0-41b3-a424-d804c6f33a16"

    If you pipe the Item, you can directly restore it:

    Get-PnPRecycleBinItem  -Identity "eaf270e4-08f0-41b3-a424-d804c6f33a16" | Restore-PnPRecycleBinItem -Force
    
    Screenshot of a specific item in the recycle bin in PowerShell

    I want to restore only the files, from John Dodo, so I am doing the restore with the out grid view cmdlet:

    Get-PnPRecycleBinItem  |
        Select-Object Title, ID, AuthorEmail, DeletedbyEmail, DeletedDate, DirName | 
            Out-GridView -PassThru |
                ForEach-Object { Restore-PnPRecycleBinItem -Identity $_.Id.Guid -Force}

    Now you can see the grid, where you can choose John Dodo as the deletor:

    Screenshot of the out gridview output, which shows all the items in the recycle bin

    I add a filter criteria:

    Screenshot of the out gridview output, which shows the SharePoint files, with filtering options

    Now I can add John Dodos mailaddress and with CTRL + A you can mark all entries and confirm with ok

    Screenshot of the out gridview output, which shows the SharePoint files, before the restore filtered on John.Dodo

    After doing this, the files, which have been by John Dodo are restored!

    Screenshot of the recycle bin after the restore of SharePoint files

    Troubleshooting

    If you experience this issue, your permission is not sufficient for the site:

    Get-PnPRecycleBinItem : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
    At line:1 char:1
    + Get-PnPRecycleBinItem
    + ~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : WriteError: (:) [Get-PnPRecycleBinItem], ServerUnauthorizedAccessException
        + FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.RecycleBin.GetRecycleBinItems

    If you encounter this issue, a file with the name already exists at the location, where it was. Rename the file in the library or remove it, to restore the file.

    Restore-PnPRecycleBinItem : A file with this name "Document.docx" already exists in "sites/Sales/Invoices". To restore the file, rename the existing file and try again.
    At line:17 char:30
    + ... rEach-Object { Restore-PnPRecycleBinItem -Identity $_.Id.Guid -Force}
    +                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : WriteError: (:) [Restore-PnPRecycleBinItem], ServerException
        + FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.RecycleBin.RestoreRecycleBinItem

    If this error occurs, it is because you have stressed the API.

    Get-PnPRecycleBinItem : The request uses too many resources.
    At line:1 char:1
    + Get-PnPRecycleBinItem | Select-Object Title, ID, AuthorEmail, Deleted ...
    + ~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : WriteError: (:) [Get-PnPRecycleBinItem], ServerException
        + FullyQualifiedErrorId : EXCEPTION,PnP.PowerShell.Commands.RecycleBin.GetRecycleBinItems

    Retry your cmdlet and if it is still problematic, add -rowlimit 5000, so you will see the first 5000 items.

    If you add rowlimit to your code, it will look like this:

    Get-PnPRecycleBinItem -RowLimit 5000 |
        Select-Object Title, ID, AuthorEmail, DeletedbyEmail, DeletedDate, DirName | 
            Out-GridView -PassThru |
                ForEach-Object { Restore-PnPRecycleBinItem -Identity $_.Id.Guid -Force}

    BONUS: Ready-to-run PowerShell script

    param
    (
        [Parameter(Mandatory=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
         $SiteUrl
    )
    
    
    Connect-PnPOnline -Url $SiteUrl -Interactive -ErrorAction Stop
    
    
    Get-PnPRecycleBinItem  |
        Select-Object Title, ID, AuthorEmail, DeletedbyEmail, DeletedDate, DirName | 
            Out-GridView -PassThru |
                ForEach-Object { Restore-PnPRecycleBinItem -Identity $_.Id.Guid -Force}

    Further Reading

    Out-Gridview gives us many possibilities to filter objects interactively, check out the blog post to learn more: How to filter for PowerShell objects easily | SPO Scripts

    Here you can find the official reference of Microsoft regarding the recycle bin: Manage the Recycle bin of a SharePoint site (microsoft.com)

    References

    Cover picture:

    Haus Foto erstellt von kues1 – de.freepik.com