What’s New in SharePointDSC.Reverse 1.9.0.0

The latest release of the ReverseDSC Orchestrator Script for SharePoint is the biggest release we’ve had so far and it is packed with new features that will make it even easier for users to migrate their on-premises SharePoint 2013 or 2016 Farms from their on-premises data centers to the cloud. This release drastically changes the way the orchestrator script does its extraction. The following is a complete list of all improvements that have been included in the latest release.

New Features

  • Configuration Data: A new ConfigurationData.psd1 file will be generated with all extractions. This external .psd1 file exposes all parameters that are specific to the source SharePoint environment. If you are planning on deploying a copy of the extracted SharePoint Farm into a new environment (Azure, on-premises, etc.), all you need to do is update the values contained in that file to reflect the destination environment. For more information regarding what parameters are now exposed in the Configuration Data, please refer to the Configuration Data wiki Page.
  • Extraction Modes: Previous versions of the Orchestrator Script would expose a -Lite switch, which allow the extraction process to skip certain DSC Resources such as the Timer Job States, Features, SPWeb, etc. In version 1.9.0.0, we introduce the concept of Extraction Modes, where each mode will capture a different set of DSC resources. to learn more about what set of DSC Resource will be extracted in each mode, please refer to Extraction Mode wiki page.
  • Full Installation: Previous versions of the Orchestrator Script extracted resources starting at the SPFarm level, meaning that the machines you were trying to replicate your environment on had to have the SharePoint bits installed on them already for the configuration to get applied. With the new version, the SPInstallPrereqs and SPInstall resources are automatically included in the configuration file. The Configuration Data files drives whether or not these are included in the compiled MOF file using the FullInstallation. parameter. If this parameter is set to $True in the configuration data, then the compiled MOF instances will attempt to install the SharePoint Prerequisites (using the Online Mode) and the binaries on the servers being configured.

New Resources Supported

SharePointDSC.Reverse 1.9.0.0 introduces support for the following Resources:

  • SPInstallPrereqs
  • SPInstall
  • SPMachineTranslationServiceApp

Deprecated Features

the following switches have been deprecated with the 1.9.0.0 Release. The equivalent result can now be achieved 9in most part) by using the -Mode Lite parameter.

  • -Lite
  • -SkipFeatures
  • -SkipWebs
  • -SkipHealthRules
  • -SkipSearchFileType
  • -SkipTimerJobState
  • -SkipUserProfile

ReverseDSC for Files and Registry

As mentioned in multiple blog article I wrote in the past, ReverseDSC is technology agnostic, meaning that it can reverse engineer any component (Windows, Linux, MacOS, etc.) as long as there is a DSC module that exists for it. What we refer to as Orchestrator Scripts is what is technology specific and does specific operations for a specific component. You’ve seen me blog mainly about the SharePointDSC.Reverse Orchestrator Script lately, and show users how they can extract configuration of existing SharePoint Farms and replicate them elsewhere. There are however several other Orchestrator Scripts being developed at the moment including the SQLServer.Reverse one and the PSDesiredStateConfiguration.Reverse one that allows you to replicate file and registry structured on other machines using DSC.

In this article, I want to demo how the PSDesiredStateConfiguration.Reverse Orchestrator Script can be used to extract a DSC Configuration script. While the Orchestrator Script allows you to extract numerous components of an environment (Windows Features, Logs, Archives, Users, etc.) we will only focus on Files and Registry Entries within this article.

How to Install

Just like all other Orchestrator Scripts we are currently working on, the PSDesiredStateConfiguration.Reverse script is made available in the PowerShell Gallery. You can download and install it from any machine that has internet connectivity by executing the following lines of PowerShell (needs WMF 5.0).

Install-Script PSDesiredStateConfiguration.Reverse -Force

If you don’t have internet connectivity on your machine or if you don’t have access to a machine with WMF 5.0+ on it, then you will need to manually download the script for the GitHub Repository from a machine that has internet connectivity and copy it manually onto the machine. By default, the PowerShell code above downloads the script under c:\Program Files\WindowsPowerShell\Scripts. I would therefore recommend copying the script under that folder (create the Scripts folder if it doesn’t already exist).

ReverseDSC for Files

In this section we will cover the process of using the PSDesiredStateConfiguration.Reverse Orchestrator Script to extract a file structure from an existing environment as a DSC configuration script that we can use to recreate that exact file structure onto another environment. The challenge here is that in order for this file structure to be recreated on a new computer, we need to have access to the files from a central location. Let’s take the following example where I wish to extract the File structure in the path C:\MyCustomApplication from my machine.

MyCustomApplication file Structure

MyCustomApplication file Structure

The files are currently local to the machine. If we were to simply extract the file structure as a DSC Configuration Script, it would result in DSC Resources blocks similar to the following:

File 469e3fe0-b1c6-4b70-bc69-725dd2bc3739
{
    DestinationPath = 'C:\MyCustomApplication';
    Type = 'Directory';
    Recurse = $True;
    SourcePath = 'C:\MyCustomApplication';
    Ensure = 'Present';
}

This code tells DSC on the new machine to copy all files that exist under SourcePath (C:\MyCustomApplication) under the DestinationPath (C:\MyCustomApplication). Now, let’s forget that both paths are the same for one moment and try to understand the issue here. The new machine we are trying to configure with DSC knows nothing about the one we ran ReverseDSC on. It doesn’t have access to the original file repository where all the .dll, .chm, and .exe are located. Therefore we need to make sure these files we want to copy over our new machine are made available either on a network drive or with a Share from the machine we ran ReverseDSC on.

In order to solve this issue and to make it easy for users to extract a DSC Configuration script that can easily be leverage on other machines, the script accepts and array of HashTable values for the Folders parameter. The HashTable defines the following values:

Key Description
Source Root URL of the folder to scan on the local machine
SharedSource Root URL of the Shared folder or network location where the files to be copied are located.

To go back to our example above, I will be using the following values when calling the script:

Key Value
Source C:\MyCustomApplication
SharedSource \\NetworkShare\MyCustomApplication

I will then be calling the PSDesiredStateConfiguration.Reverse.ps1 script as follow:

.\PSDesiredStateConfiguration.Reverse.ps1 -Folders @{Source="C:\MyCustomApplication";SharedSource="\\NetworkShare\MyCustomApplication"}

Doing so will then result in the DSC Resource blocks having the DestinationPath match what was on the original server we ran ReverseDSC onto, and the SourcePath pointing to the shared location as follow:

File 617c1dc1-a7f6-48b6-bd69-24b4cefe2d21
{
    DestinationPath = 'C:\MyCustomApplication\Bin\Core.dll';
    Type = 'File';
    SourcePath = '\\NetworkShare\MyCustomApplication\Bin\Core.dll';
    Ensure = 'Present';
}

Upon compiling the DSC Configuration Script into a MOF file and initiating the DSC configuration on the new machine, the machine will automatically copy the files from the shared location to a local path matching the original machine on which we ran ReverseDSC onto.

It is also important to note that as mentioned above, the Folders parameter is actually an array of HashTable, meaning that multiple values could be passed to it. Assuming you had two folders you wanted to extract, you code do it as follow:

.\PSDesiredStateConfiguration.Reverse.ps1 -Folders @(@{Source="C:\Folder1";SharedSource="\\NetworkShare\Folder1"}, @{Source="C:\Folder2";SharedSource="\\NetworkShare\Folder2"})

ReverseDSC for Registry Keys

The process to use the PSDesiredStateConfiguration.Reverse Orchestrator Script against some Registry Keys is very similar to the Files process described above. The Script accepts a parameter named RegistryPaths that is an array of string that contains the paths to the Parent Registry key you wish to extract. The Registry Key paths can be entered either as the long or short form (e.g. HKLM:\System or HKEY_LOCAL_MACHINE\System).

Assuming I wanted to extract everything under the following Registry Paths:

  • HKLM:\System\CurrentControlSet\Control\Lsa
  • HKLM:\Software\dotnet

All I would need to do is call the script as follow:

.\PSDesiredStateConfiguration.Reverse.ps1 -RegistryPaths @("HKLM:\System\CurrentControlSet\Control\Lsa", "HKLM:\Software\dotnet")

That would then produce an output having DSC Resource Blocks similar to the following:

Registry 23e6bc1f-2f88-4b14-afbe-dddd8e3aa3e6
{
    ValueName = "NoLmHash";
    Key = "HKLM:\System\CurrentControlSet\Control\Lsa";
    Ensure = "Present";
    ValueType = "DWord";
    ValueData = @("1");
}

Reverse Engineer an Existing SharePoint Farm using PowerShell DSC

PowerShell Desired State Configuration (DSC) is a technology that allows you to define a configuration for an environment, and ensure it automatically gets configured tat way. It can also help you monitor the delta between the current state of the environment and what the defined configuration (a.k.a. Desired State) is. So as an example in the case of SharePoint, if you were to specify that you needed to register a Managed Account for user sp_services, and that someone was to delete that account, PowerShell DSC would automatically report the discrepancy in the logs or attempt to automatically recreated it based on how you configured your environment (Configuration Mode).

The SharePoint module for DSC is great to build new environments from the ground up. Simply specify what the Desired State should be (Web Applications, Site Collections, Service Applications, etc.) and let DSC do its job and automatically configure it. It acts as some kind of abstraction layer for the user, where they only need to define resources and their various settings for DSC to create and configure everything for them in the background. They don’t need to know the cmdlets to use, know what the objects’ properties are, and even less understand the object model behind it all. They only need to know what the DSC resource accepts.

Existing Environments

In many cases however, clients who want to get into DSC already have investments in SharePoint, where they have an existing SharePoint 2013 or 2016 farm that they would like to replicate somewhere else with DSC. Writing a DSC configuration script from scratch that represents their exact environment is a complex job and often results in the customer writing tens of thousands of lines of code. This is where the ReverseDSC orchestrator for SharePoint, called SharePointDSC.Reverse comes into play. SharePointDSC.Reverse is a PowerShell script (.ps1) that you execute against an existing SharePoint 2013 or 2016 farm to extract its PowerShell Desired State Configuration. The script will scan every component of the farm, down to the SPWeb level. This means that everything that can be defined in Central Administration, with the addition of Sites can be extracted with the SharePointDSC.Reverse. The output will be another .ps1 script that will contain the DSC configuration of your farm. That script can then be executed to compile a .MOF file that can be used to replicate your SharePoint environment elsewhere.

Other Scenarios

The ReverseDSC Orchestrator for SharePoint can also be used for several scenarios other than replicating a farm. It can be used to:

  • Extract a SharePoint 2013 Farm Configuration and replicate it as a SharePoint 2016 one;
  • Compare deltas between two extractions to see what changed between two point in time;
  • Create standalone copies of a Farm for developers;
  • Move an on-premises SharePoint Farm into Azure Infrastructure-as-a-Service;
  • On-board an existing farm onto PowerShell DSC to monitor it and ensure it remains in its desired state.

How to Use

The ReverseDSC Orchestrator for SharePoint is now officially available in the PowerShell Gallery. It can be installed, alongside all of its dependencies, by executing the following PowerShell one-liner from a machine that has internet connectivity:

Install-Script SharePointDSC.Reverse -Force 

Note that this has to be done on only one server in the farm; you don’t need to install it on all servers. If your SharePoint server doesn’t have internet connectivity, simply run the above line of PowerShell from a machine that does have internet connectivity, and manually copy the ReverseDSC, SharePointDSC from the PowerShell modules folder (C:\Program Files\WindowsPowerShell\Modules), and the SharePointDSC.Reverse.ps1 script from the PowerShell script folder (C:\Program Files\WindowsPowerShell\Scripts) onto the destination server.

Additional Resources

To learn more about the ReverseDSC Orchestrator for SharePoint, you can watch my Microsoft Virtual Academy session on the topic at the following URL: https://mva.microsoft.com/en-US/training-courses/sharepoint-automation-with-dsc-17843

SharePointDSC and ReverseDSC Microsoft Virtual Academy Session

In June of 2017, my colleague Brian Farnhill and I recorded a Full Day Microsoft Virtual Academy (MVA) session on Automating SharePoint with PowerShell Desired State Configuration. The recording is now available for free on the MVA site and covers the following topics:

  • Introduction to PowerShell Desired State Configuration
  • Advanced PowerShell DSC on-premises
  • Introduction to SharePointDSC
  • Azure Automation DSC with SharePoint
  • ReverseDSC for SharePoint
  • SharePointDSC Best Practices and Guidance

Click on the following link to watch the Full Session.

How to Get Started with SharePointDSC

The goal of this article is to help people interested in learning how to use PowerShell Desired State Configuration (DSC) to configure their SharePoint environment get started. While it is totally possible for you to configure a SharePoint Farm on an environment that has PowerShell 4.0 installed on, it is our recommendation that you try to use PowerShell 5+ as much as possible, as it offers a lot of improvements on the DSC side. The example covered in this article will be a Single Server SharePoint 2016 farm deployed with SQL Server 2016, on Windows Server 2016. We will be using DSC in push mode, meaning that we will manually execute the Start-DSCConfiguration cmdlet on the environment, and will ensure all dependent DSC Modules are put on the server prior to attempting to configure it.

The end-goal for this article, is to have a brand new Windows Server 2016 virtual machine with nothing on it to begin with, and then let DSC do the following automatically:

  • Install and configure Active Directory Domain Services;
  • Create all required SharePoint accounts;
  • Install SQL Server 2016;
  • Install the SharePoint 2016 Prerequisites;
  • Install SharePoint 2016;
  • Install the SharePoint 2016 French Language Pack;
  • Install the SharePoint 2016 Security Update (KB3115299); This update needs to be extracted in the updates folder of the SharePoint installation (slipstreamed).
  • Configure the SharePoint Farm;

The binaries to install SharePoint and SQL Server will be put on a Shared location, to which our Virtual Machine will have access to. Also, the Security Update (KB3115299) is required for the farm to get properly configured. Without it, you will get the following error thrown when calling the SPFarm DSC Block (at the New-SPConfigurationDatabase step): An error occurred while getting information about the user sp_farm at server contoso.com: The RPC server is unavailable. This installation will be slipstreamed within the SharePoint 2016 installation binaries.

Prerequisites

In this section we will cover the various prerequisites that have to be in place before initiating the DSC configuration.

Shared Location

In the current example, I will create a first Virtual Machine that will act as a file server. The SharePoint 2016 binaries, the SQL Server 2016 binaries, the Windows SXS folder, and the SharePoint 2016 language packs will be put on it. The Virtual Machine will be named DSC-Share, and will expose the following Shared Folders:

  • \\DSC-Share\Media\SP2016Binaries
  • \\DSC-Share\Media\SP2016LanguagePack
  • \\DSC-Share\Media\SQL2016Binaries
  • \\DSC-Share\Media\SXS (Needs to contain the SXS content of the Windows Server 2012 R2 installation Media)
SharePoint 2016 and SQL 2016 Binaries on Shared Drive

SharePoint 2016 and SQL 2016 Binaries on Shared Drive

The SXS folder contains the /Sources/SXS content from the Windows Server 2012 R2 installation media. It contains files that are required by the Prerequisites installer to install the .NET 3.5 components. During the DSC process, the SXS folder will be copied from the Remote Network Share onto the local disk. This is required for the Windows Feature to be properly installed on the local server.

Also, it is important for you to extract the Language pack in the specified folder. By default, when you download a SharePoint language pack, you get a file called serverlanguagepack.exe, however that won’t work with SharePointDSC. In order to properly extract its content you need to run the following command: .\serverlanguagepack.exe /extract:path. This will generate the folder structure expected by SharePointDSC.

SharePoint Virtual Machine

When I said earlier that the goal of this article was to start with a plain vanilla Virtual Machine with nothing installed on it….I lied. Just a tiny bit. Because we will be using DSC in Push mode for our example, the destination server (in my case named DSC-SP) needs to have all the required DSC modules installed on it first. The modules required for our example are the following:

  • xActiveDirectory
  • xNetworking
  • xSQLServer
  • SharePointDSC

The steps to get these properly installed on your server will differ depending on whether or not your Virtual Machine has internet connectivity or not.

Virtual Machine has Internet Connectivity

If your SharePoint server Virtual Machine has internet connectivity, then you are in luck. With the help of the Package Management component of PowerShell 5+, you can simply run the following cmdlets to have PowerShell automatically download the modules from the PowerShell Gallery:

Install-Module xActiveDirectory -Force
Install-Module xNetworking -Force
Install-Module xSQLServer -Force
Install-Module SharePointDSC -Force

Virtual Machine without Internet Connectivity

If your Virtual Machine doesn’t have internet connectivity, then you will have to manually copy the required modules inside the PowerShell modules repository. The easiest way to do that, is to download all the required module from a machine that has internet connectivity (see section just above), and then copy the downloaded modules manually onto the server. When you call the Install-Module cmdlet, PowerShell actually installs the specified module in C:\Program Files\WindowsPowerShell\Modules. Simply make sure you copy all the folders from that machine that has internet connectivity and from where you executed the Install-Module cmdlets, over to your SharePoint Virtual Machine, under the same path.

PowerShell DSC Modules

PowerShell DSC Modules

Set Network Share as being Part of the Intranet Zone

By default, the network share won’t be recognized as a trusted location. If you were to navigate to it from your SharePoint server and try to execute a program from it, you will get prompted with a Security Warning prompt that allows you to trust the file. PowerShell DSC is faced with the same issue in the background and this actually causes the process to hang. In order for PowerShell Desired State Configuration to be able to properly execute remote executable such as the SharePoint Prerequisite Installer, you need to add your network share to the Intranet zone in Internet Explorer. Simply launch the IE options, switch to the security tab, select Intranet, click on Sites and add the path to your network share (in my case file://dsc-share).

The Script

The following DSC script will be used to configure our environment. Its variables will be passed as Configuration data (defined in the next section below). The script itself is very self-explanatory and I will not go over each section in details. Note that for an offline installation, the script expects to see the following files in the prerequisiteinstallerfile folder in the root of your SharePoint 2016 binary installation folder:

  • AppFabric-KB3092423-x64-ENU.exe
  • dotNetFx45_Full_setup.exe
  • MicrosoftIdentityExtensions-64.msi
  • NDP453-KB2969351-x86-x64-AllOS-ENU.exe
  • setup_msipc_x64.exe
  • sqlncli.msi
  • Synchronization.msi
  • vc_redist.x64.exe
  • vcredist_x64.exe
  • WcfDataServices.exe
  • WindowsServerAppFabricSetup_x64.exe
SharePoint 2016 Prerequisites

SharePoint 2016 Prerequisites

You will also notice that at the bottom of the screen there is a section that will automatically compile a Meta-Mof file to configure the Local Configuration Manager (LCM) process on that machine so that it will automatically reboot the server when needed (prerequisites, domain-join, etc.) and automatically pickup the configuration where it left it upon being restarted.

SPStandAlone.ps1

Configuration SPStandAlone
{
    Import-DscResource -ModuleName PSDesiredStateConfiguration
    Import-DscResource -ModuleName xActiveDirectory
    Import-DSCResource -ModuleName xNetworking
    Import-DSCResource -ModuleName xSQLServer
    Import-DscResource -ModuleName SharePointDSC
 
    #region Credentials
    $Script:FarmAdmin = Get-Credential -Username "contoso\sp_farm" -Message "Farm Admin"
    $Script:FarmDomainAdmin = Get-Credential -Username "contoso\administrator" -Message "Domain Administrator"   
    #endregion
 
    node $AllNodes.NodeName
    {
        xFireWall SQLFirewallRule
        {
            Name = "AllowSQLConnection"
            DisplayName = 'Allow SQL Connection'
            Group = 'DSC Configuration Rules'
            Ensure = 'Present'
            Enabled = 'True'
            Profile = ('Domain') 
            Direction = 'InBound'
            LocalPort = ('1433') 
            Protocol = 'TCP'
            Description = 'Firewall Rule to allow SQL communication'
            DependsOn = @("[xADDomain]Domain","[xADUser]FarmAdmin")
        }
 
        WindowsFeature ADDS
        {
            Name = "AD-Domain-Services"
            IncludeAllSubFeature = $true
            Ensure = "Present"
        }
 
        WindowsFeature ADDSTools
        {
            Name = 'RSAT-AD-Tools'
            IncludeAllSubFeature = $true
            Ensure = "Present"
        }
 
        xADDomain Domain
        {
            DomainName = $AllNodes.DomainName
            DomainAdministratorCredential = $Script:FarmDomainAdmin
            SafemodeAdministratorPassword = $Script:FarmDomainAdmin
            DependsOn = "[WindowsFeature]ADDS"
        }
 
        xADUser FarmAdmin
        {
            DomainName = $AllNodes.DomainName
            Username = $Script:FarmAdmin.UserName.Replace(($AllNodes.DomainNetBIOS + "\"),"")
            Password = $Script:FarmAdmin
            PasswordNeverExpires = $true
            DependsOn = "[xADDomain]Domain"
        }
         
        xSQLServerSetup SQLSetup
        {
            InstanceName = "MSSQLServer"
            SourcePath = $AllNodes.SQLBinaryPath
            Features = "SQLENGINE"
            InstallSharedDir = "C:\Program Files\Microsoft SQL Server"
            SQLSysAdminAccounts = $Script:FarmAdmin.UserName
            SQLSvcAccount = $Script:FarmDomainAdmin
            AgtSvcAccount = $Script:FarmDomainAdmin
            PSDscRunAsCredential = $Script:FarmDomainAdmin
            DependsOn = @("[xADDomain]Domain","[xADUser]FarmAdmin","[xFirewall]SQLFirewallRule")
        }

	File SXSFolder
	{
	    SourcePath = $AllNodes.SXSRemotePath
	    Type = "Directory"
	    DestinationPath = $AllNodes.SxsLocalPath
	    Recurse = $true;
	    Credential = $Script:FarmDomainAdmin
	    Force = $true
	    PSDSCRunAsCredential = $Script:FarmDomainAdmin
	}
 
        SPInstallPrereqs SPPrereqs
        {
            InstallerPath = $AllNodes.SharePointBinaryPath + "\prerequisiteinstaller.exe"
            OnlineMode = $false
            SQLNCli = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\sqlncli.msi"
            DOTNETFX = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\dotNetfx45_Full_setup.exe"
            NETFX = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\NDP453-KB2969351-x86-x64-AllOS-ENU.exe"
            Sync = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\Synchronization.msi"
            AppFabric = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\WindowsServerAppFabricSetup_x64.exe"
            IDFX11 = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\MicrosoftIdentityExtensions-64.msi"
            MSIPCClient = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\setup_msipc_x64.exe"
            WCFDataServices56 = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\WcfDataServices.exe"
            KB3092423 = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\AppFabric-KB3092423-x64-ENU.exe"
            MSVCRT11 = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\vcredist_x64.exe"
            MSVCRT14 = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\vc_redist.x64.exe"
            ODBC = $AllNodes.SharePointBinaryPath + "\prerequisiteinstallerfiles\msodbcsql.msi"
            DependsOn = @("[xSQLServerSetup]SQLSetup","[File]SXSFolder")
	    SXSPath = $AllNodes.SXSLocalPath
            PSDSCRunAsCredential = $Script:FarmDomainAdmin
        }
 
        SPInstall InstallSharePoint 
        { 
             Ensure = "Present" 
             BinaryDir = $AllNodes.SharePointBinaryPath 
             ProductKey = $AllNodes.ProductKey
             DependsOn = @("[SPInstallPrereqs]SPPrereqs", "[xFirewall]SQLFirewallRule")
 	     PSDSCRunasCredential = $Script:FarmDomainAdmin
        }
         
        SPInstallLanguagePack InstallLPBinaries
        {
            BinaryDir = $AllNodes.LanguagePackPath
            Ensure = "Present"
            DependsOn = "[SPInstall]InstallSharePoint"
            PsDscRunAsCredential = $Script:FarmDomainAdmin
        }
         
        SPFarm SharePointFarm
        {
            Passphrase = New-Object System.Management.Automation.PSCredential ('Passphrase', (ConvertTo-SecureString $AllNodes.Passphrase -AsPlainText -Force));
            AdminContentDatabaseName = "SP2016_CENTRAL_ADMIN";
            FarmAccount = $Script:FarmAdmin;
            FarmConfigDatabaseName = "SP2016_Config";
            CentralAdministrationPort = 7777;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            CentralAdministrationAuth = "NTLM";
            RunCentralAdmin = $True;
	    ServerRole = "SingleServerFarm"
            Ensure = "Present";
            DatabaseServer = $AllNodes.NodeName;
            DependsOn = @("[SPInstallLanguagePack]InstallLPBinaries");
        }
        SPManagedAccount b3c4904a-3e85-4ddd-896b-1359901667e7
        {
            Account = $Script:FarmAdmin;
            AccountName = $Script:FarmAdmin.Username;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            Ensure = "Present";
            EmailNotification = 5;
            PreExpireDays = 2;
        }
	
        SPServiceAppPool SearchServiceAppPool
        {
            Name = "SP2016-Search";
            ServiceAccount = $Script:FarmAdmin.Username;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            Ensure = "Present";
        }  
        SPWebApplication SP2016
        {
            DatabaseName = "SP2016-Content";
            Url = "http://" + $AllNodes.NodeName + "/";
            ApplicationPool = "SP2016-AppPool";
            Path = "C:\inetpub\wwwroot\wss\VirtualDirectories\80";
            UseSSL = $False;
            AllowAnonymous = $False;
            Name = "SP2016";
            AuthenticationMethod = "NTLM";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            ApplicationPoolAccount = $Script:FarmAdmin.Username;
            Ensure = "Present";
            Port = "80";
            DatabaseServer = $AllNodes.NodeName;
            AuthenticationProvider = "Windows Authentication";
        }
        SPServiceAppPool MMS        
        {
            Name = "MMS";
            ServiceAccount = $Script:FarmAdmin.Username;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            Ensure = "Present";
        }  
        SPContentDatabase SP2016-Content
        {
            Enabled = $True;
            MaximumSiteCount = 5000;
            Name = "SP2016-Content";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            WarningSiteCount = 2000;
            DatabaseServer = $AllNodes.NodeName;
            WebAppUrl = "http://" + $AllNodes.NodeName;
        }                    
        SPQuotaTemplate 10bac15d-d097-471a-b09c-82a63d1818bb
        {
            Name = "10GB";
            MaximumUsagePointsSolutions = 300;
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            StorageMaxInMB = 10240;
            WarningUsagePointsSolutions = 275;
            StorageWarningInMB = 8192;
        }   
        SPSite 50721b23-6892-4353-a104-814a4295ea42
        {
            OwnerAlias = $Script:FarmAdmin.Username;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            Url = "http://" + $AllNodes.NodeName
            Language = 1033;
            QuotaTemplate = "10GB";
            CompatibilityLevel = 15;
            Template = "STS#0";
            ContentDatabase = "SP2016-Content";
            DependsOn =  @("[SPWebApplication]SP2016");
        }
        SPSite 50721b23-6892-4353-a104-814a2395ea42
        {
            OwnerAlias = $Script:FarmAdmin.Username;
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
            Url = "http://" + $AllNodes.NodeName + "/sites/searchcenter"
            Language = 1033;
            QuotaTemplate = "10GB";
            CompatibilityLevel = 15;
            Template = "SRCHCEN#0";
            ContentDatabase = "SP2016-Content";
            DependsOn =  @("[SPWebApplication]SP2016");
        }
        SPServiceInstance CentralAdministrationInstance
        {
            Name = "Central Administration";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance ManagedMetadataWebServiceInstance
        {
            Name = "Managed Metadata Web Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance MicrosoftSharePointFoundationIncomingE-MailInstance
        {
            Name = "Microsoft SharePoint Foundation Incoming E-Mail";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance MicrosoftSharePointFoundationWebApplicationInstance
        {
            Name = "Microsoft SharePoint Foundation Web Application";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance MicrosoftSharePointFoundationWorkflowTimerServiceInstance
        {
            Name = "Microsoft SharePoint Foundation Workflow Timer Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance SearchHostControllerServiceInstance
        {
            Name = "Search Host Controller Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance SearchQueryandSiteSettingsServiceInstance
        {
            Name = "Search Query and Site Settings Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance SharePointServerSearchInstance
        {
            Name = "SharePoint Server Search";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
        SPServiceInstance UserProfileServiceInstance
        {
            Name = "User Profile Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $Script:FarmDomainAdmin;
        }
    }
}
 
#region LCM Config
[DSCLocalConfigurationManager()]
Configuration LCMConfig
{
    Node $env:ComputerName
    {
        Settings
        {
            ActionAfterReboot = 'ContinueConfiguration';
            RebootNodeIfNeeded = $true;
        }
    }
}
LCMConfig
Set-DscLocalConfigurationManager LCMConfig -Force -Verbose
#endregion
 
SPStandAlone -ConfigurationData .\SPStandAlone-ConfigData.psd1

The Configuration Data

If you pay close attention to the last line of the script above, you’ll notice that upon calling our DSC Configuration’s name (in our case SPStandAlone), that we are passing it a path to a .psd1 file for the -ConfigurationData parameter. This basically tells PowerShell Desired State Configuration that it needs to read the variables contained in that .psd1 file in order to properly compile itself.

That PowerShell Data File (.psd1) is where we will specify all of the variables for our environment. This allows us to keep the .ps1 script generic so that it can be used to generate 100’s of Virtual Machines having the same configuration without having to modify it for each one. We simply need to modify the .psd1 file in order for the changes to be picked up upon the MOF compilation job. In our example, we will be specifying the following content in our .psd1 file. Note how the script above is accessing each of these variables by using $AllNodes.<Variable>. This $AllNodes is a reserved keyword that allows us to access values in the ConfigurationData of any given DSC configuration script. You could also expand the .psd1 to include variables unique for a specific node, if your DSC script node ever defined more than one node (like it should be the case for most SharePoint farms).

SPStandAlone-ConfigData.psd1

@{
    AllNodes = @(    
    @{
        NodeName = $env:COMPUTERNAME;
        PSDscAllowPlainTextPassword = $true;
        PSDscAllowDomainUser = $true;

        #region Parameters
        Passphrase = "pass@word1"
        DomainName = "contoso.com"
        DomainNetBIOS = "contoso"
        ProductKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"  
        LanguagePackPath = "\\DSC-Share\Media\SP2016LanguagePack"
        SharePointBinaryPath = "\\DSC-Share\Media\SP2016Binaries"   
        SQLBinaryPath = "\\DSC-Share\Media\SQL2016Binaries"
        SXSLocalPath = "c:\SXS" # The content from the Network Share will be copied locally at that location; 
        SXSRemotePath = "\\DSC-Share\Media\SXS\" 
        #endregion  
    }
)}

Executing the DSC Script

This section describes the steps you need to take in order to initiate the deployment and configuration of your SharePoint 2016 Farm using the PowerShell Desired State Configuration scripts above.

  1. Copy both the SPStandAlone.ps1 and SPStandAlone-ConfigData.psd1 file onto the SharePoint server. In my case, I put hem both under C:\temp\.
    DSC Script and its associated Configuration data

    DSC Script and its associated Configuration data

  2. Open a new PowerShell console as an administrator and browse to the directory where you’ve copied the 2 files.
  3. Execute the SPStandAlone.ps1 script and provide both the SharePoint Farm Admin and Domain Admin credentials when prompted. Even though this user doesn’t yet exist, the credentials you provide here will be used to create the account in Active Directory.
    Compiling your SharePoint DSC MOF file

    Compiling your SharePoint DSC MOF file


    SharePoint DSC MOF File Generated

    SharePoint DSC MOF File Generated

  4. Your .MOF file has now been generated in a new folder named by our Configuration (SPStandAlone).
    SPStandAlone Compiled MOF File

    SPStandAlone Compiled MOF File


    All that is now left to do is to call the following PowerShell cmdlet to initiate the deployment process:

    Start-DSCConfiguration SPStandAlone -Force -Wait -Verbose
    

    The server will automatically reboot several times, and upon rebooting, you will loose the verbose PowerShell console, but don’t worry DSC is still being executed in the background. If you need to check the DSC execution logs, simply open Event Receiver and navigate to Applications and Services Logs > Microsoft > Windows > Desired State Configuration > Operational. After about an hour or so depending on the performance of your environment, you will have a fully working SharePoint 2016 Standalone machine.