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. 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 have the “LastContentModifiedDate” property available at the 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

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

In conclusion, finding all unused sites based on the last modified date in SharePoint Online can help to keep your SharePoint environment organized and running efficiently. By using the SharePoint Admin Center and PowerShell, you can retrieve a list of all sites that have not been modified in a certain period of time. Once you have this information, you can decide whether to delete or archive the sites that are no longer in use.

Salaudeen Rajack

Salaudeen Rajack - SharePoint Expert with Two decades of SharePoint Experience. Love to Share my knowledge and experience with the SharePoint community, through real-time articles!

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

  • I have a query, i need to get the list of site not used in last 30 days along with the users in the site collection with Full Control. Can you please help? Its an urgent requirement.

    Motive: Get list of sites not used in last 30 days and send email to the users in the Full Control permission group and delete the site if no response received from user.

    Reply
  • can i use CSOM and Connect-SPO simultaneously ? I am getting below error :-
    “Connect-SPOService : The ‘Connect-SPOService’ command was found in the module
    ‘Microsoft.Online.SharePoint.PowerShell’, but the module could not be loaded. For more information, run ‘Import-Module
    Microsoft.Online.SharePoint.PowerShell’.
    At line:1 char:1
    + Connect-SPOService https://adobe-admin.sharepoint.com
    + ~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (Connect-SPOService:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CouldNotAutoloadMatchingModule

    Reply
  • Hi, How do i get the Template Title(BlankSite) in the output instead of Template type(STS#1)

    Reply
  • Excellent¡, thanks for sharing, we have many sites for read only, is there a way to get the last access information?

    Reply
    • The “Last Activity” in the Admin center refers to any file activity or Page views! So that should help here.

      Reply
    • I noticed even after uploading a document in one of the site ‘s document library, last modified on admin centre doesn’t change. pnp powrshell, Admin centre UI, and graph api all methods give different values. Not sure which is realistic.

      Reply
  • For some organizations I recommend considering a different way. Because this method purely looks at last modified date and not the last activity on the site. Some organizations might use SharePoint sites as a pure information source and update the information once or twice a year.

    Reply
  • 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

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