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!

Microsoft Premier Field Engineer – SharePoint

2 thoughts on “PowerShell DSC Pull Server for SharePoint

  1. Hilton Giesenow

    Hey Nik,

    I’m hitting a wierd one installing a 2016 dev machine and wanted to check something with you. Even if I run New-SPConfigurationDatabase directly in PoSh, it keeps giving me an error, after already running for a minute or two, about a missing password. I have a suspicion it -might- be because I’ve installed WMF 5. Have you installed SP 2016 on a machine with PoSh 5 installed by any chance?

Leave a Comment

Your email address will not be published. Required fields are marked *

*
*