Find and remove missing features, the PowerShell way

How to find and remove missing features without querying the content database or 3rd party tools. It's only PowerShell and I like it (sorry for this Mr. Jagger)

I do SharePoint updates and migrations since a long time and one of the most annoying error I often get is the "Missing feature". Searching the web you will find a lot of posts that show how to find the missing feature details by querying the content database. Most of them warn about the fact that querying SharePoint database is non supported by Microsoft (ref. https://support.microsoft.com/en-us/help/841057/support-for-changes-to-the-databases-that-are-used-by-office-server-pr). You could create a database snapshot and query against it or you could use 3rd party tools, but if none of these fit your needs you could try the PowerShell way.

Recently I was working for a client that runs a SQL Server edition that do not allows database snapshots and for internal policy no 3rd party tool can be installed on the production server. I created the following PowerShell script that looks for and (optionally) remove the missing feature when it finds it.

First of all you should run a test over you content dbs with Test-SPContentDatabase (or check the Health Analyzer rule).

If you get error like above you can use the following script. You have to save the script and run it by passing two parameters, the database name and the feature id. You can find both on the error message. The script will go through all site collections and webs in the content database and looks for the feature id. If you specified the Remove switch it will try to remove the feature.

Param([Parameter(Mandatory=$true)] 
      [String]$database,
      [Parameter(Mandatory=$true)] 
      [String]$id,
      [Parameter(ParameterSetName='Remove')]
      [switch]$remove
)

if ((gsnp *SharePoint* -ErrorAction SilentlyContinue) -eq $null){
    asnp *SharePoint* -ErrorAction Stop
}

Get-SPSite -Limit All -ContentDatabase $database | %{
    if ($_.Features[$id]){
        Write-Host "Feature found in $($_.Url)" -ForegroundColor Yellow

        if ($remove){
            Remove-SiteFeature $_.Url $id;
        }
    };
    
    $_ | Get-SPWeb -limit All | %{
        if ($_.Features[$id]){
            Write-Host "Feature found in $($_.Url)" -ForegroundColor Yellow
        }

        if ($remove){
            Remove-WebFeature $_.Url $id;
        }
    }
}

function Remove-SiteFeature {
    param( [string]$spsite, [string]$featureid );

    try {
        $s = Get-SPite $spsite;
        $f = $s.Features[$featureid];
        $s.Features.Remove($f.DefinitionId,$true);
        Write-Host "Feature $featureid removed successfully from $spsite" -ForegroundColor Green
    } catch {
        Write-Host "An error occurred while removing feature $featureid on site $spsite. $($Error[0].Exception.Message)" -ForegroundColor Magenta
    }
}

function Remove-WebFeature {
    param( [string]$spweb, [string]$featureid );

    try {
        $w = Get-SPWeb $spweb;
        $f = $w.Features[$featureid];
        $w.Features.Remove($f.DefinitionId,$true);
        Write-Host "Feature $featureid removed successfully from $spweb" -ForegroundColor Green
    } catch {
        Write-Host "An error occurred while removing feature $featureid on site $spweb. $($Error[0].Exception.Message)" -ForegroundColor Magenta
    }
}

I don't know why you should trust me, but if you do, do it at your own risk :)

As soon I'll be back from my summer trip to Poland (if I'll ever come back) I will publish the script in TechNet Gallery.

Posted by Riccardo