This script create/delete crawled properties, managed properties, crawled property categories,

and property mappings. The script will also generate a sample configuration file as a starting point.

This script can be downloaded, along with many other SharePoint 2013 search related PowerShell scripts and tools via the following utility:
       http://gallery.technet.microsoft.com/coming_soon :)
 

Quickly find SharePoint 2013 Enterprise Search resources!!

Bing SP2013ES

http://www.bing.com/search?q=sp2013es

 
PowerShell
Edit|Remove
<# 
.SYNOPSIS 
       This script create/delete crawled properties, managed properties, crawled property categories,  
       and property mappings. The script will also generate a sample configuration file as a starting point. 
.DESCRIPTION 
       This script create/delete crawled properties, managed properties, crawled property categories,  
       and property mappings. The script will also generate a sample configuration file as a starting point. 
        
       This script can be downloaded, along with many other SharePoint 2013 search related  
       PowerShell scripts and tools via the following utility:  
       http://gallery.technet.microsoft.com/coming_soon :)  
        
       Quickly find SharePoint 2013 Enterprise Search resources!! 
       Bing SP2013ES  
       http://www.bing.com/search?q=sp2013es 
.EXAMPLE 
.\Maintain-SPEnterpriseSearchMetadataProperties.ps1 -GenerateSampleConfigurationFile  
Generate a sample configuration file 
.LINK 
http://gallery.technet.microsoft.com/ScriptCenter 
.NOTES 
  File Name : Maintain-SPEnterpriseSearchMetadataProperties.ps1 
  Author    : Brent Groom, Matthew King 
 
  TODO List 
   - 
   
#> 
 
 
 
param 
  ( 
    [string] $ConfigurationFile = $null,   
               # Specifies confuguration file name. The default is "this script name".config. i.e. Maintain-SearchMetadataProperties.config 
    [string] $SSAName = $global:ssaname,                
        # Specifies the search service application 
    [switch$GenerateSampleConfigurationFile, 
            # Specifies whether the script should generate a sample configuration file. If you specify this flag,  
            # the script will generate the file and exit. 
    [switch$DebugMode = $global:debug        
        # Signifies that the script should output debug statements 
     
    ) 
 
 
"" 
"<><><><><> Running script '$($MyInvocation.MyCommand.Definition)' <><><><><>"  
"" 
 
Add-PSSnapin AdminSnapIn -erroraction SilentlyContinue  
Add-PsSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue  
 
$spversion = get-spfarm|select BuildVersion 
 
# used for recursive iteration, i.e. running this script multiple times, if caller script is not in this folder must change 
$configurationname = $myinvocation.mycommand.Definition 
# strip it down 
$configurationname = $configurationname.replace('.ps1'''-split "\\" | select -last 1 
if ( $ConfigurationFile.length -eq 0) 
{ 
  $ConfigurationFile = "$configurationname.config" 
  if ($global:scriptsdir) { 
    $ConfigurationFile = Join-Path $global:scriptsdir $ConfigurationFile 
  } 
} 
 
$ssa = $falseif ( !$SSAName -or $SSAName -eq "" ) { $ssa = Get-SPEnterpriseSearchServiceApplication } 
else { $ssa = Get-SPEnterpriseSearchServiceApplication "$SSAName" } 
if ( !$ssa ) 
{ 
    write-host "Failed to retrieve Search Service Application...is this a SharePoint Admin node?" -fore red 
    $global:scriptsuccess = $false; 
    return 
} 
 
$fghighlight = "darkgreen" 
#---------------------------- Functions ------------------------------- 
 
Function FunctionGenerateSampleConfigurationFile([string]$ConfigurationFile) 
{ 
    $funcname = "FunctionGenerateSampleConfigurationFile" 
    if ($DebugMode) { "Starting  $funcname " } 
 
Write-Host "Writing file $ConfigurationFile" 
if (Test-Path $ConfigurationFile) { 
    if ($Confirm) { 
        $response = Read-Host "Overwite file '$ConfigurationFile'? y/n?" 
        if ($response -ne 'yes' -and $response -ne 'y') { 
            return 
        } 
    } 
} 
$selfdocumentingxml = @"  
<Configuration> 
  <configurationSection> 
      <ManagedProperties>  
        <!-- Valid types: 1=Text, 2=Integer, 3=Boolean, 4=Float, 5=Decimal, 6=Datetime--> 
        <!-- This is an example showing what each configurable property means 
        <mproperty name="referencemanagedproperty" type="1"> 
         <property name="RefinementEnabled">1</property> 
          <property name="Queryable">1</property> 
          <property name="SortableType">1</property> 
          <property name="MergeCrawledProperties">1</property>  
          Include values from all crawled properties mapped. All multi valued fields must have this value set to 1.  
          For example all taxonomy fields 
          <property name="MergeCrawledProperties">0</property>  
          Include values from a single crawled property based on the order specified. 
        </mproperty> 
      --> 
        <mproperty name="mptaxonomy1" type="1"> 
          <property name="RefinementEnabled">1</property> 
          <property name="Queryable">1</property> 
          <property name="SortableType">1</property> 
          <property name="MergeCrawledProperties">1</property> 
        </mproperty> 
 
        <mproperty name="mpxmlmapper1" type="1"> 
          <property name="RefinementEnabled">1</property> 
          <property name="Queryable">1</property> 
          <property name="SortableType">1</property> 
          <property name="MergeCrawledProperties">1</property> 
        </mproperty> 
 
      </ManagedProperties>  
       
      <CrawledProperties> 
 
        <CrawledProperty propertyName="cptaxonomy1" propertySet="e80ee0e8-71c0-4d8d-b918-360ad2fd7aa2" varType="31"/> 
        <CrawledProperty propertyName="cpxmlmapper1" propertySet="e80ee0e8-71c0-4d8d-b918-360ad2fd7aa2" varType="31"/> 
 
      </CrawledProperties> 
       
      <CrawledPropertyCategories> 
        <category name="Debug" propset="e80ee0e8-71c0-4d8d-b918-360ad2fd7aa2" MapToContents="1" DiscoverNewProperties="1"/> 
        <category name="JDBC" propset="4cc9f20a-c782-4c48-8961-c5356f8dff89" MapToContents="1" DiscoverNewProperties="1"/> 
      </CrawledPropertyCategories> 
       
      <Mappings> 
 
        <!--  Debug mappings --> 
        <mapping ManagedProperty="mpxmlmapper1"           CrawledProperty="cpxmlmapper1" /> 
        <mapping ManagedProperty="mptaxonomy1"               CrawledProperty="cptaxonomy1"  />  
         
      </Mappings> 
       
  </configurationSection> 
 </Configuration> 
"@ | Out-File $ConfigurationFile 
    if ($DebugMode) { "Finished $funcname " } 
 
}  
 
# TODO split this into two functions. One for check if changed and one to change. 
Function setProperty($objin$propertyName$propertyValue) 
{ 
    $funcname = "setProperty" 
    if ($DebugMode) { "Starting  $funcname " } 
 
    if ( $propertyName -eq "MergeCrawledProperties") {return $false} 
    if ( $propertyName -eq "RefinementEnabled") {return $false} 
 
 
    $propertyChanged = $false 
    $fname = "$configurationname setProperty" 
    #if ( $debug) {$fname+" objin="+$objin+" propertyName="+$propertyName+" propertyValue="+$propertyValue;$objin} 
    $oldvalue = $objin.$propertyName 
    if ( $oldvalue -is [bool]) 
    { 
         
        if ( $propertyvalue -eq "True") 
        {             
            if ($oldvalue -ne $true) 
            { 
              $propertyChanged = $true 
              $objin.$propertyName = $true; 
            } 
        } 
        elseif ( $propertyvalue -eq "False")    
        { 
            if ($oldvalue -ne $false) 
            { 
              $propertyChanged = $true 
              $objin.$propertyName = $false;                             
            } 
        } 
        else 
        { 
             
            $intpropertyvalue = [int]$propertyvalue 
            $boolvalue = [bool]$intpropertyvalue;                         
            if ($oldvalue -ne $boolvalue) 
            { 
              $propertyChanged = $true 
              $objin.$propertyName = $boolvalue;              
            } 
        } 
    } 
    else 
    { 
      if ($oldvalue -ne $propertyValue) 
      { 
        $propertyChanged = $true 
        #write-host ("hello1" ) -Foregroundcolor $fghighlight 
         
        $objin.$propertyName = $propertyValue; 
        #write-host ("hello2" ) -Foregroundcolor $fghighlight 
      } 
 
    } 
    if ($DebugMode) { "Finished $funcname " } 
 
    return $propertyChanged 
} 
 
Function manageCrawledPropertyCategory([xml]$thedata) 
{    
    $funcname = "manageCrawledPropertyCategory" 
    if ($DebugMode) { "Starting  $funcname " } 
 
    # handle all the  Crawled Properties 
    $thecats = $thedata.SelectNodes("Configuration/configurationSection/CrawledPropertyCategories/category") 
    if ($DebugMode) { "Starting manageCrawledPropertyCategory: "+$thecats } 
    if ( !$thecats -or $thecats -eq $null -or $thecats.length -eq 0) 
    { 
          "No categories to manage" 
    } 
    write-host ("Creating Categories..." ) -Foregroundcolor $fghighlight 
         
    foreach ($thecat in $thecats) 
    {       
      $catname = $thecat.name 
      if ($DebugMode) { "catname: "+$catname } 
      Write-Host ("."-NoNewline 
      $catid = $thecat.propset 
      $maptocontents = $thecat.MapToContents -eq '1' 
      $discovernewprops = $thecat.DiscoverNewProperties -eq '1' 
      #"Looking at catname:$catname catid:$catid" 
 
      # get the category 
      $searchapp = $ssa 
      #$catobj = Get-SPEnterpriseSearchMetadataCategory -Identity $catname -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
       
      $schema = new-object Microsoft.Office.Server.Search.Administration.Schema($searchapp) 
      $allCats = $schema.AllCategories 
      $doNotSkipThisCat = $True       
      if ( $allCats.Contains($catname-eq $true) 
      { 
          $existingCat = $allCats.Item($catname) 
          #$doNotSkipThisCat = $False 
          #"The category name already exists: $catname" 
      } 
      $guid = new-object System.Guid($catid) 
      if ( $allCats.Contains($guid-eq $true) 
      { 
          $existingCat = $allCats.Item($guid) 
          $doNotSkipThisCat = $False       
          #"The category id already exists: $catid" 
      } 
      if ( $doNotSkipThisCat -eq $True) 
      { 
          "Creating crawled category: $catname catid:$catid" 
          $catobj = $allCats.Create($catname$catid)  
       
          # O15 PowerShell doesn't work yet 
          #$catobj = New-SPEnterpriseSearchMetadataCategory -Name $catname -Propset $catid -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
           
          Set-SPEnterpriseSearchMetadataCategory -Identity $catname -MapToContents $maptocontents -DiscoverNewProperties $discovernewpropsi -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      } 
       
      if ($DebugMode) { "$configurationname manageCrawledPropertyCategory:  Category name="+$catname  } 
    } 
    write-host ("." )  
    write-host ("Done Creating Categories" ) -Foregroundcolor $fghighlight 
 
    if ($DebugMode) { "Finished $funcname " } 
 
} 
 
Function manageCrawledProperties([xml]$thedata) 
{ 
    # <CrawledProperty varType="31" propertyName="url" propertySet="11280615-f653-448f-8ed8-2915008789f2" />  
 
    $funcname = "manageSPEnterpriseSearchMetadataCrawledProperty" 
    if ($DebugMode) { "Starting  $funcname " } 
    write-host ("Creating Crawled Properties..." ) -Foregroundcolor $fghighlight 
 
    # handle all the  Crawled Properties 
    $thecps = $thedata.SelectNodes("Configuration/configurationSection/CrawledProperties/CrawledProperty") 
         
    foreach ($thecp in $thecps) 
    {       
            Write-Host ("."-NoNewline 
      $cpobj = $null 
          $cpname = $thecp.propertyName 
          $cppropset = $thecp.propertySet 
      $cpvarianttype = $thecp.varType 
      if (-not $cpvarianttype) { $cpvarianttype = 0 } 
 
      # get the property - using Set-SPEnterpriseSearchMetadataCrawledProperty ensures that a single crawledprop object is returned 
      #trap {write-host ("Creating Crawled property $cpname " ) -Foregroundcolor green; Continue} 
      $searchapp = $ssa 
       
      $cat = Get-SPEnterpriseSearchMetadataCategory -Identity $cppropset -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      if ( !$cat) 
      { 
        write-host ("Category with PropSet:$($cppropset) doesn't exist. Skipping crawled property:$($cpname)"-ForegroundColor Red 
        continue 
      } 
       
      $cpobj = Get-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -Category $cat -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
       
      if ( !$cpobj) 
      { 
 
<# 
 
      Debug helper 
      $searchapp = Get-SPEnterpriseSearchServiceApplication "Search Service Application" 
      $cppropset = "00130329-0000-0130-c000-000000131346" 
      $cpname = "ows_geographies" 
      $cpvarianttype = "4127" 
      $cat = Get-SPEnterpriseSearchMetadataCategory -Identity $cppropset -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      $cpobjget = Get-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -Category $cat -SearchApplication $searchapp  
      $cpobjnew = New-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -VariantType $cpvarianttype -Category $cat -Propset $cppropset -SearchApplication $searchapp -IsNameEnum $false  
      $cpobjget2 = Get-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -Category $cat -SearchApplication $searchapp  
      $cpobjget 
      $cpobjnew 
      $cpobjget2 
      # The above code may 
#> 
        # TODO handle all the different variant types 
        # apparently all properties can be multivalued. you cannot set this variable as it is not used... -IsMultiValued 1 
        #New-SPEnterpriseSearchMetadataCrawledProperty -SearchApplication $searchapp -Category $cat -Propset -Name testcrawledproperty -IsNameEnum $false 
        #$cpobj = New-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -SearchApplication $searchapp -Category $cat -Propset $cppropset -IsNameEnum $false -ErrorAction:SilentlyContinue 
        # Try to leave off the category?? 
        $cpobj = New-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -VariantType $cpvarianttype -Category $cat -Propset $cppropset -SearchApplication $searchapp -IsNameEnum $false  
        $cpobj = Get-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -Category $cat -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
        # The new command outputs a strange message -- WARNING: Extra parameter ignored: '-SearchApplication'. 
        #$cpobj = New-SPEnterpriseSearchMetadataCrawledProperty -Name $cpname -VariantType $cpvarianttype -Category $cat -Propset $cppropset -IsNameEnum $false -ErrorAction:SilentlyContinue 
        if ( !$cpobj) 
        { 
          write-host ("Failed to create Crawled Property - name:'$cpname' variant:'$cpvarianttype' propset:'$($cppropset)' " ) -Foregroundcolor Red 
        } 
        else 
        { 
          write-host ("Created Crawled Property - name:'$cpname' variant:'$cpvarianttype' propset:'$($cppropset)' " ) -Foregroundcolor green  
        } 
      } 
      else 
      { 
        #TODO write-host ("Previously created Crawled property $cpname propset:$($cppropset)" ) -Foregroundcolor green  
        #$cpobj | Set-SPEnterpriseSearchMetadataCrawledProperty -propset $cppropset -ErrorAction:SilentlyContinue 
      } 
      if ($DebugMode) { "$configurationname mainwork:  Crawled Property name="+$cpname  } 
    } 
    if ($DebugMode) { "Finished $funcname " } 
    write-host ("." )  
    write-host ("Done Creating Crawled Properties." ) -Foregroundcolor $fghighlight 
 
} 
 
# TODO handle all the different types 
# TODO handle changing the type delete/add 
Function ManageManagedProperties([xml]$thedata) 
{ 
    $funcname = "ManageManagedProperties" 
    if ($DebugMode) { "Starting  $funcname " } 
    write-host ("Creating Managed Properties..." ) -Foregroundcolor $fghighlight 
 
    $themps = $thedata.SelectNodes("Configuration/configurationSection/ManagedProperties/mproperty") 
     
    #manage-ManagedProperties 
    # handle all the  Managed Properties 
    foreach ($themp in $themps) 
    { 
      Write-Host ("."-NoNewline 
      $mpname = $themp.name 
       
      # get the property 
      $searchapp = $ssa 
      $mpobj = Get-SPEnterpriseSearchMetadataManagedProperty -Identity $mpname -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      if ( !$mpobj) 
      { 
        $mptype = $themp.type 
        $mpobj = New-SPEnterpriseSearchMetadataManagedProperty -Name $mpname -Type $mptype -SearchApplication $searchapp 
        write-host ("Created managed property '$mpname'" ) -Foregroundcolor green 
      } 
      $objchanged = $false 
      foreach ($prop in $themp.property) 
      { 
        
        $propname = $prop.name 
        # type is a readonly property so continue. TODO have setProperty handle readonly properties 
        if ( $propname -eq "type") 
        { 
          continue 
        } 
        $propval = $prop.get_InnerText() 
        #if ( $DebugMode){"value before setProperty="+$mpobj.$propname} 
        $propchanged = setProperty $mpobj $propname $propval 
        if ( $propchanged -eq $True) 
        { 
          $objchanged = $True 
        } 
        #if ( $DebugMode){"value after setProperty="+$mpobj.$propname} 
      } 
 
      if ( $objchanged) 
      { 
        $mpobj.update()          
        #TODO write-host ("Updated managed property $mpname") -Foregroundcolor green 
      } 
      else 
      { 
      # TODO "No changes to managed property $mpname " 
      } 
      if ($DebugMode) { "$configurationname ManageManagedProperties:  Managed Property name="+$mpname  } 
     
    } 
    if ($DebugMode) { "Finished $funcname " } 
    write-host ("." )  
    write-host ("Done Creating Managed Properties." ) -Foregroundcolor $fghighlight 
} 
 
 
#TODO remove the mappings 
Function manageMappings([xml]$thedata) 
{ 
    $funcname = "manageMappings" 
    if ($DebugMode) { "Starting  $funcname " } 
    write-host ("Creating Crawled to Managed Property Mappings..." ) -Foregroundcolor $fghighlight 
    $searchapp = $ssa 
 
    # setup the property mappings 
    $themappings = $thedata.SelectNodes("Configuration/configurationSection/Mappings/mapping") 
     
    foreach ($themap in $themappings) 
    { 
      Write-Host ("."-NoNewline 
      $mpobj = $NULL 
      $cpobj = $NULL 
      $position = "first" 
       
      $mpname = $themap.ManagedProperty 
      $cpname = $themap.CrawledProperty 
      $propset = $themap.cppropset 
      $cpvarianttype = $themap.cpvarianttype 
      #$propset 
      if ($themap.position) { 
        $position = $themap.position 
      } 
      $mpobj = Get-SPEnterpriseSearchMetadataManagedProperty -Identity $themap.ManagedProperty -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      # need to add in the propset here !!! 
      $cpobj = Get-SPEnterpriseSearchMetadataCrawledProperty -Name $themap.CrawledProperty -PropSet $propset -SearchApplication $searchapp -ErrorAction:SilentlyContinue 
      #$cpobj 
      if ($mpobj -eq $NULL) 
      { 
        "manageMappings: Managed Property '$mpname' doesn't exist" 
      } 
      if ($cpobj -eq $NULL) 
      {  
        Write-Host("manageMappings: Crawled Property doesn't exist with name:'$cpname' variant type:'$cpvarianttype' propset:'$propset' "-ForegroundColor Red 
      } 
      if ($mpobj -eq $NULL -or $cpobj -eq $NULL) 
      { 
         continue 
      } 
      #"mptype=    $($mpobj.ManagedType)" 
      #"cpvarianttype = $cpvarianttype" 
 
      # See if this mapping already exists 
      $mplist = $cpobj.GetMappedManagedProperties() 
      $alreadymapped = $false 
      foreach ($mp in $mplist) 
      { 
        if ( $mpname -eq $mp.name) 
        { 
          $alreadymapped = $true 
        } 
      } 
      if ( !$alreadymapped) 
      { 
        write-host ("Creating new mapping - MP: '$($mpobj.Name)' $($mpobj.ManagedType) to CP: '$($cpobj.Name)' in $position position" ) -Foregroundcolor green 
        if ($position -eq 'last') 
        { 
            # this inserts at the end of the list 
            try 
            { 
                $newmap = New-SPEnterpriseSearchMetadataMapping -ManagedProperty $mpobj -CrawledProperty $cpobj -SearchApplication $searchapp  
                if ( !$newmap) 
                { 
                    "Failed to create mapping of - MP: '$($mpobj.Name)' $($mpobj.ManagedType) to CP: '$($cpobj.Name)' $($cpobj.VariantType)" 
                } 
            } 
            catch [Exception]  
            { 
                #$_.Exception.GetType().FullName  
                #$_.Exception.Message 
                "Failed to create mapping of - MP: '$($mpobj.Name)' $($mpobj.ManagedType) to CP: '$($cpobj.Name)' $($cpobj.VariantType)" 
            } 
        } 
        else 
        { 
            try 
            { 
                $mappings = $mpobj.GetMappings() 
                # first get the existing crawled properties 
                $cplist = $mappings | % { 
                    $map = $_;   
                    Get-SPEnterpriseSearchMetadataCrawledProperty -Name $map.CrawledPropertyName -SearchApplication $searchapp -PropSet $map.CrawledPropset 
                } 
                # delete the mappings 
                $mappings | % {  
                    Remove-SPEnterpriseSearchMetadataMapping $_ -SearchApplication $searchapp -Confirm:$false  
                } 
                # add ours in 
                $newmap = New-SPEnterpriseSearchMetadataMapping -ManagedProperty $mpobj -CrawledProperty $cpobj -SearchApplication $searchapp  
                if ( !$newmap) 
                { 
                    "Failed to create mapping of - MP: '$($mpobj.Name)' $($mpobj.ManagedType) to CP: '$($cpobj.Name)' $($cpobj.VariantType)" 
                } 
 
                # add originals back in after it 
                $cplist | % { $cp = $_ 
                    New-SPEnterpriseSearchMetadataMapping -ManagedProperty $mpobj -CrawledProperty $cp -SearchApplication $searchapp | Out-Null 
                } 
            } 
            catch [Exception]  
            { 
                #$_.Exception.GetType().FullName  
                #$_.Exception.Message 
                "Failed to create mapping of - MP: '$($mpobj.Name)' $($mpobj.ManagedType) to CP: '$($cpobj.Name)' $($cpobj.VariantType)" 
            } 
        } 
      } 
      #else 
      #{ 
          # TODO "No change to mapping: $cpname -> $mpname" 
      #} 
    } 
    if ($DebugMode) { "Finished $funcname " } 
    write-host ("." )  
    write-host ("Done Creating Crawaled Property to Managed Property Mappings." ) -Foregroundcolor $fghighlight 
 
} 
 
Function main([string]$ConfigurationFile) 
{ 
    $funcname = "main" 
    if ($DebugMode) { "Starting  $funcname " } 
     
 
    [xml]$xmldata = [xml](Get-Content $ConfigurationFile) 
    if (-not $xmldata) { return } 
    if ( $DebugMode) 
    { 
      $ConfigurationFile 
      $xmldata.get_InnerXml() 
    } 
     
    manageCrawledPropertyCategory $xmldata 
 
    manageCrawledProperties $xmldata 
 
    manageManagedProperties $xmldata 
     
    manageMappings $xmldata 
 
    # TODO optimize the gets and store the objects in a hash so we don't have to get them again 
    if ($DebugMode) { "Finished $funcname " } 
     
} 
 
if ( $GenerateSampleConfigurationFile) 
{ 
  FunctionGenerateSampleConfigurationFile $ConfigurationFile 
} 
else 
{ 
  main $ConfigurationFile 
}