Scoping a SharePoint Feature for a Specific Web Application

This is something that came up at a client’s site yesterday, which I thought should be a trivial problem to solve. As always, it ended up being a little more complex than I originally anticipated. So everyone knows that a SharePoint solution can either be globally scoped, meaning that all web applications in the farm can leverage it, or it can be scoped at a specific web application. But what about features contained within these solutions?

Assume you have a custom .WSP solution that contains 1 feature and which is scoped for your web application on port 81 (e.g. http://localhost:81). One would assume that by default, if the solution is only made available to the Web Application on port 81 (scoped at web app level) that it would be the same for the feature contained within it, meaning that this feature, assuming it it scoped at the site collection level, would only be accessible by site collections within the Web Application contained in the Web Application on port 81. Well, in fact it is not. A feature is always made available to all web applications in a farm, even if its associated solution is targeted at a unique web application.

For this article, I will be using new SharePoint 2010 Foundation Single Server Farm. My farm contains two web applications: one deployed on port 80 and a second one deployed on port 81. From the following picture we can clearly see that I have a custom solution named “MySP2010Solution.wsp” scoped at the web application level and which has been deployed against my web application on port 80.

Webappscopedsolution

This solution contains a single feature, which in turn contains a web part. The feature is scoped at the site collection level. Since my solution was deployed against my web application on port 80, it is expected to see my feature show up in every site collection Under that web application:

featureport80

What one may not expect however, is to also find this feature available to every site collection in my web application hosted on port 81. After all, the solution was never deployed to it in the first place. However, navigating to the site collection feature page of a site collection Inside the web application on port 81 does show my feature and allows me to activate it.

featureport81

The following schema summarizes what is really happening in my environment:

schema

How it Works in the Background

In order to better understand why SharePoint is behaving this way, we need to first understand what really happens in the background when we deploy a solution that contains a feature. When deploying a solution that contains a feature, SharePoint automatically creates a folder in <14 Hive>\TEMPLATES\FEATURES for your custom feature contained within your solution:
Featurelocaldisk
Because this feature is stored physically on disk, it is by default made available by all web applications. Upon activating the feature from the Web interface, SharePoint will create an entry in the Features table of the content database associated with the current Web Application.

SQLFeatureTable

Options

So if you are in the business of managing several web applications for different clients, how can you let one client deploy a web application scoped solution without having it appear on all of your other clients’ web application? Well there are really 3 options made available to you:

1 – Hide the feature by default and activate it via PowerShell

The first option is to make your feature hidden by default, and then to use PowerShell to activate it manually only on the site collections that require it. This is what I consider a nasty workaround. The beauty about SharePoint feature is that you wish to let Site Collection admins manage the features to be activated themselves. With this approach, first off the site collection admins need to know that the feature exists because remember, it will be hidden from them via the web interface, and second they will need to contact the IT admin team so that they can activate it via PowerShell everytime they need a hidden feature activated. This adds a burden on both the site collection administators ad the IT admins responsible for SharePoint.

If you wish to make a feature hidden, simply navigate to its associated folder under <14 Hive>\TEMPLATE\FEATURES\, edit the Feature.xml by adding the property hidden=”true” to the Feature node. Save the file, and do an IISReset on the server. Remember to perform this on every server in the farm.
HideaFeatureSP

2 – Context Feature Logo

The second option is probably a even worst idea than the first one, but I list it here anyway in case it fits someone’s business scenario. Whenever you create a custom feature, you can specify a custom “logo” (icon) for it. By default, if no logo has been specified for a feature, SharePoint uses the default half spinwheel icon (GenericFeature.gif).
GenericFeatureicon

The idea here is to specify a custom icon for your feature, but that would be hosted at the root of each web application. Still unclear on what I mean right? Imagine we had two icons, a Green light icon, and a red “cancel” circle icon. We would ensure that the feature displays the cancel icon on all site collections contained within a Web Application for which we did not intent to deploy the feature onto, and the green icon on the ones where the feature was intended for the users to consume.

Let’s show you a graphical example of what I mean. By simply modifying the Feature.xml file for my feature and adding the following property to the Feature node

FeatureFakeIcon

I was able to tell SharePoint to go and check the root of the current Web Application for a Library called “Icons”, which contains a file named MySPWebPart.png file and to use that image as my feature icon. In my scenario, if a user is to navigate to the Site Collection feature page on the Web Application hosted on port 80 (where the feature was intended for) they would be presented with the green light icon, meaning that this feature is meant for them to consume:

GoodFeatureCanConsume

However, if a user was to navigate to the site collection feature page on a site collection hosted within the Web Application on port 81 (whee the feature was not intended to be deployed). they would be presented with the cancel icon, meaning that this feature is not intended for them to consume:

BadFeatureDoNotConsume

This solution can cause a LOT of maintenance nightmare for your IT Admin team, and relies 100% on the user’s discipline to not activate features that are not meant for them to consume. It salso requires you to create individual icons for every feature you create and to have them stored in the icons Library of each root web of your Web Applications.

3 – Make your Feature Dépendent of a Web Application Scoped One

This solution is by far the best option you have. You could even combine it with Option #2 to get even more out of it for your clients. The idea here is to create an empty Feature, have it scoped at the Web Application level, and make all other features dépendent on it. For example, if we wish to make sure the features contained within our custom MySP2010Solution.wsp can only be activated on site collections contained within the Web Application hosted on port 80, we can make our custom feature (containing our web part) dependent on a web application scoped feature that would only be activated on that web application. Users who would navigate to the site collection feature page on the Web Application hosted on port 81 would still see the feature, but as soon as they would try to activate it, SharePoint would present them with a message saying that a dependent feature was not activated.

Let’s have a closer look at that solution. Within Visual Studio, add a new feature to your project (let’s call it Controller for Web App Port 80) and scope it at the Web Application level.
ControllerFeature

Then open the Feature Wizard for the feature that contains our Web Part. At the bottom of the main screen, there’s a section called Feature activation dependencies. Expand that section and click on the Add… button.
Featureependency

You will be brought to a dialog box that let’s you pick any of the other features included in the current solution, and make your feature dependent on them. From the list, select the “Controller for Web App Port 80” Feature we just created and click on the Add button
pickdependency

You can now go ahead and deploy your solution. By default the feature will be made available to all web applications in your farm, and can be activated from anywhere. What we need to do now, is go in central administration and de-activate the Controller for Web App Port 80 feature on the Web Application hosted on port 81. To do this, in central administration, navigate to Application Management > Manage Web Applications. From the list of web application, pick the Web Application that is hosted on port 81, and in the ribbon, click on the Manage Features button.
ManageWebAppFeatureCA
Clicking this button will launch a dialog window listing all features that have been activated against this Web Application. Make sure the Controller for Web App Port 80 is deactivated.

ControllerDeactivated

By deactivating this feature you prevent anyone from being allowed to enable dependent features on the Web Application hosted on port 81. Even if the feature is still listed in the site collection feature, anyone trying to activate this feature on that web application will get a message from SharePoint:

Preventactivationmessage

While this may not be a pretty option still, it at least prevents the feature from being activated within a Web Application it was not intended for.

Installing SharePoint 2016 Release Candidate (RC)

So you heard the news? SharePoint 2016 officially hit Release Candidate (RC) yesterday, January 20th 2016. This means that we are getting really close to an RTM release. If you are like me and always want to be the first one to play with the new tools as soon as Microsoft relases them, you may be in for a little surprise. If you try to go an get the bits that were released by Microsoft yesterday from the download center (https://www.microsoft.com/en-us/download/details.aspx?id=50737) you will soon realize that this is no traditional SharePoint download. The package on Microsoft download centre is actually called SharePoint Server 2016 Release Candidate Patch and consists of at least two main downloads: one for the language you are using and the second is called a Global patch and patches the entire environment. So it’s a patch….but what is it patching? A patch has to be installed on an existing product right? Right! The SharePoint 2016 Release Candidate (RC) has to be installed on top of the SharePoint 2016 Beta 2 (https://www.microsoft.com/en-us/download/details.aspx?id=49961).

This new update process introduced in SharePoint 2016 is called the Zero Downtime Patching and allows users who have High Available SharePoint environments to patch their farms without causing any downtime. For the purpose of this article, I will guide you through the process of installing the SharePoint 2016 RC, but as always, to keep things simple, I will be patching a Single Server Farm environment. Because I am applying my patches on a Single Server Farm, I will not be able to take advantage of the Zero Downtime Patching process. I will be using the psconfiggui.exe wizard to complete the patching process which will cause downtime in my farm. Normally, what you wish to do is take off one of your load balanced server from your farm, patch it, re-introduce it back within the farm, and take the second server down for patching.

Environment Overview
– Single Server Farm (18 Gb of RAM);
– Connected to the contoso.com domain (Domain Controller is a separate Virtual Machine);
– SQL Server 2014 Enterprise is installed locally on my VM;
– SharePoint Server 2016 Beta 2 is already installed on my VM;

Patching Process
You should be able to verify that you have SharePoint Server 2016 Beta 2 installed in your environment by going Central Administration > Upgrade and Migration > Check product and patch installation status. The build version should be 16.0.4306.1002.
SP2016RCManagePatch-Beta2

Now, in my environment I don’t have any language pack installed. Therefore I only need to download two patch zip packages from the Download Center:

– SharePoint Server 2016 Release Candidate English Prerequisite Installer;
– SharePoint Server 2016 Release Candidate Global Patch;

Once you have all your packages downloaded, make sure you extract both onto your SharePoint Server (in my case extracted to C:\CUs\). We will start by patching the Prerequisite Installer.

1 – Execute the Prerequisites patch: c:\CUs\SharePoint Server 2016 Release Candidate English Prerequisite Installer\prerequisiteinstaller.exe

Prereqs1

Prereq2

Prereq3

Prereqs1
After a while, the prerequisites installer may ask you to reboot your server. In my case, it was the installation of .NET 4.6 that required it. Simply click finish and wait for the server to reboot. One it reboots, the prerequisites installer will start again automaticallly and finish its installation.

Prereq4

2 – Install the global patch by executing C:\CUs\SharePoint Server 2016 Release Candidate Global Patch\sts.msp
sts1

After a while it may prompt you to restart the App Fabric Service
Appfabricshutdown
Then it will go on and continue its installation
Progresspatch
One thing to note is that whenever the patch has finished installing, the executable (.msp) simply closes itself, you won’t get any GUI telling you that it was installed successfully.

At this point, the patch has been installed, but is not yet applied to the farm. If you were to go back to Central Administration at this point, you would see the following:
ProductPatched

3 – Run PSConfig to finlize the patching process
psconfig1

psconfig2
psconfig3

psconfig4

psconfig5

Off you go, you now have your environment running the SharePoint Server 2016 Release Candidate.

PowerShell DSC Pull Server for SharePoint

In the DSC world, there are two modes: Pull or Push. The first one means that the node servers will connect to a centrally located server called the DSC Pull Server and get all the information from it. The Local Configuration Manager (LCM) component running on the nodes will ping the pull server at a specific interval to check if the Desired State has changed and to ensure their current configuration still complies with the desired state. The second mode, Push DSC means that the DSC configuration files will be manually pushed and executed on the server node.

In an enterprise context, DSC really takes all its sense when you deploy server nodes in Pull mode. Let’s take a minute to think about what DSC Pull has to offer to us SharePoint people. You set up a central server that will contain all the required files including all the prerequisites, the SharePoint binaries & cummulative updates, define the desired configuration state for all dependent servers and have them automatically comply. Whenever a new cummulative update is released, if you wish to push it to all your SharePoint farms, all you have to do is put the binaries on the Pull server and update the DSC configuration for all SharePoint servers for which you wish to apply the Cummulative Update to. This offers a lot of potential!

Another interesting scenario would be to ensure that the default configurations for the severs don’t get changed by users without the IT department first approving the change. Imagine for a minute that a power user has access to the SharePoint central administration and decided to put all diagnostics logs to verbose to troubleshoot a problem. Sure you may want to allow users to do this, but you need a way to ensure that this setting doesn’t stay forever, otherwise you risk flooding the disks. Using DSC pull, you could specify that the desired configuration for your SharePoint farm is to have the Desired State Configuration set to a default diagnostic log level. You could then configure the Local Configuration Manager component of your SharePoint server to check for compliance with the Desired State overnight, and have any configuration that steered away from the default set back automatically. That way if the user forgets to turn off verbose in the log, PowerShell DSC would automatically care of turning it off over night.

As mentionned previously, it is the Local Configuration Manager (LCM) component who’s responsible for connecting to the central pull server and apply the desired state locally. The LCM can be configured in 3 different ways:

a) ApplyOnly: which tells the LCM to simply apply the desired configuration once, and then to do nothing even if the server steers away from the desired state obtained from the pull server.

b) ApplyAndMonitor: which tells the LCM to apply the desired configuration once, and then if the server steers away from the desired state, to simply reports discrepancies in the logs. We could then use a tool like System Center to send notifications to the IT team when a server node is no longer in the desired state, allowing them to go an contact the users who changed the desired state to learn more about why a specific change was made.

c) ApplyAndAutocorrect: which tells the LCM to apply the desired configuration, and whenever it detects that the server is no longer in a desired state to automatically fix it so that it becomes compliant with the desired state. Using this mode, the LCM will still report all discrepancies in the logs.

For the purpose of this article, we will be using the ApplyAndAutocorrect mode for the LCM. Using this mode, the LCM will ping the pull server on a regular basis to determine if it steered away from the desired state. The frequency at which the LCM on the server node will ping the DSC pull server is determined by the ConfigurationModeFrequencyMins which specified in minutes at what frequency to ping the pull server. By default the LCM will ping the pull server every 15 minutes.

In this article I will cover the basis of how you can create a DSC pull server in your environment to manage your SharePoint farms. All examples will be using the xSharePoint DSC resources maintained on GitHub (http://www.GitHub.com/PowerShell/xSharePoint/).

Environment Overview:
The environment used in this article is all Hyper-V based. My DSC pull server is running Windows Server 2012 R2 and runs the Windows Management Framework 5.0 Preview February 2015. It is joined to the contoso.com domain. I’ve assigned 4Gb of RAM to the DSC Server, but it could be brought down to as low as 2Gb of RAM.

xPSDesiredStateConfiguration:
Talk about a way to eat your own dog food, the best way to configure a new DSC Pull Server is by leveraging a DSC resource called xPSDesiredStateConfiguration which allows you to configure you server node as a DSC pull server…using DSC push. That’s right, in order to configure a DSC pull server node, you need to use DSC push mode.

1 – Acquire the xPSDesiredStateConfiguration resource. Run the following PowerShell line (remember I’m running PowerShell 5)
Install-Module xPSDesiredStateConfiguration
Install-ModuleDSC

Install DSC Service:
Windows Server 2012 R2 offers a Windows service called PowerShell Desired State Configuration Service. This is the underlying service for our DSC Pull Server.

2 – Install the PowerShell Desired State Configuration Service on your DSC Pull Server. Launch Server Manager, and in the Features tab, under Windows PowerShell, select Windows PowerShell Desired State Configuration Service. Leave all default options on.
AddDSCService
AddFeatures
InstallNext
InstallNext2
InstallNext3
InstallNext4
InstallNext5

Write DSC Configuration Script for the Pull Server:
As I mentionned previously, we will be using DSC push to configure our Pull Server. Now that we have the xPSDesiredStateConfiguration resource installed on our server, we will go ahead and generate the configuration MOF files required to initate the DSC push configuration.

3 – Save the following PowerShell script and execute it. In my case I saved it under c:\Scripts\DSCPull.ps1

configuration DSCPullServer {
    param (
        [string[]]$ComputerName = 'localhost'
    )

    Import-DSCResource -ModuleName xPSDesiredStateConfiguration

    Node $ComputerName {
        WindowsFeature DSCServiceFeature {
            Ensure = 'Present'
            Name   = 'DSC-Service'
        }

        xDscWebService DSCPullServer {
            Ensure                  = 'Present'
            EndpointName            = 'DSCPullServer'
            Port                    = 8080
            PhysicalPath            = 'C:\inetpub\wwwroot\DSCPullServer'
            CertificateThumbPrint   = 'AllowUnencryptedTraffic'
            State                   = 'Started'
            DependsOn               = '[WindowsFeature]DSCServiceFeature'
        }

        xDscWebService DSCComplianceServer {
            Ensure                  = 'Present'
            EndpointName            = 'DSCComplianceServer'
            Port                    = 9080
            PhysicalPath            = 'C:\inetpub\wwwroot\DSCComplianceServer'
            CertificateThumbPrint   = 'AllowUnencryptedTraffic'
            State                   = 'Started'
            IsComplianceServer      = $true
            DependsOn               = ('[WindowsFeature]DSCServiceFeature','[xDSCWebService]DSCPullServer')
        }
    }
}

DSCPullServer
Start-DSCConfiguration .\DSCPullServer -Wait

DSCConfig

Running the above script will generate a MOF file and will initiate the DSC configuration. Once completed, you should be abe to access the service endpoint by opening a new web browser on the pull server and navigating to http://localhost:8080/PSDSCPullServer.svc
SvcTest

At this point we have an up and running DSC Pull Server.

Create a file share for the SharePoint binaries:

What we need to do next is to create a central repository on our Pull Server where all binaries required by our SharePoint server nodes will be located. These servers will connect to the pull server and download the binaries they require in order to obtain their desired state from it. Ideally, this file share would be setup on a separate server, but for demo purposes, I will create the file share directly on my pull server. The UNC path for my file share will be \\\Share (in my case \\WIN-Q09MN73DJCC\Share).

4 – Create a new folder in your new shared location called SharePoint2016Beta2. Copy all the binaries from the SharePoint 2016 beta 2 media in that folder.

5 – Create a second folder directly Under the root of your shared folder (\\\Share\) and name it SharePoint2016Prereqs. Download all SP2016 prerequisites and put them in that folder. These files can be downloaded from my OneDrive at https://onedrive.live.com/redir?resid=F745FFDFD97324BA!111001&authkey=!AH8Z2IkRwFIJBVs&ithint=file%2czip

Obtain the required SharePoint DSC Resources and make them available:
The next step for us is to obtain a local copy of all the DSC resources our SharePoint DSC process depends on: xDisk (not required in this demo), xCredSSP, xWebAdministration and off course xSharePoint. In a DSC pull mode, resources are made available to node servers by being placed in the C:\Program Files\WindowsPowerShell\DscService\Modules\. The Local Configuration Manager on the server node, upon detecting a missing DSC resource will automatically go look in that folder on the Pull Server to try to download it and install it locally (assuming you kept the default values when configuring the DSC Pull server).

While one may think sharing resources from a pull server would be easy, it is actually quite a complex process. DSC resources are iterative (the reason why most of them have an ‘x’ before their name is for experimental). Because of this, we very often run into situations in an enterprise where two server nodes, connected to the same pull server will require a different version of the same DSC resource. To solve this problem, Microsoft came up with a naming convention for all resources made available by a pull server: ModuleName_Version.zip. As you’ve noted, DSC resources to be shared with server node are contained within a ZIP file to reduce the overall size of files to be transferred and to package the various files included as part of the resource into a single file. Also, for every DSC resource you wish to share, you will need to create a new Checksum for it (ModuleName_Vesion.zip.checksum).

6 – Obtain the xDisk DSC resource by running the following PowerShelll line:

Install-Module xDisk

InstallXdisk

7 – Create a shareable resource for xDisk. Navigate to C:\Program Files\WindowsPowerShell\Modules\xDisk and open file xDisk.psd1. In the file, look for line ModuleVersion=.
xDiskModuleVersion
This version number will be used in naming our shareable DSC resource. In my case, the xDisk resource is at version 1.0. Navigate back to C:\Program Files\WindowsPowerShell\Modules\ and create a ZIP file containing the xDisk resource. Once created, rename that file xDisk_1.0.zip.
xDiskZip
We now need to move that ZIP file to a location where it will be accessible by the DSC Pull Server Service we created earlier. Move the ZIP file to the following location on the Pull Server C:\Program Files\WindowsPowerShell\DscService\Modules

xDiskShareable

Congrats, you’ve now created your first DSC Pull Server shareable DSC resource.

8 – Using the same process as detailed in step 6 and 7, create a shareable DSC resource for xWebAdministration, xCredSSP and xSharePoint.
DSCResourcesAllShareable

9 – Create a checksum for each shareable resource. This can be achieved in batch using the following PowerShell line of code:

Get-ChildItem "C:\Program Files\WindowsPowerShell\DscService\Modules" -File | Foreach{New-DSCCheckSum -Path $_.FullName}

DSCChecksums

Create MOF files for your server nodes
I will no go into the details of how to create a new MOF file using xSharePoint to configure your SharePoint farm in this article. I have already written another article a few months ago that will guide you through the process of creating a Single Server Farm for SharePoint 2016. Throughout the next section of the present article, I will be using the MOF file resulting from that previous article.

10 – Make sure you understand how to go about generating a MOF file representing the Desired State Configuration for your given server by reading through my previous article: Installing SharePoint 2016 Beta 2 using PowerShell Desired State Configuration

For this article, the DSC configuration script I will be using will be different than the one used in my previous article for several reasons. First, I want to specify that the binary path for my SharePoint binaries are located on the Pull Server. Therefore I will need to add a new specification in my DSC configuration script that will initiate the download of the binaries onto a local folder on my server node. Secondly, I will need to add a component to the LocalConfigurationManager node to tell the server where to obtain its desired state (from the pull server). I will still be creating a single server farm in order to keep thing simple. My server node (SP2016DSC) is connected to my contoso.com domain and already has a local copy of SQL Server 2014 installed on it (this just makes the Learning process easier for the sake of this article). Here is the resulting scaled down script. Executing it will automatically generate your associated file. Please note that in order to actually execute the DSC script to generate the MOF files, you need to run the script on a server that has the dependant DSC resources installed. This means that trying to execute this script to generate the MOF files on the server to be configured is never going to work since it most likely won’t have any of the DSC resources on it. My recommendation is to always run these onto the pull server since it will always have the dependent resources installed.

Configuration SP2016DSC
{
    param (
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [string] $CredSSPDelegates,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [PSCredential] $SPBinaryPathCredential,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [PSCredential] $FarmAccount,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [PSCredential] $InstallAccount,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [string] $ProductKey,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [string] $FarmPassPhrase,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [PSCredential] $WebPoolManagedAccount,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [PSCredential] $ServicePoolManagedAccount,
    [Parameter(Mandatory=$true)] [ValidateNotNullorEmpty()] [string] $WebAppUrl
    )
    Import-DscResource -ModuleName xSharePoint
    Import-DscResource -ModuleName xWebAdministration
    Import-DscResource -ModuleName xCredSSP
    Import-DscResource -ModuleName xDisk

    node "SP2016DSC"
    { 
        xCredSSP CredSSPServer { Ensure = "Present"; Role = "Server" }
        xCredSSP CredSSPClient { Ensure = "Present"; Role = "Client"; DelegateComputers = $CredSSPDelegates }
 
        File SPBinaryDownload
        {
            DestinationPath = "C:\SPInstall"
            Credential = $SPBinaryPathCredential
            Ensure = "Present"
            SourcePath = "\\WIN-Q09MN73DJCC\Share\SharePoint2016Beta2"
            Type = "Directory"
            Recurse = $true
        }

        File SPBinaryPrereqDownload
        {
            DestinationPath = "C:\SPInstall\prerequisiteinstallerfiles\"
            Credential = $SPBinaryPathCredential
            Ensure = "Present"
            SourcePath = "\\WIN-Q09MN73DJCC\Share\SharePoint2016Prereqs"
            Type = "Directory"
            Recurse = $true
        }

        xSPInstallPrereqs InstallPrerequisites
        {
            InstallerPath = "C:\SPInstall\Prerequisiteinstaller.exe"
            OnlineMode = $false
            SQLNCli = "C:\SPInstall\prerequisiteinstallerfiles\sqlncli.msi"
            PowerShell = "C:\SPInstall\prerequisiteinstallerfiles\Windows6.1-KB2506143-x64.msu"
            NETFX = "C:\SPInstall\prerequisiteinstallerfiles\dotNetFx45_Full_setup.exe"
            IDFX = "C:\SPInstall\prerequisiteinstallerfiles\Windows6.1-KB974405-x64.msu"
            Sync = "C:\SPInstall\prerequisiteinstallerfiles\Synchronization.msi"
            AppFabric = "C:\SPInstall\prerequisiteinstallerfiles\WindowsServerAppFabricSetup_x64.exe"
            IDFX11 = "C:\SPInstall\prerequisiteinstallerfiles\MicrosoftIdentityExtensions-64.msi"
            MSIPCClient = "C:\SPInstall\prerequisiteinstallerfiles\setup_msipc_x64.exe"
            WCFDataServices = "C:\SPInstall\prerequisiteinstallerfiles\WcfDataServices.exe"
            KB2671763 = "C:\SPInstall\prerequisiteinstallerfiles\AppFabric1.1-RTM-KB2671763-x64-ENU.exe"
            KB3092423 = "C:\SPInstall\prerequisiteinstallerfiles\AppFabric-KB3092423-x64-ENU.exe"
            KB2898850 = "C:\SPInstall\prerequisiteinstallerfiles\Windows8.1-KB2898850-x64.msu"
            WCFDataServices56 = "C:\SPInstall\prerequisiteinstallerfiles\WcfDataServices56.exe"
            MSVCRT11 = "C:\SPInstall\prerequisiteinstallerfiles\vcredist_x64.exe"
            MSVCRT14 = "C:\SPInstall\prerequisiteinstallerfiles\vcredist_x64-v14.exe"
            ODBC = "C:\SPInstall\prerequisiteinstallerfiles\msodbcsql.msi"
            DotNet452 = "C:\SPInstall\prerequisiteinstallerfiles\NDP452-KB2901907-x86-x64-AllOS-ENU.exe"
            Ensure = "Present"
        }
 
        xSPInstall InstallBinaries
        {
            BinaryDir = "C:\SPInstall"
            ProductKey = $ProductKey
            Ensure = "Present"
            DependsOn = "[xSPInstallPrereqs]InstallPrerequisites"
        }

        xWebAppPool RemoveDotNet2Pool { Name = ".NET v2.0"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites" }
        xWebAppPool RemoveDotNet2ClassicPool { Name = ".NET v2.0 Classic"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites" }
        xWebAppPool RemoveDotNet45Pool { Name = ".NET v4.5"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites"; }
        xWebAppPool RemoveDotNet45ClassicPool { Name = ".NET v4.5 Classic"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites"; }
        xWebAppPool RemoveClassicDotNetPool { Name = "Classic .NET AppPool"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites" }
        xWebAppPool RemoveDefaultAppPool { Name = "DefaultAppPool"; Ensure = "Absent"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites" }
        xWebSite RemoveDefaultWebSite { Name = "Default Web Site"; Ensure = "Absent"; PhysicalPath = "C:\inetpub\wwwroot"; DependsOn = "[xSPInstallPrereqs]InstallPrerequisites" }

        xSPCreateFarm CreateSPFarm
        {
            DatabaseServer = "SP2016DSC"
            FarmConfigDatabaseName = "SP_Config"
            Passphrase = $FarmPassPhrase
            FarmAccount = $FarmAccount
            InstallAccount = $InstallAccount
            AdminContentDatabaseName = "SP_AdminContent"
            DependsOn = "[xSPInstall]InstallBinaries"
        }
 
        xSPManagedAccount ServicePoolManagedAccount
        {
            AccountName = $ServicePoolManagedAccount.UserName
            Account = $ServicePoolManagedAccount
            Schedule = ""
            InstallAccount = $InstallAccount
           DependsOn = "[xSPCreateFarm]CreateSPFarm"
        }
 
        xSPManagedAccount WebPoolManagedAccount
        {
            AccountName = $WebPoolManagedAccount.UserName
            Account = $WebPoolManagedAccount
            Schedule = ""
            InstallAccount = $InstallAccount
            DependsOn = "[xSPCreateFarm]CreateSPFarm"
        }

        xSPWebApplication HostNameSiteCollectionWebApp
        {
            Name = "SharePoint Sites"
            ApplicationPool = "SharePoint Sites"
            ApplicationPoolAccount = $WebPoolManagedAccount.UserName
            AllowAnonymous = $false
            AuthenticationMethod = "NTLM"
            DatabaseName = "SP_Content_01"
            DatabaseServer = $DatabaseServer
            Url = $WebAppUrl
            Port = 80
            InstallAccount = $InstallAccount
            DependsOn = "[xSPManagedAccount]WebPoolManagedAccount"
        }
    }
} 

$CredSSPDelegates = "*.contoso.com"
$SPBinaryPathCredential = Get-Credential -UserName "contoso\administrator" -Message "Administrator"
$FarmAccount = Get-Credential -UserName "contoso\sp_farm" -Message "SP_Farm"
$InstallAccount = Get-Credential -UserName "contoso\sp_install" -Message "SP_Install"
$ProductKey ="NQGJR-63HC8-XCRQH-MYVCH-3J3QR"
$FarmPassPhrase = "pass@word1"
$WebPoolManagedAccount = Get-Credential -UserName "contoso\sp_webapp" -Message "SP_WebApp"
$ServicePoolManagedaccount = Get-Credential -UserName "contoso\sp_serviceapps" -Message "SP_ServiceApps"
$WebAppUrl = "SP2016-DSCTBD"
$ConfigData = @{
    AllNodes = @(
    @{
        NodeName = "SP2016DSC"
        PSDscAllowPlainTextPassword = $true
    })
}
SP2016DSC -ConfigurationData $ConfigData -CredSSPDelegates $CredSSPDelegates -SPBinaryPathCredential $SPBinaryPathCredential -FarmAccount $FarmAccount -InstallAccount $InstallAccount -ProductKey $ProductKey -FarmPassPhrase $FarmPassPhrase -WebPoolManagedAccount $WebPoolManagedAccount -ServicePoolManagedAccount $ServicePoolManagedAccount -WebAppUrl $WebAppurl

In the DSC pull server world, MOF files are always referenced to by a GUID. Meaning that every MOF file you place on a DSC pull server will always be named .mof.

11 – Rename your MOF file using a GUID. In the DSC pull server world, MOF files are always referenced to by a GUID. Meaning that every MOF file you place on a DSC pull server will always be named .mof. Using PowerShell, you can run the following line of PowerShell:

[guid]::NewGuid() 

This will output a new GUID that you will need to copy and rename your MOF file with.

Renamemoftoguid

Once you have the GUID named MOF files, place them into the following directory on your DSC Pull Server: C:\Program Files\WindowsPowerShell\DscService\Configuration
MoveMOFFiletodscservice

12 – Generate the MOF checksum. Just like we did with the shareable resources, MOF files require that you create a checksum for them. To achieve this, simply run the following line of PowerShell code:

Get-ChildItem "C:\Program Files\WindowsPowerShell\DscService\Configuration" -File | Foreach{New-DSCCheckSum -Path $_.FullName}

mofchecksum

Initiate the configuration
We are now ready to initiate the configuration of our server node. To achieve this, we simply need to configure the Local Configuration Manager (LCM) component of the node server to have it connect to our pull server. We will do this by creating a new DSC configuration thatwill take care of configuring the LCM.

12 – Configure the Local Configuration Manager (LCM) on the server node by executing the following PowerShell script. Please note that the UID parameter here refers to the GUID of our associated MOF file on the pull server.

Configuration SetPullMode
{
    param([string]$guid)
    Node SP2016DSC
    {
        LocalConfigurationManager
        {
            ConfigurationMode = "ApplyAndAutocorrect"
            ConfigurationID = $guid
            RefreshMode = "Pull"
            DownloadManagerName = "WebDownloadManager"
            DownloadManagerCustomData = @{
                ServerUrl = "http://WIN-Q09MN73DJCC:8080/PSDSCPullServer.svc";
                AllowUnsecureConnection = "true"
            }
            RebootNodeIfNeeded = $true
        }
    }
}

SetPullMode –guid "a38403ef-1a0c-49b7-aa78-3109517c08f9"

Set-DSCLocalConfigurationManager –Computer SP2016DSC -Path ./SetPullMode –Verbose

SetPullMode

Once the LCM has been configured, I recommend you reboot the server. After the reboot the configuration process will automatically start. After a minute or two, you should start to see the SharePoint binaries download initiate (a new SPInstall folder will get created at the root of your c:\)

FoldersAppear

The last thing left now is to be patient. The server node will automatically configure itself and by the end of the process, you will have a complete working SharePoint 2016 singl server farm!