2017-11-10 22:47:19

SharePoint Missing Feature

This is the message you'll see about missing features:
Category        : MissingFeature
Error           : True
UpgradeBlocking : False
Message         : Database [WSS_Content_OpsCentre] has reference(s) to a missing feature: Id = [a59ff911-7eb6-4671-abea-079e58218fde],
                  Name = [K2 Site Settings], Description = [Adds the K2 Site Settings link to the Site Actions menu, allowing for
                  configuration of K2 components in the site collection.], Install Location = [K2SiteSettings].
Remedy          : The feature with Id a59ff911-7eb6-4671-abea-079e58218fde is referenced in the database [WSS_Content_OpsCentre], but is
                  not installed on the current farm. The missing feature may cause upgrade to fail. Please install any solution which
                  contains the feature and restart upgrade if necessary.
This isn't anything new, it has been discussed here and also here. While Phil and Eitenne did a good job with their scripts (Phils was a big source for my own) they didn't exactly meet my expectations.
That mostly being being able to run a script without editing it and be able to clean up my mess. Now you might think that soudns rediculous cause everyones enviroemnt is different.
While I do agree there, that doesn't mean ones scipt can't ask for these variables instead of having to edit the script to define them.
Here's my script and a picture of what I mean.



If you think this is cool and exactly what you need to fix your SharePoint. Have at my code, I offer it as is, without warranty, and use at your own risk.
This is one of 4 scripts to be released on an epic Sharepoint adventure!
########################################################################################################################## 
# Author: Zewwy (Aemilianus Kehler)
# Date:   Oct 28, 2017
# Script: Remove-SPSiteFeature
# This script allows to remove Site Collection Features.
# Cudos to Phil Childs from get-spscripts.com
# Required parameters: 
#   A valid  SharePoint Site Collection URL and a string (case insensitive :D) for the particular SP Feature.
#   Best to be run from a SharePoint Mgmt Console with an account that has collection admin on the Web URL 
##########################################################################################################################

##########################################################################################################################
#   Variables
##########################################################################################################################
#The Varible used to display wrong URL provided, or not found
$BadURL = @("Sorry, the string you entered ","is not a valid Site Collection.")
#File not found from SQL query response
$BadfileQry = "Sorry mate but it seems the dingo ate your file."
#MyLogoArray
$MylogoArray = @("#####################################","# This script is brought to you by: #","#                                   #","#             Zewwy                 #","#                                   #","#####################################"," ")
#Static Variables
$ScriptName = "Remove-SPSiteFeature; cause some SharePoint features just suck ass.`n"
$SQLServer = "Server\instance"
$DB = "WSS_Content"

$pswheight = (get-host).UI.RawUI.MaxWindowSize.Height
$pswwidth = (get-host).UI.RawUI.MaxWindowSize.Width

##########################################################################################################################
#   Functions
##########################################################################################################################

#function takes in a name to alert confirmation of deletion of a web part, returns true or false
function confirm($name)
{
    #function variables, generally only the first two need changing
    $title = "Confirm SharePoint Feature Removal!"
    $message = "You are about to remove a SharePoint Feature: $name"

    $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "This means Yes"
    $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "This means No"

    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $result = $host.ui.PromptForChoice($title, $message, $Options, 0)
    Write-Host " "
    Switch ($result)
        {
              0 { Return $true }
              1 { Return $false }
        }
}

#Function to Centeralize Write-Host Output, Just take string variable parameter and pads it
#Nerd Level over 9000!!! Ad-hoc Polymorphic power time!!
function Centeralize()
{
  param(
  [Parameter(Position=0,Mandatory=$true)]
  [string]$S,
  [Parameter(Position=1,Mandatory=$false,ParameterSetName="color")]
  [string]$C
  )
    $sLength = $S.Length
    $padamt =  "{0:N0}" -f (($pswwidth-$sLength)/2)
    $PadNum = $padamt/1 + $sLength #the divide by one is a quick dirty trick to covert string to int
    $CS = $S.PadLeft($PadNum," ").PadRight($PadNum," ") #Pad that shit
    if ($C) #if variable for color exists run below
    {    
        Write-Host $CS -ForegroundColor $C #write that shit to host with color
    }
    else #need this to prevent output twice if color is provided
    {
        $CS #write that shit without color
    }
}

#Found Function, to be called whenever the feature is found... Sorry guys I kinda had bad code using the break, whoopsie.
function FoundItinWeb($ss, $SPF)
{
    $FName = "Feature Found: "+$SPF.Definition.DisplayName
    Centeralize $FName "Yellow"
    
    #[System.Collections.ArrayList]$ShitArray = "Parent Site: "+$SPFeature.Parent.Url,"Feature guid: "+$SPFeature.DefinitionId.Guid #,"Feature ID: "+$SPFeature.Definition.Id,"Feature Name: "+$SPFeature.Definition.DisplayName,"Compatibility Level: "+$SPFeature.Definition.CompatibilityLevel,"Physical File: "+$SPFeature.Definition.RootDirectory,"Scope: "+$SPFeature.$SPFeature.FeatureDefinitionScop)
    [System.Collections.ArrayList]$ShitArray = @("Parent Site: "+$SPF.Parent.Url)
    $ShitArray += "Feature guid: "+$SPF.DefinitionId.Guid
    $ShitArray += "Feature ID: "+$SPF.Definition.Id
    $ShitArray += "Feature Name: "+$SPF.Definition.DisplayName
    $ShitArray += "Compatibility Level: "+$SPF.Definition.CompatibilityLevel
    $ShitArray += "Physical File: "+$SPF.Definition.RootDirectory
    $ShitArray += "Scope: "+$SPF.FeatureDefinitionScope

    foreach($line in $ShitArray){Centeralize $line "white"}
    $FName = $SPF.Definition.DisplayName
    if(confirm $FName)
    {     
        Centeralize "Deleting SharePoint Feature $FName`n" "Red"
        $ss.Features.Remove($SPF.DefinitionId, $true)
    }
}

function FoundIt()
{
    $FName = "Feature Found: "+$SPFeature.Definition.DisplayName
    Centeralize $FName "Yellow"    
    [System.Collections.ArrayList]$ShitArray = @("Parent Site: "+$SPFeature.Parent.Url)
     $ShitArray += "Feature guid: "+$SPFeature.DefinitionId.Guid
     $ShitArray += "Feature ID: "+$SPFeature.Definition.Id
     $ShitArray += "Feature Name: "+$SPFeature.Definition.DisplayName
     $ShitArray += "Compatibility Level: "+$SPFeature.Definition.CompatibilityLevel
     $ShitArray += "Physical File: "+$SPFeature.Definition.RootDirectory
     $ShitArray += "Scope: "+$SPFeature.FeatureDefinitionScope

     foreach($line in $ShitArray){Centeralize $line "white"}
     $FName = $SPFeature.Definition.DisplayName
     if(confirm $FName)
     {     
         Centeralize "Deleting SharePoint Feature $FName" "Red"
         $SPSiteCol.Features.Remove($SPFeature.DefinitionId, $true)
     }
     #Remember the site is checked first, so even if the feature is found in the main site we don't want to forget each sub site
     TryWeb
}

function TryWeb()
{
        $SSW = $SPSiteCol | Get-SPWeb
        foreach($ss in $SSW)
        {
            $SPWFeature=$ss.Features[$StupidFeatureStr]
            if($SPWFeature)
            {
                FoundItInWeb $ss $SPWFeature
            }
        }
        Write-Host "Script is done."
}

#Start actual script by posting and asking user for responses
foreach($L in $MylogoArray){Centeralize $L "green"}
Centeralize $ScriptName "White"
#Notify User to enter the Site Collection URL then check if it exits.
Centeralize "Please enter a SP Site Collection URL`n"
Write-host "SharePoint Site Collection URL: " -ForegroundColor Magenta -NoNewline
$SPSitecolstr = Read-Host
Write-Host " "
Centeralize "Verifying SharePoint Site Collection URL, Please Wait...`n" "White"
if ($SPSiteCol=Get-SPSite $SPSitecolstr -EA SilentlyContinue)
{
    $DB = $SPSiteCol.ContentDatabase.Name
    $SQLServer = $SPSiteCol.ContentDatabase.Server
    Centeralize "Phhhh, ok good guess, that is a site collection here's what the systems got:`n" "Cyan"
    Centeralize "SqlSever\Instance: $SQLServer Using Database: $DB `n" "White"
    Write-host "Please enter the feature ID string: " -ForegroundColor Magenta -NoNewline
    $StupidFeatureStr = Read-Host
    Write-Host " "
    $SPFeature = $SPSiteCol.Features[$StupidFeatureStr]
    if(!$SPFeature)
    {
        Write-Host "Not Found in main site.... Trying Web"
        TryWeb
    }
    else
    {
         #Call Found Function
         Write-Host "Found in main site running found it function."
         FoundIt
    }

}
else
{
    $SPSitecolstr = "`""+$SPSitecolstr+"`" "
    $BadURL = $BadURL[0] + $SPSitecolstr + $BadURL[1]
    foreach($str in $BadURL){Centeralize $str "red"}
}   

Posted by Aemilianus Kehler | Permanent link