SharePoint Online: Copy Attachments from List to Document Library using PowerShell

Requirement: Copy all attachments from a SharePoint Online List to Document Library.

sharepoint copy attachment from list to document library

How to Copy Attachments from SharePoint Online List?

Would you like to copy attachments from a list to a document library in SharePoint Online? If so, this handy guide will show you how! With just a few simple steps, you can easily move files between lists and libraries. You can use this method to copy multiple attachments, either from one list item or from all items of a list at once.

Here is the trick to copy all attachments from the SharePoint Online list: SharePoint stores list item attachments by creating sub-folders for each list item based on the item id under a folder called “Attachments”. 

  1. Append “Attachments” to your list URL. E.g. If your list URL is: https://tenant.sharepoint.com/lists/project, then the URL is: https://tenant.sharepoint.com/lists/project/attachments
  2. Map the URL you framed in the above step as a network drive. Go to Windows explorer >> Right-click on “This PC” (or “My Computer”) >> Choose “Map Network Drive” >> Enter the URL and finish that wizard. 
  3. This opens the list attachments in the File Explorer view, where you can see and copy attachments from the list.
    sharepoint copy list attachment to document library

You can also use SharePoint Designer to navigate to the “Attachments” folder and copy attachments for any list.

SharePoint Online: Copy Attachments to Document Library using PowerShell

Let’s copy all attachments from a list item to a document library:

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

#Set Parameters
$SiteURL= "https://crescent.sharepoint.com/sites/marketing"
$ListName= "Projects"
$ListItemID = 1
$DocumentLibraryURL = "/Sites/Marketing/Project Documents/" #Relative URL - Must end with /

#Setup Credentials to connect
$Cred = Get-Credential

#Setup the context
$Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

#Get the List Item
$List = $Ctx.Web.Lists.GetByTitle($ListName)
$ListItem = $List.GetItemByID($ListItemID)
$Attachments = $ListItem.AttachmentFiles
$Ctx.Load($Attachments)
$Ctx.ExecuteQuery()
    
#Get All attachments from the List Item
Write-host "Total Attachments Found:"$Attachments.count
ForEach($Attachment in $Attachments)
{
    #Get attachment from List Item
    $File = $Ctx.Web.GetFileByServerRelativeUrl($Attachment.ServerRelativeUrl)
    $Ctx.Load($file)
    $Ctx.ExecuteQuery()

    #Copy attachment to Document Library
    $File.CopyTo($DocumentLibraryURL+$File.Name, $True)
    $Ctx.ExecuteQuery()
    Write-host "Attachment Copied:"$File.name
}

This PowerShell script copies all attachments from the list item with item ID “1” to an existing document library, “Project Documents” on the same site.

PowerShell to Copy Attachments from All List Items in SharePoint Online

Let’s copy all attachments of the given list items to a document library.

#Load SharePoint CSOM Assemblies
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

Function Copy-ListAttachments()
{
    Param
    (
        [Parameter(Mandatory=$true)] [string] $SiteURL,
        [Parameter(Mandatory=$true)] [string] $ListName,
        [Parameter(Mandatory=$true)] [string] $DocumentLibraryName
    )
    
    Try {
        #Setup Credentials to connect
        $Cred = Get-Credential

        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
        $Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.UserName,$Cred.Password)

        #Get the Document Library 
        $DocumentLibrary = $Ctx.Web.Lists.GetByTitle($DocumentLibraryName)
        $Ctx.Load($DocumentLibrary.RootFolder)
        $Ctx.Load($DocumentLibrary.RootFolder.Folders)
        $Ctx.ExecuteQuery()

        #Get all List Items with attachments
        $Query = New-Object Microsoft.SharePoint.Client.CamlQuery
        $Query.ViewXml = "<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='Attachments' /><Value Type='Boolean'>1</Value></Eq></Where></Query></View>"
        $List = $Ctx.Web.Lists.GetByTitle($ListName)
        $ListItems = $List.GetItems($Query)
        $Ctx.Load($ListItems)
        $Ctx.ExecuteQuery()
        
        #Iterate through each list item
        ForEach($ListItem in $ListItems)
        {
            #Get All attachments from the List Item
            $Attachments = $ListItem.AttachmentFiles
            $Ctx.Load($Attachments)
            $Ctx.ExecuteQuery()

            #Create New Folder for each List Item
            $Folders = $DocumentLibrary.RootFolder.Folders | Select -ExpandProperty Name
            If($Folder -notcontains $ListItem.Id)
            {
                $Folder = $DocumentLibrary.RootFolder.Folders.Add($ListItem.Id)
                $Ctx.ExecuteQuery()
            }

            ForEach($Attachment in $Attachments)
            {
                #Get attachment from List Item
                $File = $Ctx.Web.GetFileByServerRelativeUrl($Attachment.ServerRelativeUrl)
                $Ctx.Load($file)
                $Ctx.ExecuteQuery()

                #Copy attachment to Document Library
                $TargetFileURL = $DocumentLibrary.RootFolder.ServerRelativeUrl+"/"+$ListItem.Id+"/"+$File.Name
                $File.CopyTo($TargetFileURL, $True)
                $Ctx.ExecuteQuery()
                Write-host "Attachment Copied:"$TargetFileURL
            }
        }
    }
    Catch {
        write-host -f Red "Error Copying List Attachments!" $_.Exception.Message
    } 
}

#Call the function to copy attachments
Copy-ListAttachments -SiteURL "https://crescent.sharepoint.com/sites/marketing" -ListName "Projects" -DocumentLibraryName "Project Documents"

PnP PowerShell to Copy List Item Attachments to Document Library

Here is the PnP PowerShell way to copy attachments from a list to a document library:

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/marketing"
$ListName = "Projects"
$ListItemID = "1"
$DocumentLibraryURL = "/Sites/Marketing/ProjectDocuments"

#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

#Get the List Item
$ListItem = Get-PnPListItem -List $ListName -Id $ListItemID

#Get All Attachments of the List Item
$Attachments = Get-PnPProperty -ClientObject $ListItem -Property "AttachmentFiles"

#Copy Attachment File to Document Library 
$Attachments | ForEach-Object { Copy-PnPFile -SourceUrl $_.ServerRelativeUrl -TargetUrl "$DocumentLibraryURL/$($_.FileName)" -OverwriteIfAlreadyExists:$True -Force}

Copy List Attachments to Document library using PnP PowerShell

How about copying all attachments to a documents library, folder-wise? Let’s create folders in the document library for each list item (based on the list item id) and attachments.

#Parameters
$SiteURL = "https://crescent.sharepoint.com/sites/marketing"
$ListName = "Projects"
$DocumentLibraryName = "Project Documents"

#Connect to PnP Online
Connect-PnPOnline -Url $SiteURL -Credentials (Get-Credential)

#Get All List Items with Attachments
$SPQuery = "<View Scope='RecursiveAll'><Query><Where><Eq><FieldRef Name='Attachments' /><Value Type='Boolean'>1</Value></Eq></Where></Query></View>"
$ListItems = Get-PnPListItem -List $ListName -Query $SPQuery

#Get the Web and Document Library
$Web = Get-PnPWeb
$DocumentLibrary = Get-PnPList $DocumentLibraryName -Includes RootFolder

ForEach($ListItem in $ListItems)
{
    #Get All Attachments of the List Item
    $Attachments = Get-PnPProperty -ClientObject $ListItem -Property "AttachmentFiles"

    #Create Folder for List Item Attachments in the document library
    $FolderURL = "$($DocumentLibrary.RootFolder.ServerRelativeUrl)/$($ListItem.ID)"
    $FolderSiteRelativeURL = $FolderURL.Replace($Web.ServerRelativeUrl,[string]::Empty)
    $Folder = Resolve-PnPFolder -SiteRelativePath $FolderSiteRelativeURL

    #Copy Attachment File to Document Library 
    $Attachments | ForEach-Object { Copy-PnPFile -SourceUrl $_.ServerRelativeUrl -TargetUrl "$FolderURL/$($_.FileName)" -OverwriteIfAlreadyExists:$True -Force}
}

Here is another post to copy attachments to another list: SharePoint Online: Copy Attachment from One List to Another

In conclusion, By using the methods outlined in this tutorial, you can easily and efficiently transfer attachments from a List to a Document Library to take advantage of the capabilities offered by SharePoint Online, such as version control, search and retrieval, and collaboration. By automating the process, you can save time and resources, and ensure that the attachments are properly stored and maintained.

Salaudeen Rajack

Salaudeen Rajack - Information Technology Expert with Two-decades of hands-on experience, specializing in SharePoint, PowerShell, Microsoft 365, and related products. He has held various positions including SharePoint Architect, Administrator, Developer and consultant, has helped many organizations to implement and optimize SharePoint solutions. Known for his deep technical expertise, He's passionate about sharing the knowledge and insights to help others, through the real-world articles!

2 thoughts on “SharePoint Online: Copy Attachments from List to Document Library using PowerShell

  • The code does not seem to work for the “PnP PowerShell to Copy List Item Attachments to Document Library” when it’s cross-site collection on same Tenant.
    Error message :
    “Unsupported list type for copying or moving. The copy or moving can only be handled in a document library.”

    Reply
  • Hello, this is a big help, thank you for putting it together.
    In the PnP script, is there an option to check if the document already exists in the Document Library I am copying it into, and only overwrite it if it’s changed?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *