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 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
}
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:
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.
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.
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
Hi, How do i get the Template Title(BlankSite) in the output instead of Template type(STS#1)
Excellent¡, thanks for sharing, we have many sites for read only, is there a way to get the last access information?
The “Last Activity” in the Admin center refers to any file activity or Page views! So that should help here.
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.
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.
What about us poor souls that are stuck with on-prem? Do you have similar scripts for on-prem SharePoint?
This should help: Find Active-Inactive Site Collections in SharePoint
Worked perfectly, thanks.
How could use it with MFA? It getting an errors
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.
If you are not an Admin Site in each site, but you are a Global Admin, the script get an error
That’s obvious! Doesn’t matter if you have Global Admin or SharePoint Online Admin rights, But you must add your account as “Site collection Admin” on all sites first. How to Add Site Collection Admin to All Sites in SharePoint Online?
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.”
Fixed it! Thanks.