Disable featureIDs by SolutionID


I’m fairly new to Powershell (10 months) and I think is great - but still a novice (learning).
I’ve been tasked with providing scripts for automating content deployment. I am stuck on how to write a script disabling and re-enabling features associated with a solutionID. The below script runs fine in an environment with only (1) site/webapp/web but fails when run on a farm with multiple sites/webapps/webs.

How can I add to the script to traverse all sites/webapps/webs to identify all featureIDs assiciated with (a) solution ID and disable/enable at each level successfully.
Please respond soon. I’ve been stuck here for a very long time…way too long.

The Script loads data from an XML file

Locate FeatureIDs and deactivate all features associated with this solution

$s = get-spsite | ForEach{ $.URL }
$F = Get-SPFeature | where-object {$
.solutionid -eq $currentSolutionID} |%{ disable-SPFeature -Identity $_ -URL $s -confirm:$false } -ErrorAction SilentlyContinue
$feature = Get-spfeature| where-object {$.solutionid -eq $currentsolutionid} |%{ write-host " Deactivating feature: " $.displayname } -ErrorAction SilentlyContinue

Thank you in advance for your assistance.


$s = get-spsite | ForEach{ $_.URL }

Sets the variable $s to the last item in the ForEach loop, since it overwrites the item in the variable everytime it loops.

You should rather insert the code that you want to run in the ForEach-Object loop.

Also when you use


You should filter the object you want in that cmdlet and the just pipe it to


Instead of using all those ForEach-Object loops.

I dont have access to the SP module at the moment so I cant try it out.

In which way does it fail when you run it on multiple sites? Does it only run on one site?


Thanks for your response.
I believe I can understand what you mean by “not so many foreach statements” but being a novice, I am unaware of another way to do this (at this time).
Also, I have filtered first and then piped output to the disable-spfeature with this line :
$F = Get-SPFeature | where-object {$.solutionid -eq $currentSolutionID} |%{ disable-SPFeature -Identity $ -URL $s -confirm:$false }
To be clear, are you saying that this line could be more efficient?

The solution file has many features with some scoped at farm level, some at site, and some at webapp level.

There is a dev farm with one server, site/webapp and the script runs fine to disable all features under the same solutionID.

There is a test farm with (3) servers, (5) webapps and multiple subsites. The script has been added to one of the WFEs.
The script fails when retrieving the feature URL with the error “can’t convert object to a string”, so no features are disabled or re-enabled.

The desired state for the automated process is for the script to be standardized and traverse Get-SPFarm -> Get-SPsolution -> Get-SPFeature …
with an XML file that holds srandard/solution data (servers, solution IDs, etc.) so that needed changes are made in the XML file only.

Hope this helps to explain better.

Thanks again,


Alexander Johansson wrote:
$s = get-spsite | ForEach{ $_.URL } Sets the variable $s to the last item in the ForEach loop, since it overwrites the item in the variable everytime it loops.

This is not entirely accurate. It would be true if the code looked like this:

Get-SPSite | ForEach { $s = $_.URL }

The difference being that the assignment to $s is happening inside the ForEach loop body. When you assign the result of the entire pipeline to $s, on the other hand, $s will typically be an array (though it can also be $null or a single URL value, if Get-SPSite returns zero or one results.) This can also be written as:

$s = Get-SPSite | Select-Object -ExpandProperty URL

# In PowerShell v3 or later:

$s = (Get-SPSite).URL

The documentation for Disable-SPFeature (found here: http://technet.microsoft.com/en-us/library/ff607879(v=office.15).aspx ) says that the type of the -URL parameter is System.String. That’s a single string, not an array. If Get-SPSite returns multiple values, $s will be an array, which can be casted back to a string, but doesn’t have quite the effect you were after. If you need to call Disable-SPFeature for each site, then you’ll need another loop. Something along these lines, though I can’t test this code as I don’t have a SharePoint environment handy:

# Using the @() operator here ensures that $sites is an array, even if Get-SPSite only returns zero or one result.
# This can be important in PowerShell 2.0. 
$urls = @(Get-SPSite | ForEach-Object { $_.URL })

Get-SPFeature |
Where-Object {$_.solutionid -eq $currentSolutionID} |
ForEach-Object {
    $feature = $_

    Write-Host " Deactivating feature: $($feature.displayname)"

    foreach ($url in $urls)
        Disable-SPFeature -Identity $feature -URL $url -Confirm:$false -ErrorAction SilentlyContinue

Thank you Dave,

I understand a little better now. I knew I didn’t have the formula quite right.
I think this is what I was looking for. I’ll try it right away and let you know.

Thank you also Alexander.

Hi Dave,

I was finally able to test this and it works just fine.
Thank you very much again.

I hope I’m able to assist someone else in the future. You guys ROCK!

Thanks again.