Blog

SharePoint health check (2): Extracting Customizations

5 min read
5 min read

This post is the second of a series explaining how to audit your SharePoint farm.

Extracting Customizations from the Farm

PowerShellWhen auditing the customizations of your SharePoint farm, the first step is to retrieve all of them in their currently deployed versions.

This can be achieved by some PowerShell code executed on one of the farms servers.

  1. Log in to one of your server on your SharePoint farm (you will require farm administrator permissions)
  2. Create a local folder to store the customizations.
  3. Open a SharePoint Management Shell (PowerShell) window and browse to that new folder.
  4. Extract all WSPs and/or Apps from SharePoint by running the following PowerShell scripts
    • Full trust farm solutions
      $ftcPath = "filepath to store the full trust farm solutions"
      (Get-SPFarm).Solutions | % {
              $_.SolutionFile.SaveAs($ftcPath + "\" + $_.Name)
      }
    • Sandboxed solutions (from a single site)
      $sbsPath = "filepath to store the sandboxed solutions"
      $site = Get-SPSite "http://<url to site>"
      $listTemplate = [Microsoft.SharePoint.SPListTemplateType]::SolutionCatalog
      $solGallery = $_.GetCatalog($listTemplate)
      $solGallery.Items | % {
          [System.IO.FileStream]$outStream = New-Object System.IO.FileStream(($sbsPath+"\"+$_.File.Name), [System.IO.FileMode]::Create);
          $fileData = $_.File.OpenBinary();
          $outStream.Write($fileData, 0, $fileData.Length);
          $outStream.Close();
      }
      
    • SharePoint Apps (from a single web)
      $appPath = "filepath to store the apps"
      $instances = Get-SPAppInstance -Web "http://<url to web>"
      foreach ($instance in $instances) {
          $filename = [RegEx]::Replace($instance.Title, "[{0}]" -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), '')+".app"
          Export-SPAppPackage -App $instance.App -Path ($webPath + "\"+ $filename)
      }
      
  5. Copy the folder with your customizations to a network share, which is accessible from where you run SPCAF

I want them all!

The code above just illustrates how to get solutions and apps from a single instance of a site or web.

To extract all code customizations from the farm you can use the following complete script. It retrieves them from all sites and webs which are accessible by the account executing the script.

It creates a folder named after the current date, subfolders for full trust solutions, sandboxed solutions and apps as well as subfolders for each site or web that contains a customisation.

folderstructure
Folder structure

During execution it logs the processed sites, solutions and apps.

Exporting SharePoint Customizations
Saving farm solutions to C:\Users\sp_admin\temp\20141009\FullTrust
    ftc.text1.wsp...Done
    ftc.text2.wsp...Done
    ftc.text3.wsp...Done
Saving sandboxed solutions to C:\Users\sp_admin\temp\20141009\Sandboxed
    http://my.sp2013.dev.local...None
    http://my.sp2013.dev.local/personal/sp_admin...None
    http://my.sp2013.dev.local/personal/sp_searchcontent...None
    http://portal.sp2013.dev.local...1
        MyCompany.Intranet.WebParts.wsp...Done
    http://portal.sp2013.dev.local/search...1
        MyCompany.Intranet.Search.wsp...Done
    http://portal.sp2013.dev.local/sites/appcat...None
    http://portal.sp2013.dev.local/sites/community...None
    http://portal.sp2013.dev.local/sites/developer...None
    http://portal.sp2013.dev.local/sites/project...None
    http://portal.sp2013.dev.local/sites/team...None
        MyCompany.Intranet.WebParts.wsp...Done
Saving Apps to (C:\Users\sp_admin\temp\20141009\Apps
    http://my.sp2013.dev.local...None
    http://my.sp2013.dev.local/personal/sp_admin...None
    http://portal.sp2013.dev.local...1
        OneNoteClassNotebookCreator.app...Done
    http://portal.sp2013.dev.local/Docs...None
    http://app-5cb2120e29a29f.app.dev.local/edu1note...None
    http://portal.sp2013.dev.local/News...2
        OneNoteClassNotebookCreator.app...Done
        MyNewsApp.app...Done
    http://portal.sp2013.dev.local/SearchCenter...None
    http://portal.sp2013.dev.local/SiteDirectory...None
    http://portal.sp2013.dev.local/Wiki...None
    http://portal.sp2013.dev.local/search...None
    http://portal.sp2013.dev.local/sites/appcat...None
    http://portal.sp2013.dev.local/sites/community...None
    http://portal.sp2013.dev.local/sites/developer...None
    http://portal.sp2013.dev.local/sites/project...None
    http://portal.sp2013.dev.local/sites/team...1
        OneNoteClassNotebookCreator.app...Done
Finished

Without further ado, her comes the complete script

### 
### Exports all SharePoint farm solutions, sandboxed solutions and apps to the file system
### Author: Matthias Einig, http://www.twitter.com/mattein
### Url: http: www.spcaf.com
###
Add-PSSnapin Microsoft.SharePoint.PowerShell
Write-Host "Exporting SharePoint Customizations"

# Preparation: (re)create folder named after the current date to collect all customizations
$foldername = (Get-Date -Format "yyyyMMdd").ToString()
rmdir  $foldername -Recurse -ErrorAction SilentlyContinue
$basePath = (mkdir $foldername).FullName

# Collect all disposable objects
Start-SPAssignment -Global

try {
    # Step 1: Save all farm solutions
    $ftcPath = (mkdir ($basePath +"\FullTrust")).FullName
    Write-Host "  Saving farm solutions to $ftcPath"

    (Get-SPFarm).Solutions | % {
        Write-Host "    $($_.Name)..." -NoNewline
        try{
            $_.SolutionFile.SaveAs($ftcPath + "\" + $_.Name)
           Write-Host -ForegroundColor Green "Done"
        }
        catch{
            Write-Host -ForegroundColor Red "Failed"
            Write-Error $_.Exception.Message; 
        }
    }

    # Step 2: Save all sandboxed solutions
    $sbsPath = (mkdir($basePath +"\Sandboxed")).FullName
    Write-Host "  Saving sandboxed solutions to $sbsPath"

    # Get all accessible SharePoint sites in the farm 
    # Warning: this might take a while, so consider to limit it to specific sites
    # Also it might retrieve duplicate solutions (in different versions) form different sites
    Get-SPSite -limit All -WarningAction SilentlyContinue | % {
        Write-Host "    $($_.Url)..." -NoNewline
        try{
            $listTemplate = [Microsoft.SharePoint.SPListTemplateType]::SolutionCatalog
            $solGallery = $_.GetCatalog($listTemplate)

            if($solGallery.ItemCount -gt 0)
            {
                Write-Host -ForegroundColor Green $solGallery.ItemCount

                # create subfolder for site with safe folder name removing the protocol and special chars
                $subfolder = [RegEx]::Replace($_.Url.Substring($_.url.IndexOf(":")+3), "[{0}]" -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), '')
                $sitePath = (mkdir ($sbsPath +"\"+ $subfolder)).FullName

                # save all sandboxed solutions of this site
                $solGallery.Items | % {
                    Write-Host "        $($_.File.Name)..." -NoNewline
                    try{

                        [System.IO.FileStream]$outStream = New-Object System.IO.FileStream(($sitePath+"\"+$_.File.Name), [System.IO.FileMode]::Create);
                        $fileData = $_.File.OpenBinary();
                        $outStream.Write($fileData, 0, $fileData.Length);
                        $outStream.Close();
                        Write-Host -ForegroundColor Green "Done"
                    }
                    catch{
                        Write-Host -ForegroundColor Red "Failed"
                        Write-Error $_.Exception.Message; 
                    }
                }
            }
            else{
               Write-Host "None" 
            }
        }
        catch{
            Write-Host -ForegroundColor Red "Failed"
            Write-Error $_.Exception.Message; 
        }
    }


    # Step 3: Save all SharePoint Apps
    $appPath = (mkdir($basePath +"\Apps")).FullName
    Write-Host "  Saving Apps to $appPath"

    # Get all accessible SharePoint webs in the farm 
    # Warning: this might take a while, so consider to limit it to specific sites
    # Also it might retrieve duplicate solutions (in different versions) form different sites
    Get-SPSite -Limit All -WarningAction SilentlyContinue | Get-SPWeb -Limit All -ErrorAction SilentlyContinue -WarningAction SilentlyContinue | % {
        Write-Host "    $($_.Url)..." -NoNewline
        try{
            $instances = Get-SPAppInstance -Web $_.Url

            if($instances.Count -gt 0)
            {
                Write-Host -ForegroundColor Green $instances.Count

                # create subfolder for web with safe folder name removing the protocol and special chars
                $subfolder = [RegEx]::Replace($_.Url.Substring($_.url.IndexOf(":")+3), "[{0}]" -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), '')
                $webPath = (mkdir ($appPath +"\"+ $subfolder)).FullName

                # export all apps
                foreach ($instance in $instances) {
                    # create safe file name
                    $filename = [RegEx]::Replace($instance.Title, "[{0}]" -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), '')+".app"
                    Write-Host "        $filename..." -NoNewline
                     try{
                        Export-SPAppPackage -App $instance.App -Path ($webPath + "\"+ $filename)
                        Write-Host -ForegroundColor Green "Done"
                    }
                    catch{
                        Write-Host -ForegroundColor Red "Failed"
                        Write-Error $_.Exception.Message; 
                    }
             
                 }
            }
            else{
               Write-Host "None" 
            }
        }
        catch{
            Write-Host -ForegroundColor Red "Failed"
            Write-Error $_.Exception.Message; 
        }
    }
}
finally{
    #Cleanup disposable objects
    Stop-SPAssignment -Global
    Write-Host "Finished"
}

The entire script can be downloaded from GitHub

Great, what now?

Now that you have extracted all code customizations you might wonder what to do with them.

» Read on at “SharePoint health check (3): Auditing Customizations”

Subscribe to our newsletter