SharePoint Online: Find All Unused Sites based on Last Modified Date

Requirement: Get All Unused SharePoint Online Sites based on the last modified date.

How to Find the Last Modified Date of SharePoint Online Site Collections?

If you’re like most SharePoint administrators, you have a number of sites in your SharePoint Online tenant that are no longer being used, and you may want to clean up your site collections to optimize performance and keep your SharePoint environment tidy. Deleting unused sites can be a manual process, but there is an easy way to find them. By using the last modified date as criteria, you can easily find those sites that have not been used in some time. This blog post will show you how to use PowerShell to find unused SharePoint Online sites.

The modern SharePoint Online Admin center gives a neat view of all sites in the tenant, along with the “Last Activity (UTC)” column that provides a date with value when List Items or Files viewed/edited/shared in the site.

powershell sharepoint online get modified date

PowerShell to Find Unused Sites in SharePoint Online

How about getting the modified date of any SharePoint Online site using PowerShell? This is useful if you are auditing your sites to make sure your sites are up-to-date before taking any further actions such as archival or deletion. Using PowerShell, let’s find the Last Modified Date value of a given site.

#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"

#Variables 
$SiteURL ="https://crescent.sharepoint.com/sites/marketing"

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 Web
    $Web = $Ctx.web
    $Ctx.Load($Web)
    $Ctx.ExecuteQuery()
 
    #Get the last modified date of the web
    Write-host "Last Modified:"$Web.LastItemUserModifiedDate
}
Catch {
    write-host -f Red "Error:" $_.Exception.Message
}
LastItemModifiedDate vs LastItemUserModifiedDate: There is a difference between the LastItemModifiedDate and LastItemUserModifiedDate. The LastItemUserModifiedDate property is a timestamp that reflects changes made by user accounts. “LastItemModifiedDate” property is the timestamp of changes made by the system.

Let’s modify this script a bit to retrieve the last modified date value of all subsites of the given site collection.

#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"

#Variables 
$SiteURL ="https://crescent.sharepoint.com/sites/marketing"

#Get Credentials to connect
$Cred= Get-Credential

Function Get-SPOSiteLastModified($SiteURL)
{
    Try { 
        #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 site
        $Web = $Ctx.web
        $Ctx.Load($Web)
        $Ctx.ExecuteQuery()
 
        #Get the last modified date of the site
        Write-host "Last Modified Date of $($SiteURL):"$Web.LastItemUserModifiedDate

        #Get Subsites of the site
        $Webs = $Web.Webs
        $Ctx.Load($Webs)
        $Ctx.ExecuteQuery()
        ForEach($SubWeb in $Webs)
        {
            #Call the function to get subsite's last modified date                
            Get-SPOSiteLastModified $SubWeb.URL
        }
    }
    Catch {
        write-host -f Red "Error:" $_.Exception.Message
    }
}

#Call the function to get last modified date of a site and its subsites
Get-SPOSiteLastModified $SiteURL

Get Inactive SharePoint Online Sites using  PowerShell

How about finding inactive SharePoint site collections in the tenant?

#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"

#Variables 
$AdminSiteURL ="https://crescent-admin.sharepoint.com"
$CSVFile = "C:\Temp\SiteData.csv"

#Get Credentials to connect
$Cred= Get-Credential

#Connect to Admin Center
Connect-SPOService -Url $AdminSiteURL -Credential $Cred

#Get All Site Collections
$Sites = Get-SPOSite -Limit All 
$SiteData = @()

#Loop through each site collection
ForEach($Site in $Sites)
{
    Write-host -f Yellow "Getting Data for Site:" $Site.URL
    Try { 
        #Setup the context
        $Ctx = New-Object Microsoft.SharePoint.Client.ClientContext($Site.URL)
        $Ctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($Cred.Username, $Cred.Password)
         
        #Get the Root Web
        $Web = $Ctx.web
        $Ctx.Load($Web)
        $Ctx.ExecuteQuery()
 
        #Get the last modified date of the site
        $SiteData += New-Object PSObject -Property @{
            SiteTitle = $Web.Title
            URL = $Web.Url
            LastModified= $Web.LastItemUserModifiedDate
        }
    }
    Catch {
        write-host -f Red "Error:" $_.Exception.Message
    }
}
#Export the results to CSV
$SiteData | Export-CSV $CSVFile -NoTypeInformation

Here is the SharePoint Online inactive sites report:

sharepoint online find inactive sites using powershell

PnP PowerShell to Get Unused Site Collections in the past 30 days

We’ve “LastContentModifiedDate” property available at site collection level. Let’s use PnP PowerShell to get the modified date in SharePoint Online.

$TenantAdminURL = "https://Crescent-Admin.SharePoint.com"
$TenantURL = "https://Crescent.SharePoint.com"
$DaysInActive = 30 # number of days to check since last modified
$CSVPath = "C:\Temp\InactiveSites.csv"

#Connect to Admin Center
Connect-PnPOnline -Url $TenantAdminURL -Credentials $Cred
    
#Get All Site collections - Exclude BOT, Video Portals and MySites
$SiteCollections = Get-PnPTenantSite -Filter "Url -like $TenantURL -and Url -notlike '-my.sharepoint.com/' -and Url -notlike '/portals/'"

#Calculate the Date
$Date = (Get-Date).AddDays(-$DaysInActive).ToString("MM/dd/yyyy")

#Get All Site collections where the content modified
$InActiveSites = $SiteCollections | Where {$_.LastContentModifiedDate -le $Date} | Select Title, Url, LastContentModifiedDate, Template, StorageMaximumLevel

#Export to CSV
$InActiveSites
$InActiveSites | Export-Csv -Path $CSVPath -NoTypeInformation

Once you have identified unused SharePoint Online sites, You can delete them! Here is my post on: How to Delete a Site Collection in SharePoint Online?

Salaudeen Rajack

Salaudeen Rajack is a SharePoint Architect with Two decades of SharePoint Experience. He loves sharing his knowledge and experiences with the SharePoint community, through his real-world articles!

9 thoughts on “SharePoint Online: Find All Unused Sites based on Last Modified Date

  • What about us poor souls that are stuck with on-prem? Do you have similar scripts for on-prem SharePoint?

    Reply
  • How could use it with MFA? It getting an errors

    Reply
    • For MFA, Instead of “-Credentials” parameter, use “-Interactive”.

      Connect-PnPOnline -Url $TenantAdminURL -Credentials $Cred

      Use:
      Connect-PnPOnline -Url $TenantAdminURL -Interactive

      You’ll get a popup to enter your credentials which is MFA aware.

      Reply
  • If you are not an Admin Site in each site, but you are a Global Admin, the script get an error

    Reply
  • Amazing, thank you for that!

    Just for your info, in the PnP script, the var $TenantURL is empty. You’re getting an error back: “Get-PnPTenantSite : Value for property Url does not contain a valid value.”

    Reply

Leave a Reply