SharePoint Online: Get all list attachments using Powershell and CSOM

Introduction

SharePoint lists can hold attachments which can grow considerably in size. This script will help you identify list attachments and estimate their size. It can be useful during re-org or migration.


Prerequisites

You need SharePoint Online SDK.


Steps

Step 1: Connect to SharePoint Online

Create ClientContext and add your credentials. ExecuteQuery() is not necessary here. I just like to use it early in order to test the connection:

$password = Read-Host "Password" -AsSecureString
$ctx=New-Object Microsoft.SharePoint.Client.ClientContext($Url)
$ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Username, $password)
$ctx.ExecuteQuery()

Step 2: Get all items

First you need to load your list:

$ll=$ctx.Web.Lists.GetByTitle($ListTitle)
$ctx.Load($ll)
$ctx.ExecuteQuery()

Then we can select only items with an attachment using CamlQuery:

$spqQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
$spqQuery.ViewXml = "<Where><Eq><FieldRef Name='Attachments' /><Value Type='Boolean'>1</Value></Eq></Where>";

Load all your items:

$listItems=$ll.GetItems($spqQuery)
$ctx.Load($listItems)
$ctx.ExecuteQuery()

Step 3: Get item attachments

This step needs to be done per every item, so we start with a loop:

for($j=0;$j -lt $listItems.Count ;$j++)
  {        
 
  }

Now, for every item, we need to load its attachments:

for($j=0;$j -lt $listItems.Count ;$j++)
  {        
      $itemAttachments=$listItems[$j].AttachmentFiles
      $ctx.Load($itemAttachments)
      $ctx.ExecuteQuery()
  }

Each item can have one or more attachments. So what we actually loaded before is a collection of attachment files. We need to loop through each of them 

for($j=0;$j -lt $listItems.Count ;$j++)
  {        
      $itemAttachments=$listItems[$j].AttachmentFiles
      $ctx.Load($itemAttachments)
      $ctx.ExecuteQuery()
 
      foreach($itemAttachment in $itemAttachments)
      {
         #do something
      }
  }

Step 4: Export to csv 

We can either add each of our attachments to a predefined array:

$array=@()
$array+=$itemAttachment

or, since we only want to export the list of them to a csv, we can directly do that:

Export-CSV -InputObject $itemAttachment  -Path $CSVPath  -Append

If you are interested in only specific properties of this attachment, you can create a custom object and define its properties:

$obj  = New-Object PSObject
$obj  | Add-Member NoteProperty ItemID($listItems[$j].ID)
$obj  | Add-Member NoteProperty ItemTitle($listItems[$j]["Title"])
$obj  | Add-Member NoteProperty AttachmentName($file.Name)
$obj  | Add-Member NoteProperty AttachmentVersions($file.Versions.Count)
$obj  | Add-Member NoteProperty "AttachmentSize in KB"($fileSize)
Export-CSV -InputObject $obj -Path  $CSVPath -Append

 


Sample results


Full script

  function Connect-SPOCSOM
{
    param (
        [Parameter(Mandatory=$true,Position=1)]
        [string]$Username,
        [Parameter(Mandatory=$true,Position=3)]
        [string]$Url
)
 
  $password = Read-Host "Password" -AsSecureString
  $ctx=New-Object Microsoft.SharePoint.Client.ClientContext($Url)
  $ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Username, $password)
  $ctx.ExecuteQuery()  
    $global:ctx=$ctx
}
 
 
function Get-SPOListItems
{
   param (
    [Parameter(Mandatory=$true,Position=1)]
        [string]$ListTitle,
    [Parameter(Mandatory=$false,Position=3)]
        [switch]$Recursive,
    [Parameter(Mandatory=$false,Position=4)]
        [string]$CSVPath
        )
   
   
  $ll=$ctx.Web.Lists.GetByTitle($ListTitle)
  $ctx.Load($ll)
  $ctx.Load($ll.Fields)
  $ctx.ExecuteQuery()
 
  $spqQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
  $spqQuery.ViewXml = "<Where><Eq><FieldRef Name='Attachments' /><Value Type='Boolean'>1</Value></Eq></Where>";
  
  if($Recursive)
  {
     $spqQuery.ViewXml +="<View Scope='RecursiveAll' />";
  }
 
  $listItems=$ll.GetItems($spqQuery)
  $ctx.Load($listItems)
  $ctx.ExecuteQuery()
 
  for($j=0;$j -lt $listItems.Count ;$j++)
    {        
        $itemAttachments=$listItems[$j].AttachmentFiles
        $ctx.Load($itemAttachments)
        $ctx.ExecuteQuery()
 
        foreach($itemAttachment in $itemAttachments)
        {
           #Write-Output $att
            $file = $ctx.Web.GetFileByServerRelativeUrl($itemAttachment.ServerRelativeUrl);
            $ctx.Load($file)
            $ctx.ExecuteQuery()
            $fileSize = [Math]::Round(($file.Length/1KB),2)
            $obj = New-Object PSObject
            $obj | Add-Member NoteProperty ItemID($listItems[$j].ID)
            $obj | Add-Member NoteProperty ItemTitle($listItems[$j]["Title"])
            $obj | Add-Member NoteProperty AttachmentName($file.Name)
            $obj | Add-Member NoteProperty AttachmentVersions($file.Versions.Count)
            $obj | Add-Member NoteProperty "AttachmentSize in KB"($fileSize)
            Export-CSV -InputObject $obj -Path  $CSVPath -Append
 
        }
    }
 
}
 
 
$global:ctx
 
 
# Paths to SDK. Please verify location on your computer.
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"
 
 
 
# Do not modify lines below 
Connect-SPOCSOM -Username $Username -Url $Url
Get-SPOListItems -ListTitle $ListTitle  -CSVPath $CSVPath  -Recursive

Downloads

The script is available for download on Github:
List all attachments from SharePoint Online list to CSV file
Copy all SharePoint Online list item attachments to a SPO library

All suggestions, code changes, and improvements are welcome at my GitHub account:  https://github.com/PowershellScripts