Wednesday, October 11, 2017

Zero Downtime Patching in SharePoint Server 2016


Recently needed to do an update of eight farms for a client.  Now I can never do as thorough of a job as Microsoft and others have done describing the process so I will not even attempt it. (see also links at bottom).

What I needed was a way to walk through the whole phase as simply as possible so I wrote and/or borrowed code for a PowerShell script to streamline the process

Basic steps of the script
  1. Copy updates to all servers in farm
  2. Prompt user to remove a web front end (WFE) user from Network Load Balancer(NLB)
  3. Enable Side By Side SharePoint code on that WFE
  4. Trigger the coping of Side By Side files to the c:\program files\common files\microsoft shared\web server extensions\16\template\layouts\{current SharePoint version number}
  5. Prompt user to install patch on this WFE (may possibly automate this) and reboot server
  6. Prompt user to add WFE back into NLB and remove other WFE (could add a loop for more than 2 WFE)
  7. Enable Side by Side on second WFE
  8. Trigger SharePoint file copy
  9. Prompt to install patches on second WFE and reboot server
  10. Disables the Distributed Cache(DC) on the first server designated with the "DistributedCache" role
  11. Prompts user to install patched on this server and reboot.
  12. Re-enables the distributed cache on this server
  13. Disables the DC on the second server
  14. Prompts to install patches and reboot server
  15. Re-enables the distributed cache on this server
  16. Runs database upgrade on all content databases
  17. Runs PSConfig on each server one at a time maintaining availability of all services
  18. Set Side By Side to the new version that has just been installed on all servers

Command Line

PS> .\SP-UpdateFarm.ps1 -farmaccount "test\test-farm" -weburl "https://test-partner.example.com/" -wfeSvr01 "TESTWFE01" -wfeSvr02 "TESTWFE01" -otherSvrs01 @( "TESTAPP01", "TESTSRCH01", "TESTDCS01") -otherSvrs02 @( "TESTAPP02", "TESTSRCH02", "TESTDCS02") -test $true

Parameters

[string] $farmaccount  ="The Farm account to connect to SharePoint"
[string] $weburl ="Web application URL to the use for upgrading"
[string] $wfeSvr01 = "Name of the first Web Front End"
[string] $wfeSvr02 ="Name of the Second Web Front End"
Array $otherSvrs01="Other servers in your HA farm all 01"
Array $otherSvrs02 ="Other servers in your HA farm all 02"
[string] $patchFolderUNC ="UNC to copy the upgrade files from"
[string] $patchInstallFolder="The folder to place the upgrade files in on remote servers"
[bool]$test ="run through script connecting to servers but not actually performing actions"

Gotchas

  • if you have a folder in the "web server extensions\16\template\layouts" folder that looks like a version number for example 1.2.3.4 it can be deleted when you enable the Side By Side
  • remote powershell must be allowed for this to work
  • you must run as Farm account
  • PowerShell must be run as Administrator

SP-UpgradeFarmZDP.ps1



See also:
https://technet.microsoft.com/en-us/library/mt767550(v=office.16).aspx
https://technet.microsoft.com/en-us/library/mt743024(v=office.16).aspx
https://blogs.technet.microsoft.com/pla/2016/03/10/zero-downtime-patching-in-sharepoint-server-2016/
https://technet.microsoft.com/en-us/library/cc748824(v=office.16).aspx




Tuesday, September 15, 2015

Nintex Task Form NullReferenceException

Brief history, a client of mine had workflows created in Nintex on Sharepoint 2010 that did not use Nintex forms. These workflows were ported to  SharePoint 2013 with Nintex Forms.  After that none of the a “Request review”, “Request data” or a “Request approval” activity would work.
The simplest way to recreated the error was to preview the form but you would get the same error if you published and ran the workflow.


Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.


Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. 

Stack Trace: 


[NullReferenceException: Object reference not set to an instance of an object.]

Nintex.Forms.SharePoint.ApplicationPages.PreviewNintexForm.GetDefaultValuesForWorkflowVariables() +557
Nintex.Forms.SharePoint.ApplicationPages.PreviewNintexForm.InitializeData() +846
Nintex.Forms.SharePoint.ApplicationPages.PreviewNintexForm.OnInit(EventArgs e) +645
System.Web.UI.Control.InitRecursive(Control namingContainer) +186
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2098

After "debugging" the Nintex's code I determined that it was using a XML file stored in a hidden SharePoint list. This XML file contained the form as well as the workflow variable definitions. 

It turns out that earlier versions of Nintex stored blank variables as . The new version stores them as the code did not support the old format.

All I had to do was go through all the workflow variables and assign them a single space as the default value and the forms started working again.

Thursday, September 11, 2014

Select all items in a MultiLookupPicker


I had a client that wanted all options in a multi-select lookup field to be selected when a new item was created


Then the user could deselect what they did not need, more common to need all. Well it turns out you can't specify the default values for a multi-lookup field.

I came across this post from Bil Simser (thanks) that gave me the ideas for this JavaScript function to select all the possible options in a specific MultiLookupPicker item.  The argument is the name of the field you want to select from, there could be more than one on the page.

jQuery.fn.exists = function () {
    return this.length !== 0;
};

SD.SP2010.MultiLookupPicker.SelectAll = function(pickerTitle) {
    if (jQuery("[id$='_SelectCandidate'][title^='" + pickerTitle + "']").exists()) {
        // append the new options to our results (this updates the display only of the second list box)
        jQuery("[id$='_SelectCandidate'][title^='" + pickerTitle + "']").children().detach().appendTo("[id$='_SelectResult'][title^='" + pickerTitle + "']");
        var data = jQuery("[id$='_SelectCandidate'][title^='" + pickerTitle + "']").closest("table").siblings("[id$='MultiLookupPicker_data']").val();
        data = data.replace( /\|t \|t /g , "");
        // append the new options to our hidden field (this sets the values into the list item when saving)   
        jQuery("[id$='_SelectCandidate'][title^='" + pickerTitle + "']").closest("table").siblings("[id$='MultiLookupPicker']").val(data);
    }
};

Tuesday, April 15, 2014

Using SharePoint CSOM to access a list's information using folder/internal name

Been quite some time since I posted anything but I could not find any place on the net that talked about getting a SharePoint list using the CSOM and the lists internal/folder name.   I finally came up with this kludge using the SPService, but basicly what I am doing is searching the default view URL for the list folder name.

The reason I needed to do this was I had a client request to setup alerts form one list that had URLS that pointed to another list. The alerts really needed to be on the referenced list and not what the user was currently selecting in the list view.

$().SPServices({
    operation: "GetListCollection",
    async: false,
    completefunc: function (xData, Status) {
        $(xData.responseXML).find("List").each(function() {
            if ($(this).attr("DefaultViewUrl").indexOf(listInternalName) >= 0) {
                 listName = $(this).attr("Title");
                 listId = $(this).attr("ID");
            }
        });
    }
});


Hope someone else finds this useful.

Thursday, June 7, 2012

Enabling in browser support for PDF


A very old post I forgot to publish about enabling SharePoint browser support of PDF


If you have a default SharePoint 2010 setup you would notice that when you go to open a Pdf file SharePoint prompts you to save it rather than opening.

The cause of this behavior is SharePoint 2010 Browser File Handling. This property is on SharePoint Web Application level and its value determines how files are treated in the browser. “Strict” specifies that MIME content types which are not listed in “AllowedInlineDownloadedMimeTypes” are forced to be downloaded. “Permissive” specifies that the HTML and other content types which might contain script are allowed to be displayed directly in the browser. Source: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebapplication.browserfilehandling.aspx


“AllowedInlineDownloadedMimeTypes” is a collection of MIME types. This list of MIME types does not contain MIME type of Pdf documents by default. It is important to understand that by adding Pdf MIME type to IIS settings you will not solve this issue.

Solution #1 (via User Interface):

The solution is to change Browser File Handling property on Web Application level. For that you need to be a Farm Administrator. Steps to change Browser File Handling property:

  • Go to SharePoint 2010 Central Administration > Application Management > Manage Web Applications
  • Select the row of your web application
  • Click General Settings in the ribbon
  • Scroll down to Browser File Handling and select Permissive
part1-screen2.png
  • Click Ok

Now, your Pdf document will be opened in the browser.

By default, option is set to Strict when creating a web application. When set to Strict, a Pdf document (and other file types) can only be Saved (downloaded), but in Permissive mode, the Pdf document can be opened in the browser by default (this is a setting you can change. See Document Library Settings -> Open in the Client).



Limitation #1:
Once you enabled “Permissive” Browser File Handling, SharePoint will allow all documents be opened in the browser. Today, there is no “out-of-the-box” option to allow Permissive option for Pdf documents only.

Monday, May 7, 2012

Migrate SharePoint 2010 My Sites from default Web Application

Recently I had a client that had setup their "My Sites" in the same web application as their default intranet and wanted to move them to a new web application.
I found several posts on how to exporting my sites like this one and migrating My Sites to a new Content Database but none that actually tacked the issue of moving to a new Web Application.
Well this turned out to be easier than I thought.

  1. Migrate the "My Sites" content to a new content database.
    $db = Get-SPContentDatabase WSS_Content
    Get-SPSite http://portal/personal/* -Limit ALL | where { $_.ContentDatabase -eq $db }
    Get-SPSite http://portal/personal/* -Limit ALL | where { $_.ContentDatabase -eq $db } | Move-SPSite -DestinationDatabase WSS_Content_My_Sites -Confirm:$false
    
  2. Remove this new content database from the current Web Application.
    In Central Admin -> Application Management -> Manage content databases. Select Content DB, in my case WSS_Content_My_Sites and select Remove content database


  3. Create a new Web Application to house the My Sites.
    Central Admin -> Application Management -> Managed web applications -> Select New from the ribbon.  There is no need to create a new site collection since we are going to drop the content database that will be created.
  4. Re-attach the content database previously removed to this new Web Application
    Central Admin -> Application Management -> Manage content databases.
    Make sure you select the correct Web Application and use the correct database name.

  5. Set the Self-Service Site Creation for the new My Site Web Application.
    Central Admin-> Application Manager -> Manage web application -> select site then Self-Service Site Creation in ribbon
  6. Update links in SharePoint to create new "My Sites" in this new location.
    Central Admin -> Application Management -> Manage service applications
    Select User Profile Service then Manage in the ribbon
    Under My Site Settings -> Setup My Sites -> update My Site Host location
  7. Remove the content database that was created with the new "My Site" Web Application

Wednesday, January 11, 2012

How to get a SharePoint person field's email adress

This week I had the need to access the email address of a user that was in a person field of a SharePoint list.

Well I incorrectly assumed that this was some flavor of  a SPUser object, turns out the person field in a SPList is a special type SPFieldUser.

Well lets cut to the chase this is the code to access the email address of a SPListItem person column;


private bool EmailUser(SPListItem spListItem, string recordColumn, string subject, string message)
        {
            SPFieldUser spFieldUser = (SPFieldUser)spListItem.Fields[recordColumn];
            SPFieldUserValue spFieldUserValue = (SPFieldUserValue)spFieldUser.GetFieldValue(
                                                    spListItem[recordColumn].ToString());
            if (string.IsNullOrEmpty(spFieldUserValue.User.Email) == false)
                return SPUtility.SendEmail(spListItem.Web, false, false, spFieldUserValue.User.Email, subject, message);
            else
                LogWrite("Error", string.Format("Send Email Error Message")
                                                ,spFieldUserValue.User.Name));
            return false;
        }