Skip to content

Instantly share code, notes, and snippets.

@joerodgers
Last active December 11, 2019 18:50
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save joerodgers/e87a5de03450b97116c80fb3a1b34ae7 to your computer and use it in GitHub Desktop.
Script to export and import search schema on SharePoint 2013+.
Add-PSSnapin Microsoft.SharePoint.PowerShell
function Enable-SearchConfigurationFeature
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPWeb]$Web
)
$activatedFeatures = Get-SPFeature -Web $Web
# SearchConfigFields
if( ($activatedFeatures | ? DisplayName -eq "SearchConfigFields") -eq $null )
{
Enable-SPFeature -Identity "SearchConfigFields" -Url $web.Url | Out-Null
}
# SearchConfigContentType
if( ($activatedFeatures | ? DisplayName -eq "SearchConfigContentType") -eq $null )
{
Enable-SPFeature -Identity "SearchConfigContentType" -Url $web.Url | Out-Null
}
# SearchConfigListTemplate
if( ($activatedFeatures | ? DisplayName -eq "SearchConfigListTemplate") -eq $null )
{
Enable-SPFeature -Identity "SearchConfigListTemplate" -Url $web.Url | Out-Null
}
# SearchConfigList
if( ($activatedFeatures | ? DisplayName -eq "SearchConfigList") -eq $null )
{
Enable-SPFeature -Identity "SearchConfigList" -Url $web.Url | Out-Null
}
}
function Get-SearchConfigurationLibrary
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPWeb]$Web,
[Parameter(Mandatory=$false)][switch]$CreateIfNotExists
)
begin
{
$title = "Custom Search Configurations"
$description = "Contains custom search configuration schema."
$url = "CustomSearchConfigurations"
}
process
{
$list = $web.Lists.TryGetList($title)
if( $list -eq $null -and $CreateIfNotExists.IsPresent )
{
$guid = $web.Lists.Add( $title, $description, $url, "00BFEA71-E717-4E80-AA17-D0C71B360101", [int]([Microsoft.SharePoint.SPListTemplateType]::DocumentLibrary),$null)
$list = $web.Lists.GetList( $guid, $true )
}
if( $list -ne $null -and -not $list.Hidden ) # hide the list from the UI
{
$list.Hidden = $true
$list.Update()
}
return $list
}
end
{
}
}
function Get-SearchConfiguration
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPSite]$Site
)
begin
{
$defaultSourceElement = "<DefaultSourceId>00000000-0000-0000-0000-000000000000</DefaultSourceId>"
$bestBetsElement = "<BestBets xmlns:d4p1=`"http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08`" />"
$sourcesElement = "<Sources xmlns:d4p1=`"http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration.Query`" />"
$resultTypesElement = "<ResultTypes xmlns:d4p1=`"http://schemas.datacontract.org/2004/07/Microsoft.Office.Server.Search.Administration`" />"
$queryRulesElement = "<QueryRules xmlns:d4p1=`"http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08`" />"
$managedPropertyElement = "<d3p1:dictionary xmlns:d4p1=`"http://schemas.microsoft.com/2003/10/Serialization/Arrays`" />"
}
process
{
Write-Verbose "Processing site: $($site.Url)"
# get the search xml configuration for the site collection
$owner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPSite, $Site.RootWeb)
$portability = New-Object Microsoft.Office.Server.Search.Portability.SearchConfigurationPortability($Site)
$searchConfig = $portability.ExportSearchConfiguration($owner)
[PSCustomObject] @{
SiteUrl = $site.Url
WebUrl = $web.Url
Scope = "SiteCollection"
DefaultContentSource = -not $searchConfig.Contains($defaultSourceElement)
BestBets = -not $searchConfig.Contains($bestBetsElement)
ResultSource = -not $searchConfig.Contains($sourcesElement)
ResultTypes = -not $searchConfig.Contains($resultTypesElement)
QueryRules = -not $searchConfig.Contains($queryRulesElement)
ManagedProperty = -not $searchConfig.Contains($managedPropertyElement)
Schema = $searchConfig
}
foreach( $web in $site.AllWebs )
{
Write-Verbose "Processing Web: $($Web.Url)"
# get the search xml configuration for the web
$owner = New-Object Microsoft.Office.Server.Search.Administration.SearchObjectOwner([Microsoft.Office.Server.Search.Administration.SearchObjectLevel]::SPWeb, $web)
$portability = New-Object Microsoft.Office.Server.Search.Portability.SearchConfigurationPortability($Site)
$searchConfig = $portability.ExportSearchConfiguration($owner)
[PSCustomObject] @{
SiteUrl = $site.Url
WebUrl = $web.Url
Scope = "Web"
DefaultContentSource = -not $searchConfig.Contains($defaultSourceElement)
BestBets = -not $searchConfig.Contains($bestBetsElement)
ResultSource = -not $searchConfig.Contains($sourcesElement)
ResultTypes = -not $searchConfig.Contains($resultTypesElement)
QueryRules = -not $searchConfig.Contains($queryRulesElement)
ManagedProperty = -not $searchConfig.Contains($managedPropertyElement)
Schema = $searchConfig
}
$web.Close()
$web.Dispose()
}
}
end
{
}
}
function Export-CustomSearchConfiguration
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPSite]$Site
)
$configurations = $site | Get-SearchConfiguration
foreach( $configuration in $configurations )
{
if( $configuration.DefaultContentSource -or
$configuration.BestBets -or
$configuration.ResultSource -or
$configuration.ResultTypes -or
$configuration.QueryRules -or
$configuration.ManagedProperty -or $true)
{
$list = Get-SPWeb -Identity $configuration.WebUrl | Get-SearchConfigurationLibrary -CreateIfNotExists
if( $configuration.Scope -eq "SiteCollection" )
{
$fileName = "searchschema_sitecollection-scope_$(Get-Date -Format FileDate).xml"
}
else
{
$fileName = "searchschema_web-scope_$(Get-Date -Format FileDate).xml"
}
$urlOfFile = "$($list.RootFolder.ServerRelativeUrl)/$fileName"
$f = $list.RootFolder.Files.Add( $urlOfFile, [System.Text.Encoding]::Default.GetBytes( $configuration.Schema ), $true <# overwrite #> )
}
}
}
function Import-CustomSearchConfiguration
{
[cmdletbinding()]
param
(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][Microsoft.SharePoint.SPSite]$Site
)
foreach( $web in $site.AllWebs )
{
$scope = ""
$list = $web | Get-SearchConfigurationLibrary
if( $list -ne $null -and $list.ItemCount -gt 0 )
{
# get items that match the pattern
$items = $list.Items | ? Name -match "-scope_(?<FileDate>\d\d\d\d\d\d\d\d).xml$"
# get the most recently created xml file that matches the naming pattern
$configFile = $items | SELECT -ExpandProperty File | SORT TimeCreated -Descending | SELECT -First 1
if( $configFile -ne $null )
{
# make sure search config features are enabled
$web | Enable-SearchConfigurationFeature
# config list created by search config features
$configList = $web.Lists.TryGetList("Search Config List")
if( $configList -ne $null )
{
if( $configFile.Name -match "sitecollection-scope" )
{
$scope = "SPSite"
}
elseif( $configFile.Name -match "web-scope" )
{
$scope = "SPWeb"
}
if( $scope )
{
# upload config xml to the search config list
$newConfigFile = $configList.RootFolder.Files.Add( $configFile.Name, $configFile.OpenBinaryStream(), $true <# overwrite #> )
if( $newConfigFile -ne $null)
{
# set item properties on new config file
$newConfigFile.Item["Status"] = "Pending Import"
$newConfigFile.Item["Scope"] = $scope
$newConfigFile.Item.Update()
}
else
{
Write-Error "Failed to upload $($configFile.Name) to $($web.Url)"
}
}
else
{
Write-Error "Scope is null for config file $($configFile.Name) on $($web.Url)."
}
}
else
{
Write-Error "'Search Config List' is null for $($web.Url)."
}
}
else
{
Write-Error "No config file found for $($web.Url)."
}
}
else
{
Write-Verbose "No custom search config list found."
}
$web.Dispose()
}
}
$site = Get-SPSite -Identity "https://sharepoint.2016.contoso.com/sites/teamsite"
$site | Export-CustomSearchConfiguration
$site | Import-CustomSearchConfiguration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment