• Hybrid Search with SharePoint 2013 and Office 365

    I recently presented a session at the Yorkshire (UK) SharePoint User Group on Hybrid Search with SharePoint 2013 and Office 365, as promised to the attendees attached is the presentation (including demo videos).

    Brendan Griffin - @brendankarl

  • Backup/Restore-SPSite and the Missing Term Set!

    One of my customers recently wanted to move a Site Collection into a new Content Database, however instead of using Move-SPSite to do this they decided to use Backup/Restore-SPSite and here is where the fun began! 

    One of the key differences between using Move-SPSite and Restore-SPSite is that Restore-SPSite will assign a new GUID to the Site Collection as part of the restore process, one of the results of this is that the Site Collection Term Set will lose its mapping and will be in-accessible - the reason for this is that the ACL for the Term Set uses the Site GUID and if the Sites GUID changes, it will not have access to its Site Collection Term Store.

    The result of this for my customer was that the Site Collection Term Set vanished and was inaccessible when the Site Collection that had been restored, fortunately this is fairly easy to resolve using the steps below:

    • Determine the GUID (UniqueId) of the Site Collection Term Set - This can be obtained by querying the MMS Service Application database. In the example below the Site Collection was named "qwerty".

    • Execute the following script - Replacing TermSetGUID with the UniqueId obtained in the step below and SiteURL with the URL of Site Collection that you need to grant access to the Term Set. Please run with an account that has "Full Control" permissions on the MMS Service Application and is a "Term Store Administrator"

    asnp *sharepoint* -ea 0
    $TermSetGUID = "TermSetGUID"
    $Site = Get-SPSite "SiteURL"
    $TermSet = Get-SPTaxonomySession -Site $Site
    $Group = $TermSet.DefaultSiteCollectionTermStore.Groups | Where {$_.Id -eq $TermSetGUID}
    $Group.AddSiteCollectionAccess($Site.Id)
    $Group.TermStore.CommitAll()


    Brendan Griffin - @brendankarl

  • Export Members of a SharePoint Audience

    Here is a quick PowerShell script that I put together for a customer to enable them to export the usernames and e-mail addresses of all users that are members of a specific SharePoint Audience to a CSV file.

    Please update the three highlighted values, $Output is the location to write the CSV file to, replace http://intranet.contoso.com with the URL of a Site Collection that resides within a Web Application that is associated with the User Profile Service Application that contains the Audience and replace "Test" with the name of the Audience that you wish to export.

    This script was tested on SharePoint 2010 but should also work on SharePoint 2013.

    asnp *SharePoint* -ea 0
    $Output="D:\Output.csv"
    "Username"+","+"Email" | Out-File -Encoding Default -FilePath $Output;
    $Site = Get-SPSite "http://intranet.contoso.com"
    $Context=[Microsoft.Office.Server.ServerContext]::GetContext($Site)
    $AudManager=New-Object Microsoft.Office.Server.Audience.AudienceManager($Context)
    $Audience=$AudManager.Audiences | Where {$_.AudienceName -eq "Test"}
    Foreach ($Member in $Audience.GetMembership())
    {
    Write-Host $Member.NTName
    Write-Host $Member.Email
    $Member.NTName + "," + $Member.Email | Out-File -Encoding Default -Append -FilePath $Output
    }

    Brendan Griffin - @brendankarl

  • SharePoint Maintenance

    From time to time you may need to temporarily make SharePoint unavailable due to applying a CU, Service Pack etc. One of my customers places an App_Offline.htm file at the root of the IIS Virtual Directory for each Web Application on each WFE server within their farm, this presents users with a nice friendly message when the try to access SharePoint explaining that SharePoint is currently unavailable due to scheduled maintenance.

    This works really well and they wanted to automate the process for copying the App_Offline.htm file prior to performing maintenance, I wrote the following two scripts to automate this. The first script copies the file (to enter maintenance), the second deletes it (to return SharePoint into service).

    The script has logic to handle MOSS 2007, SharePoint 2010 and 2013. It will iterate through all Web Apps and Zones and copy the App_Offline holding page to the Webroot of the IIS site for each Web Application on each server that hosts a Web Application. Simply execute with PowerShell on a server within the farm, the script expects there to be an App_Offline.htm file within the directory that the script is executed from. The script needs to be run using an account that has local admin permissions on each server and makes the presumption that SMB/CIFS access is available.

    Enter Maintenance

    #Load SharePoint assembly
    $Assemblies = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

    #Check if the script is running on MOSS 2007
    If (Test-Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12"){$Mode="2007"}

    #Grab all Web Apps
    $WebApps = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.WebApplications

    #Retrieve Servers
    $Farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
    $SPServers = $Farm.Servers | Where {$_.Role -eq "Application"} | Foreach {$_.Name}

    Foreach ($WebApp in $WebApps)
    {
    Foreach ($URL in $WebApp.AlternateUrls)
        {
        If ($Mode = "2007")
        {
        $WebRoot = ($WebApp.GetIisSettingsWithFallback($URL.URLZone)).Path.FullName -replace ":","$"
        }
        Else
        {
        $WebRoot = ($WebApp.GetIisSettingsWithFallback($URL.Zone)).Path.FullName -replace ":","$"
        }
        Write-Host "Setting the Holding Page for" $WebApp.Name "- Zone:" $Url.URLZone -ForegroundColor Green
        Foreach ($Server in $SPServers)
            {
            Write-Host "-Updating" $Server -ForegroundColor Green
            Copy-Item "app_offline.htm" "\\$Server\$Webroot\"
            }
        }
    }

    Return SharePoint into Service

    #Load SharePoint assembly
    $Assemblies = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")

    #Check if the script is running on MOSS 2007
    If (Test-Path "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12"){$Mode="2007"}

    #Grab all Web Apps
    $WebApps = [Microsoft.SharePoint.Administration.SPWebService]::ContentService.WebApplications

    #Retrieve Servers
    $Farm = [Microsoft.SharePoint.Administration.SPFarm]::Local
    $SPServers = $Farm.Servers | Where {$_.Role -eq "Application"} | Foreach {$_.Name}

    Foreach ($WebApp in $WebApps)
    {
    Foreach ($URL in $WebApp.AlternateUrls)
        {
        If ($Mode = "2007")
        {
        $WebRoot = ($WebApp.GetIisSettingsWithFallback($URL.URLZone)).Path.FullName -replace ":","$"
        }
        Else
        {
        $WebRoot = ($WebApp.GetIisSettingsWithFallback($URL.Zone)).Path.FullName -replace ":","$"
        }
        Write-Host "Removing the Holding Page for" $WebApp.Name "- Zone:" $Url.URLZone -ForegroundColor Green
        Foreach ($Server in $SPServers)
            {
            Write-Host "-Updating" $Server -ForegroundColor Green
            Remove-Item "\\$Server\$Webroot\app_offline.htm"
            }
        }
    }

    Brendan Griffin - @brendankarl

  • SharePoint 2010 Organization Browser - Expose more than just User, Title and About Me (Sort Of).

    One of my favourite customers was very keen to display the OOB Organisation Browser web part on several of their pages but they wanted to expose more than the default values against the user. I know this can be done by clicking across to the users profile page but the extra click was not acceptable.

    As you will know this is a Silverlight control and therefore not customisable that easily so the solution I came up with was to essentially grab the properties they wanted to expose and copy them all in to the 'About Me' portion of the user profile.

    They have somewhere in the region of 1000 users on this particular system so obviously I enlisted the help of our friend PowerShell.

    The code looks something like the below, please be aware of the warning! and that this will obviously not keep the data fresh... Also it might be worth stopping the users ability to edit the About Me field so they are not too disappointed when you run it again and clear all their entries!

    WARNING! THIS CODE WILL REMOVE ALL INFORMANTION IN THE ABOUT ME FIELD FOR ALL USERS BEFORE IT ADDS THE NEW PROPERTIES!

    #Add Sharepoint PowerShell Modules
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"

    #Get the Web application           
    $site=new-object Microsoft.SharePoint.SPSite("http://sharepoint2010/");    

    #Get the Service Context of the Web Application      
    $serviceContext = Get-SPServiceContext $site;           
    $site.Dispose();           

    #Get the User Profile Manager
    $upm = new-object Microsoft.Office.Server.UserProfiles.UserProfileManager($serviceContext);

    #Get all of the user profiles for the selected User Profile manager
    $profiles = $upm.GetEnumerator()

    #Execute a For Each loop against every user
    foreach ($userProfile in $profiles)
    {

    #Delete all values in the AboutMe Section of the User Profile for ALL Users     
    $am = $userProfile["AboutMe"]        
    $am.Clear()     
         
    #Set the AboutMe property to the value of am (empty)          
    $userProfile["AboutMe"].Value = $am;      
        
    #Save the profile changes back to the User Profile store           
    $userProfile.Commit()

    #Get the fields you want to expose in the AboutMe Section of the User Profile and on the Org Browser

    $userProperty1 = $userProfile["WorkEmail"].Value
    $userProperty2 = $userProfile["Office"].Value

    #Concatinate the values you have and add them to the AboutMe Section using the silverlight encoding

    $userProfile["AboutMe"].Value = "Email: "+"$userProperty1" + "
" + "Office Location: " +"$userProperty2"
    #Commit the changes to the user profile 

    $userProfile.Commit();

    }


    Essentially it just grabs existing profile properties and puts them into the 'About Me' field.. Don't forget to use "
" which is Silverlight encoding for new line.

    Thanks to Dave Little for his expert help with that :)

    I just ran the script on my test VM with 1000 users and it completed in sub 30 seconds.

    The results are a little something like this:

    WARNING! THIS CODE WILL REMOVE ALL INFORMANTION IN THE ABOUT ME FIELD FOR ALL USERS BEFORE IT ADDS THE NEW PROPERTIES!


    Hopefully someone will find this useful - I scoured the internet and could not find anything that did what I wanted :)


    Enjoy!


    Andy