I was doing some pre-migration activities, removing Content Types and un-used lists from SharePoint Online. One of the sites had a lot of lists / libraries and subsites, and I couldn't track down where a particular content type was "in use". So I had a look around to see if I could build something to do this work for me.

In On Prem SharePoint the Server Object Model (SOM) had a SPContentTypeUsage class. Unfortunately, for SharePoint Online its not available in the Client Side Object Model (CSOM).

So I decided to build a script to do the following:

  1. Connect to a Site Collection
  2. Examine all the Lists and see if a particular Content Type was associated
  3. Recurse through all the sub webs.

while it sounds like the script would be doing a lot of work, it only took a few seconds to check around 20 webs, and in the output I found exactly where my CT was being used.
I built the script to work in my office365-devscripts on github library. The library has some baked in goodness that encrypts passwords, downloads CSOM from nuget, manages connections and avoids bloating the scripts with boiler plate code.

Here's the PowerShell script. It won't run as is, as it depends on some modules, but you can download the source from github, or run a `git clone https://github.com/TjWheeler/office365-devscripts'.

PowerShell - Get-ContentTypeUsages.ps1

#Script:	Get-ContentTypeUsages.ps1 https://github.com/TjWheeler/office365-devscripts
#Author:	Tim Wheeler (http://timwheeler.io)
#Version:	0.3
#Purpose:   Recurse the web structure and locate any usages of the content type
#notes:     
param(
    $env =  $(Read-Host "Specify environment name"),
    [ValidateSet("Dev","Test","UAT","Prod")]
    [String] $environmentType = $(Read-Host "Specify EnvironmentType Dev,Test,UAT,Prod"),
    [string] $name = $(Read-Host "Specify name")
)
$InformationPreference = "continue"
&("$PSScriptRoot\Start.ps1")
$scriptStartTime = Get-Date


function FindCTUsages([Microsoft.SharePoint.Client.Web] $web, $name, $contentTypeId)
{
    write-host "---- Looking for usages of content type $name in $($web.Url) ----"
    $lists = $web.Lists
    $context.Load($lists)
    Execute-WithRetry $context
    
    foreach($list in $lists)
    {
        $context.Load($list)
        $context.Load($list.ContentTypes)
        Execute-WithRetry $context
        foreach($ct in $list.ContentTypes)
        {
            if($ct.Id.StringValue.ToLower().StartsWith($contentTypeId.ToLower()))
            {
                Write-Host -ForegroundColor Green "Found usage of $name at: $($list.ParentWebUrl) - $($list.Title)"
            }
        }

    }
}
function Get-ContentType($name)
{
    write-host "---- Looking for content type $name ----"
    $items = $context.Site.RootWeb.ContentTypes
    $context.Load($items)
    Write-Information "Loading Content Types"
    Execute-WithRetry $context
    
    [Array] $filtered = $items | where-object { $_.Name -ieq $name}
    if($filtered.Count -eq 0) 
    {
        Write-Warning "$name not found"
    } 
    else {
        $ct = $filtered[0]
        $context.Load($ct)
        Execute-WithRetry $context
        return $ct
    }     
    return $null

}
function RecurseWebs([Microsoft.SharePoint.Client.Web] $web, $name, $contentTypeId)
{
    $context.Load($web)
    Execute-WithRetry $context
    FindCTUsages $web $name $contentTypeId
    foreach($subWeb in $webs)
    {
        RecurseWebs $subWeb $name $contentTypeId
    }
}

$context = Create-Context $env -environmentType $environmentType

try
{
    $ct = Get-ContentType $name
    if($ct -eq $null)
    {
        Write-Error "Could not find $name Content Type"
        return
    }
    Write-Information "Found Content Type $name with ID $($ct.Id.StringValue)   "
    Write-Information "Recursing Web Structure"
    RecurseWebs $context.Site.RootWeb $name $ct.Id.StringValue
  
}
finally
{
    Write-Information "Script started at $scriptStartTime"
    Write-Information "Script finished at $(Get-Date)"
    Write-Information "Time taken is $(([DateTime]::Now - $scriptStartTime).ToString())"
    if($context -ne $null)
    {
        $context.Dispose()
        $context = $null
    } 
}

Post Image by Gerd Altmann from Pixabay