Monday, December 24, 2012

Recipe to Hide Features Across the SharePoint Farm

I was asked to make a tasty dish to hide features from all of the site collection administrators so they could not attempt to activate any of these in the recently upgraded SharePoint 2010 farm.  To do this I just needed to use PowerShell to set the hidden attribute to ”TRUE” of the feature element in each of the feature.xml files of the feature that I wanted hidden.  In the future I could activate the feature for a specific site using PowerShell.  (

Each feature has its own system folder in the 14 hive.  In my case, located here: \\machinename\c$\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\featurefolder\.  Notice I used a UNC path and referenced the C: shared drive (c$).  This will come in handy later.  Since we have multiple web front-end and application servers I created a .csv file listing these UNC paths for each machine.  Also, since I had more than one feature to update I created a .csv file listing the feature folders.  Below is a sample of my lists:
\\MACHINENAME1\c$\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\
\\MACHINENAME2\c$\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\


Here is the recipe:
Import the list of servers and paths csv file
Import the list of features csv file

Loop through each server
     Loop through each feature
          Locate the specific feature.xml file
          Load the elements of the XML into PowerShell
          Set the attribute hidden=”true”
          Save the feature.xml
     Go to next feature
IIS reset /noforce
Go to next server

The Garnish
I have two lists - 1) an Excel file of server paths and 2) an Excel file of feature folders.  I need PowerShell to read each row of these files.  PowerShell can’t easily read Excel files…but PowerShell CAN easily read .csv files and I can easily save my Excel files as .csv files.  For PowerShell to read the data in these .csv files I need to import them.  (

# List of features to be removed (file folder names)
$importedFeaturesCSV = Import-Csv C:\Features.csv

# Server/file path
$importedPathsCSV = Import-Csv C:\ServerPaths.csv

The Potatoes
I need to do a nested ForEach loop.  The outer loop will loop through each server in the farm from the ServerPaths.csv file.  The inner loop will loop through each feature in the Features.csv file.  (
ForEach Server
# Read each server path
Foreach ($line in $importedPathsCSV)

ForEach Feature
# Read each feature
Foreach ($line in $importedFeaturesCSV)

The Meat
1. Specify the XML File
I know the file system path and folder names for all of the features I need to hide.  I can do this dynamically because I have a .csv file with all of my feature folder names.  Here, I am assigning the complete UNC file path to the feature.xml file to a variable $thisXMLfile.

# Specify the XML file
$thisXMLfile = $myServerPath + $myXMLpath + "feature.xml"

2. Get-Content (PowerShell command)
To be able to modify the XML file I needed to get the content of the file to be able to modify it so I used the Get-Content PowerShell cmdlet. (

# Load the XML file
$featureXMLfile = [xml] (get-content $thisXMLfile)

3. SetAttribute (XML Method)
I need to add or change an attribute of an XML element.  If you are new to xml, allow me to save you some time here.  The SetAttribute method adds a new attribute or changes the value of an existing attribute. (

# Set the Attribute
$featureXMLfile.Feature.SetAttribute("Hidden", "TRUE")

4. Save the File
I need to save the changes back to the XML file.

# Save the XML file with the change

The Completed Dish
I ran the script using the following command just to have output of the results.
.\UpdateXML.ps1 > Results.txt

1  # Import .CSV's
2  # List of features to be removed (file folder names)
3  $importedFeaturesCSV = Import-Csv C:\Features.csv

4  # Server/file path
5  $importedPathsCSV = Import-Csv C:\ServerPaths.csv
6  # Can use filters
7  #$importedFeaturesCSV = Import-Csv C:\Features.csv | Where-Object {$_.FeaturePath -eq "FeatureFolderName\"}

8  # Set counts
9 $processedCount = 0
10 $totalCount = 0
11 $servers = 0

12 # Read each server path
13 Foreach ($line in $importedPathsCSV)
14 {   
15    if ($totalCount -ne 0)
16    {
17        echo "$processedCount of $totalCount features updated for $servers servers."
18    }
20    # Server/file path
21    $myServerPath = $line.ServerPath
22    echo "Current server: $myServerPath"
23    $servers++
25    # Read each feature
26    Foreach ($line in $importedFeaturesCSV)
27    {
28        $totalCount++
29        Try
30        {
31            $myXMLpath = $line.FeaturePath

33            # Specify the XML file
34            $thisXMLfile = $myServerPath + $myXMLpath + "feature.xml"

35            # Load the XML file
36            $featureXMLfile = [xml] (get-content $thisXMLfile)

37            # Set the Attribute
38            $featureXMLfile.Feature.SetAttribute("Hidden", "TRUE")

39            # Save the XML file with the change
40            $featureXMLfile.Save($thisXMLfile)

41            # Display attributes of the XML element
42            #echo $featureXMLfile.Feature
43            $processedCount++

44        }
46        Catch [system.exception]
47        {
48            echo "Problem with $thisXMLfile"
49        }
50    }

51 # IIS reset
52 iisreset /noforce

53 }
54 echo "Done! $processedCount of $totalCount features updated for $servers servers."

1 comment: