SharePoint Closed WebParts Slowing You Down? Migrating from SharePoint 2007 to 2010 and Can't Find an Easy Way to Resolve (preupgradecheck) Findings? PowerShell's Got Your Back!

Performance is a concern of every system administrator, unless you're an Information Assurance kind of guy and then you'd probably just assume see the server switched off (just a bit of a nudge). If SharePoint is your poison, watch out for Closed WebParts throughout your site.

Closed WebParts are still attached to your page and are temporarily hidden, but they still load when the page loads. If there are a number of them, or any with Fatal Errors, you're unnecessarily impacting page load times and probably filling your logs with errors that are difficult to track down. Worse yet, if you plan a migration from SharePoint 2007 to 2010 and run the preupgradecheck STSADM command, you'll find yourself with a list of potential upgrade problems and no clear path to resolve them.

The handy PowerShell script below can help with both. It will iterate through all of the web applications on your server and the pages at the root of each site (it doesn't look at pages in libraries, which could also be an issue) and deletes closed WebParts as well as reporting WebParts that themselves are reporting a Fatal Error.

# Load assemblies
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") > $null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Portal") > $null

$farm = [Microsoft.SharePoint.Administration.SPFarm]::Local;
$services = @($farm.Services | where -FilterScript {$_.GetType() -eq [Microsoft.SharePoint.Administration.SPWebService]})

foreach ($service in $services) {
    foreach ($app in $service.WebApplications) {
        foreach ($site in $app.Sites) {
            if (!$site.ServerRelativeUrl.Contains("ssp/")) {
                foreach ($currentWeb in $site.AllWebs) {
                    $pages = $currentWeb.Files | Where-Object {$_.Name -match ".aspx"}
                    foreach ($currentPage in $pages) {
                        Write-Host "Analyzing Page $($currentPage.ServerRelativeUrl)."
                        
                        if ($currentPage.CheckedOutByUser -ne $null) {
                            $currentPage.CheckIn("Administratively Checked-In");
                        }
                        
                        $webPartManager = $currentWeb.GetLimitedWebPartManager($currentPage.ServerRelativeUrl, [System.Web.UI.WebControls.WebParts.PersonalizationScope]::Shared)
                        $closedWebparts = New-Object System.Collections.ArrayList
                        
                        foreach($webpart in $webPartManager.WebParts) {
                            if($webpart.IsClosed) {
                                $closedWebparts.add($webpart)
                            }
                        }
                        
                        foreach($webpart in $closedWebparts) {
                            Write-Host "Deleting closed webpart $($webpart.Title)"
                            $webPartManager.DeleteWebPart($webpart)
                        }
                    }
                    $currentWeb.Dispose()
                }
            }
            $site.Dispose()
        }
    }
}

Update: This PowerShell script is now maintained in my blog-code repository on GitHub. You can find the full script and documentation in the /PowerShell-SP-Cleanup directory.