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

Requirement: Get All Unused SharePoint Online Sites

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

The modern SharePoint Online Admin center gives a neat view of all sites in the tenant along with the “Last Activity (UTC)” column that gives a date 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

Let’s find the Last Modified Date value of a given site using PowerShell.

#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

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!

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

  • August 10, 2021 at 8:13 PM

    How could use it with MFA? It getting an errors

    Reply
    • September 9, 2021 at 11:18 AM

      For MFA, Instead of “-Credentials” parameter, use “-UseWebLogin”.

      Connect-PnPOnline -Url $TenantAdminURL -Credentials $Cred

      Use:
      Connect-PnPOnline -Url $TenantAdminURL -UseWebLogin

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

      Reply
  • August 10, 2021 at 7:26 PM

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

    Reply
  • December 8, 2020 at 9:08 PM

    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