How to Write your Own ReverseDSC Orchestrator

ReverseDSC is a module that allows you to extract the PowerShell Desired State Configuration out of an existing environment, in order for you to analyze it, onboard it onto DSC, or replicate it somewhere else. ReverseDSC as it stands is a technology Agnostic PowerShell Module. It only provides methods to allow you to properly convert extracted values into a DSC notation. In order to obtain these values, you need to dynamically call into the Get-TargetResource function of a given DSC Resource.

Every DSC Resource needs to include 3 core functions in order for it to be valid: Get-TargetResource, Set-TargetResource, and Test-TargetResource. For more information on the role of each of these function, you can consult the readme content on the SharePointDSC.Reverse repository. As explained in my How to use the ReverseDSC Core article, in order for you to obtain the values of a Resource instance, you need to call the Get-TargetResource for it, passing in the mandatory parameters that will allow the function to retrieve the instance (e.g. Primary Key of the instance).

An Orchestrator script, is responsible for determining these mandatory parameters and for calling the Get-TargetResource function for each instance, to obtain the complete set of key/value pairs for that instance. It then calls the ReverseDSC Core for each of these key/value pair to obtain the DSC notation, collects them all, and saves them into a resulting .ps1 file. The Orchestrator script is technology Specific, meaning that it requires the person writing the script to be familiar to some level with the technology stack it is for. As an example, when writing the Orchestrator script for SharePoint, when trying to retrieve information about all the Web Applications, you need to be able to know how to call the Get-SPWebApplication cmdlet in order to retrieve the URL (Primary key) of a Web Application instance.

ReverseDSC is all about community effort, and to help contributors get started I published a new Orchestrator Script Template to allow people to quickly get their script up and running. In the script, you will find several instances of placeholders starting with “[**“. Simply replace these with the values specified to begin with. The next thing for you to do is to start writing the set of Read- (Read-Dash) methods in the Reverse Functions section of the template. For every DSC Resource you wish to reverse, you should define a unique Read-Dash function. The template provides a very generic example on how to write that method, but you may wish to refer to existing Orchestrator scripts for more complex scenarios and see how they are done.

The last thing left for you to do once all your Read-Dash functions have been written, is to make sure that you are actually calling them from within the Orchestrator function. Try to proceed each of these calls with a Verbose output line that will help the users identify where we are at with the script’s execution. Once you script is completed, you should be able to execute it by simply executing the .ps1 file within a PowerShell session. In order to properly test your script, make sure that you don’t get any errors running it, but also try to execute the resulting output .ps1 file, which will attempt to compile the .MOF file, and make sure you don’t get errors at compilation time either.

Should you have any questions or comments regarding the Orchestrator templates or on how to get started, please use the issue section on the GitHub repository for the templates.

Deploy a SharePoint 2016 Standalone VM in Azure using PowerShell Desired State Configuration (DSC)

In the PowerShell Desired State Configuration (DSC) world, you really have two options when it comes down to configuring a machine. You can either use Push mode to manually “push” a DSC script into a machine’s Local Configuration Manager (LCM) memory, or use a Pull Server and connect your machine to it and let them obtain their DSC script themselves. In the Azure world, we have something called “Azure Automation DSC” which is effectively a PowerShell Desired State Configuration Pull Server in the cloud, managed as “Software-as-a-Service” (SaaS). With Azure Automation DSC, you can manage both on-premises and Azure VMs by having them connect back to your Azure Automation Account as if it was just a regular DSC Pull Server.

In this article, we will go through the process of setting up a SharePoint 2016 Standalone Virtual Machine using nothing but Azure Automation DSC. The idea is for people to easily create SharePoint Development machines in Azure Infrastructure-as-a-Service (IaaS).

The Scenario

In Azure IaaS I already have setup two VMs:

  • SPTechCon-DC is a Windows Server 2016 VM acting as a Domain Controller for the contoso.com domain.
  • SPTechCon-Share is a Windows Server 2016 VM that acts as a File Share, where I have the installation media for both SQL Server 2016 Enterprise and SharePoint Server 2016. These two shares are exposed at:
    • \\SPTechCon-Share\Share\SQL2016Media\
    • \\SPTechCon-Share\Share\SP2016Media

The Process

By following the following steps in order you will be able to deploy a new Windows Server 2016 VM in Azure IaaS, have it automatically join the contoso.com domain, and install both SQL Server 2016 and SharePoint 2016 on it. By the end of this process, you will have a fully functioning SharePoint 2016 development VM that you can simply go and install Visual Studio 2017 on to use as you main development environment for developers within your enterprise. This process is completely reusable, and can help your enterprise ensure your development team all have VMs with a configuration that matches your production environment.

1 – Create a new VM

In this article, this is the only manual process. Off course this could be automated, by for this example here, I will leave it up to you to decide how you wish to create your VM. In my case, I will be creating my VM with the following specs:

  • Name: SPTechCon-Share
  • OS Version: Windows Server 2016 Datacenter
  • Memory: 7Gb of RAM
  • CPU: 2 cores

To create the VM, start by selecting Windows Server as you Template category.

Creating Azure Windows VM

Creating Azure Windows VM

From the following screen, I select Windows Server 2016 Data Center.

Make sure you select Resource Manager as your deployment model and click Create

Azure Resource Manager

Azure Resource Manager

Fill in all the mandatory information, give your machine a meaningful name and make sure you create it as part of the same resource group where your Domain Controller and File Share servers are. Click OK.

Create an Azure Virtual Machine

Create an Azure Virtual Machine

Choose an appropriate VM size for your environment. In my case, I use a DS11_V2 Standard size. Click Select.

DS11_V2 Azure Machine

DS11_V2 Azure Machine

Leave out the default values on the Settings screen. Click OK.

Review the details for your machine. Click OK.

Summary of Azure VM

Summary of Azure VM

Wait a few minutes until you receive notification that the VM was successfully provisioned.

Azure VM being provisioned

Azure VM being provisioned


2 – Create a New Azure Automation Account

Remember we mentioned that Azure Automation is somewhat a DSC Pull Server in the Cloud. What we need to do next is create an instance of an Azure Automation Account to manage our DSC configurations. Azure Automation Accounts are available in the marketplace. Simply do a search for Azure Automation to find and select it.

Create a new Azure Automation Account

Create a new Azure Automation Account

Click on it to select it from the marketplace and then click Create.

Azure Automation Account

Azure Automation Account

Give your Azure Automation Account a name, make sure you select the same Resource Group as all the VMs we have created so far in this demo. Click Create.

Setting up Azure Automation

Setting up Azure Automation

The Azure Automation Account creation process is almost instantaneous, it should only take a few seconds to get created.

Review the Desired State Configuration Script

To configure our Standalone SharePoint box, we will be using the following PowerShell Desired State Configuration (DSC) script. I strongly encourage you quickly read through it an try to understand what is really happening under the covers. This below, is the complete script.

Configuration SharePoint2016StandAlone
{
    param(
        [String]$ParamDomain,
        [String]$ParamInternalDomainControllerIP,
        [String]$ParamMachineName,
        [String]$ParamProductKey,
        [String]$ParamUsername,
        [String]$ParamPassword,
        [String]$ParamShareName
	)

    Import-DSCResource -ModuleName xDSCDomainJoin
    Import-DSCResource -ModuleName xNetworking    
    Import-DSCResource -ModuleName SharePointDSC
    Import-DSCResource -ModuleName xSQLServer    

    $secdomainpasswd = ConvertTo-SecureString $ParamPassword -AsPlainText -Force
    $ParamCredsJoindomain = New-Object System.Management.Automation.PSCredential($ParamUsername, $secdomainpasswd)

    Node $ParamMachineName
    {
        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' 
        }

        xDNSServerAddress DNS
	{
	    Address = $ParamInternalDomainControllerIP
	    AddressFamily = "IPv4"
	    InterfaceAlias = "Ethernet 2"
	}

        xDSCDomainJoin Join
	{
	    Domain = $ParamDomain
	    Credential = $ParamCredsJoindomain
	    DependsOn = "[xDNSServerAddress]DNS"
	}		

        xSQLServerSetup SQLSetup
        {
            SetupCredential = $ParamCredsJoindomain
            InstanceName = "MSSQLServer"
            SourcePath = "\\$ParamShareName\Share\SQL2016Media\"
            Features = "SQLENGINE,FULLTEXT,RS,AS,IS"
            InstallSharedDir = "C:\Program Files\Microsoft SQL Server"
            SQLSysAdminAccounts = $ParamCredsJoindomain.UserName
            DependsOn = "[xDSCDomainJoin]Join"
        }

        SPInstallPrereqs SP2016Prereqs
        {
            InstallerPath = "\\$ParamShareName\Share\SP2016Media\prerequisiteinstaller.exe"
            OnlineMode = $true
            DependsOn = "[xSQLServerSetup]SQLSetup"
        }

        SPInstall InstallSharePoint 
        { 
             Ensure = "Present" 
             BinaryDir = "\\$ParamShareName\Share\SP2016Media\" 
             ProductKey = $ParamProductKey
             DependsOn = @("[SPInstallPrereqs]SP2016Prereqs", "[xFirewall]SQLFirewallRule")
        } 

        SPCreateFarm CreateSPFarm 
        { 
            DatabaseServer           = $ParamMachineName
            FarmConfigDatabaseName   = "SP_Config" 
            Passphrase               = $ParamCredsJoindomain 
            FarmAccount              = $ParamCredsJoindomain 
            AdminContentDatabaseName = "SP_AdminContent" 
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            ServerRole               = "SingleServerFarm"
            CentralAdministrationPort = 7777
            DependsOn                = "[SPInstall]InstallSharePoint" 
        }

        SPManagedAccount FarmAccount
        {
            AccountName = $ParamCredsJoindomain.UserName
            Account = $ParamCredsJoindomain
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            DependsOn = "[SPCreateFarm]CreateSPFarm"
        }

        SPServiceAppPool SharePoint80
        {
            Name = "SharePoint - 80"
            ServiceAccount = $ParamCredsJoinDomain.UserName
            PsDSCRunAsCredential     = $ParamCredsJoindomain
            DependsOn = "[SPManagedAccount]FarmAccount"
        }

        SPWebApplication RootWebApp
        {
            Name = "RootWebApp"
            ApplicationPool = "SharePoint - 80"
            ApplicationPoolAccount = $ParamCredsJoinDomain.UserName
            Url = "http://$ParamMachineName"
            DatabaseServer = $ParamMachineName
            DatabaseName = "WebApp-SharePoint-80"
            Port = 80
            PsDSCRunAsCredential = $ParamCredsJoinDomain
            DependsOn = "[SPServiceAppPool]SharePoint80"
        }

        SPSite RootSite
        {
            Url = "http://$ParamMachineName"
            OwnerAlias = $ParamCredsJoinDomain.UserName
            Template = "STS#0"         
            PsDSCRunAsCredential = $ParamCredsJoinDomain
            DependsOn = "[SPWebApplication]RootWebApp"
        }
	}
}

Let’s take a closer look at what the script actually defines. Note that the Configuration script actually expects 7 parameters to be passed at compilation time. These parameters are:

Parameter Name Value Description
ParamDomain contoso.com Specifies the domain name that our machine will be joining.
ParamInternalDomainControllerIP 10.0.10.5 Internal IP address of our Domain Controller VM. (Note that this will likely differ for you).
ParamMachineName SPTechCon-SA Name of the Azure VM we created at Step 1 above.
ParamProductKey XXXXX-XXXXX-XXXXX-XXXXX-XXXXX Your own SharePoint 2016 (Standard or Enterprise) Product Key.
ParamUsername contoso\sp_farm Username for your SharePoint Farm Account.
ParamPassword pass@word1 Password for the SharePoint Farm Account used.
ParamShareName SPTechCon-Share Name of the File Share VM.

We will now break down each resource block an give you a quick overview of what it actually does.

The following creates a Domain Firewall rule on port 1433, to allow connections to our SQL Server (in our case hosted on the local machine) in case we wished to add more servers to our farm.

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' 
}

This block changes the DNS Server IP address to point to our domain controller.

xDNSServerAddress DNS
{
    Address = $ParamInternalDomainControllerIP
    AddressFamily = "IPv4"
    InterfaceAlias = "Ethernet 2"
}

The following joins the machine to the contoso.com domain.

xDSCDomainJoin Join
{
    Domain = $ParamDomain
    Credential = $ParamCredsJoindomain
    DependsOn = "[xDNSServerAddress]DNS"
}

This block installs SQL Server 2016 from our Shared Media Installation location.

xSQLServerSetup SQLSetup
{
    SetupCredential = $ParamCredsJoindomain
    InstanceName = "MSSQLServer"
    SourcePath = "\\$ParamShareName\Share\SQL2016Media\"
    Features = "SQLENGINE,FULLTEXT,RS,AS,IS"
    InstallSharedDir = "C:\Program Files\Microsoft SQL Server"
    SQLSysAdminAccounts = $ParamCredsJoindomain.UserName
    DependsOn = "[xDSCDomainJoin]Join"
}

This block installs the SharePoint 2016 pre-requisites. The server will automatically reboot itself once it reaches that step and will automatically resume the DSC configuration process.

SPInstallPrereqs SP2016Prereqs
{
    InstallerPath = "\\$ParamShareName\Share\SP2016Media\prerequisiteinstaller.exe"
    OnlineMode = $true
    DependsOn = "[xSQLServerSetup]SQLSetup"
}

This block installs the actual SharePoint 2016 bits on the machine.

SPInstall InstallSharePoint 
{ 
    Ensure = "Present" 
    BinaryDir = "\\$ParamShareName\Share\SP2016Media\" 
    ProductKey = $ParamProductKey
    DependsOn = @("[SPInstallPrereqs]SP2016Prereqs", "[xFirewall]SQLFirewallRule")
} 

This block creates the SharePoint Farm. Think of it as being the equivalent of running PSConfig.

SPCreateFarm CreateSPFarm 
{ 
    DatabaseServer           = $ParamMachineName
    FarmConfigDatabaseName   = "SP_Config" 
    Passphrase               = $ParamCredsJoindomain 
    FarmAccount              = $ParamCredsJoindomain 
    AdminContentDatabaseName = "SP_AdminContent" 
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    ServerRole               = "SingleServerFarm"
    CentralAdministrationPort = 7777
    DependsOn = "[SPInstall]InstallSharePoint" 
}

This block creates a SharePoint Managed Account for our farm admin.

SPManagedAccount FarmAccount
{
    AccountName = $ParamCredsJoindomain.UserName
    Account = $ParamCredsJoindomain
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    DependsOn = "[SPCreateFarm]CreateSPFarm"
}

This block creates a SharePoint Application Pool for our Web Application to be.

SPServiceAppPool SharePoint80
{
    Name = "SharePoint - 80"
    ServiceAccount = $ParamCredsJoinDomain.UserName
    PsDSCRunAsCredential     = $ParamCredsJoindomain
    DependsOn = "[SPManagedAccount]FarmAccount"
}

this block should be self explanatory. It creates a SharePoint Web Application on port 80.

SPWebApplication RootWebApp
{
    Name = "RootWebApp"
    ApplicationPool = "SharePoint - 80"
    ApplicationPoolAccount = $ParamCredsJoinDomain.UserName
    Url = "http://$ParamMachineName"
    DatabaseServer = $ParamMachineName
    DatabaseName = "WebApp-SharePoint-80"
    Port = 80
    PsDSCRunAsCredential = $ParamCredsJoinDomain
    DependsOn = "[SPServiceAppPool]SharePoint80"
}

This last block simply creates a Site Collection at the root of our Web Application.

SPSite RootSite
{
    Url = "http://$ParamMachineName"
    OwnerAlias = $ParamCredsJoinDomain.UserName
    Template = "STS#0"         
    PsDSCRunAsCredential = $ParamCredsJoinDomain
    DependsOn = "[SPWebApplication]RootWebApp"
}

What we need to do now, is upload this DSC configuration into our Azure Automation Account. To do this, start by navigating to your Automation Account and click on DSC Configurations.

DSC Configuration Node

DSC Configuration Node

Click on Add a configuration.

Upload DSC Configuration

Upload DSC Configuration

Click on the folder icon and browse to the .ps1 script above. Note that you will need to copy the complete script and save it locally first. Click on OK.

Our DSC Configuration Script is now in the Cloud, contained within our Azure Automation Account.

4 – Import the Required DSC Module

If you paid close attention to our full script above, you’ve realized that it needs to import 4 different DSC modules:

  • xDSCDomainJoin
  • xNetworking
  • SharePointDSC
  • xSQLServer

However, by default Azure Automation knows nothing about these modules. We need to import them first for Azure Automation to be able to properly configure our servers. Think of this as being the equivalent of putting the required modules and resources on a Pull Server for your registered nodes to consume (in an on-premises type of scenario). To import a resource, you need to go back to the main Azure Automation screen and click on Assets.

On the next screen, select Modules.

Adding Azure Automation Module

From here we have two choices: upload the required resources as individual .zip files from our local machine, or Import them from the PowerShellGallery.com repository. In my case, I choose to import them from the gallery, therefore I need to click on Browse gallery.

PowerShell Gallery Import

In the search box, type in the name of the first module we are trying to import: xDSCDomainJoin. Select the proper module from the search results by clicking on it.

Import DSC Domain Join

To finalize the import process, click on the Import icon.

Module imported

On the next screen, simply click OK.

Imported Module DSC

Repeat the same import process with the remaining missing modules: xNetworking, SharePointDSC, and xSQLServer.

5 – Initiate Compilation Job

In an on-premises scenario, you need to call the Configuration keyword of your DSC script in order for it to get “compiled” as a .MOF file. In Azure Automation, this is normally done by clicking on your DSC Configuration (uploaded at Step 3 above), and by clicking on Compile. However, in our case, we have credentials that need to be passed to our configuration. Therefore, instead of manually initiating the compilation job from the Azure Portal, we will use a local PowerShell script to remotely initiate the compilation job, allowing us to pass in parameters.

We will be using the following PowerShell script to remotely initiate that compilation job. Note that these are all the parameters we mentioned previously that are simply passed up to my Azure Automation Account.

$ProductKey = Read-Host "Please enter your SharePoint 2016 Product Key"
$MachineName = "SPTechCon-SA"
$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = $MachineName
            PSDscAllowPlainTextPassword = $True
        }
    )
}

$Parameters = @{
    ParamDomain = "contoso.com"
    ParamInternalDomainControllerIP = "10.0.10.5"; 
    ParamMachineName= $MachineName
    ParamProductKey = $ProductKey 
    ParamUsername = "contoso\sp_farm"
    ParamPassword = "pass@word1"
    ParamShareName = "SPTechCon-Share"
}

Login-AzureRMAccount
Start-AzureRmAutomationDscCompilationJob -ResourceGroupName "SPTechCon" -AutomationAccountName "SPTechCon-Automation" -ConfigurationName "SharePoint2016StandAlone" -ConfigurationData $ConfigData -Parameters $Parameters

Upon executing this script, you will get prompted to enter your Azure Credentials, which you’ll need to do in order for the compilation jo to get queued up.

Provide Azure credentials

The script should only take a second or two to execute and will automatically initiate a compilation job in Azure.

Azure Automation DSC Compilation

Give Azure about5 minutes to initiate and finalize the compilation. Once the job has completed, the compilation status will get updated to Completed.

Completed Compilation of Azure DSC

6 – Register a DSC Node

If we recap what we have done so far, we started off by creating a new Azure IaaS VM that we wish to configure as a SharePoint 2016 Standalone development machine. We have then wrote the Desired State Configuration script for it, and have uploaded and compiled it into Azure. Now what we need to do is actually associate the VM we created with the DSC script we’ve uploaded. To do this, you need to go back to your Azure Automation’s account main page, and this time click on DSC Nodes.

Register an Azure DSC Node

Azure Automation gives you the option of managing both Azure and on-premises Virtual Machines. On-premises Virtual Machines will be covered in another article, when time permits. In our case we want to register an existing Azure VM. Click on Add Azure VM.

Register Azure VM with DSC

Azure Automation will then ask you for two things: The VM you wish to register, and the DSC Configuration to associate with it. Start off by clicking on Virtual Machines, and select the Virtual Machine we created from the list (in my case SPTechCon-SA). Click OK. One thing that is interesting to note here, is that because we have generalized our DSC script (meaning not valus are hardcoded in it), we can easily select multiple VMs in this step and they will each get assigned the exact same configuration.

Associate Azure VM with Automation DSC in Azure

Now that you have selected your VM, it’s time to pick the DSC Configuration we wish to deploy onto it. Click on Registration. From the Node Configuration Name, pick the Node Configuration we compiled previously. The rest of the properties listed on the page should look familiar to you. They represent the LCM settings that can normally be set via PowerShell in on-premises scenarios. Leave everything as default, with the exception of the Reboot Node if Needed checkbox that absolutely need to be checked for the installation to complete properly. Click OK.

Associate Configuration

The last thing left for us to do now is initiate the registration process by clicking on Create.

Initiate DSC Registration

Now sit back and relax, your machine is going to go and configure itself. Depending on several factors (machine size, region, etc.) the process may take up to 45 minutes to complete

How does it Work?

If you were to connect to your VM before registering it to the Azure Automation Account and run the Get-DSCLocalConfigurationManager cmdlet on it, you would see that by default the machine’s LCM is set to PUSH mode.

Get-DSCLocalConfigurationManager

Upon registering your machine against the Azure Automation Account, a DSC Extension is assigned to your VM. That extension will automatically change the configuration of the machine’s LCM to set it in PULL mode and register it against your Azure Automation’s Pull Server endpoint.

DSC Azure Pull mode

Deploying a Multi-Server SharePoint Farm with PowerShell Desired State Configuration

In this article we will cover the process of writing a DSC Configuration with SharePointDSC and deploying it to multiple servers to create a SharePoint 2016 farm (note that this would also work for SharePoint 2013 with minor changes to the SPFarm block). We will be using a Push Refresh mode, meaning that the Configuration will be manually applied to our servers, and not using a Pull Server to centrally manage the configuration. Using this approach, if our configuration was to change, it would need to be manually re-pushed onto the servers in the farm. While I believe that in most cases, you will wish to use a Pull refresh mode along with an Enterprise Pull Server to manage your SharePoint deployments, we are using a Push mode to keep things simple for the sake of this article.

The article will cover a scenario I will be demonstrating over at SPTechCon Austin next week. In this demo, I have a very small multi-server SharePoint 2016 farm that is made up of one dedicated SQL Server 2016 (SPTechCon-SQL), one Web Front-End (SPTechCon-WFE1), and one Application Server (SPTechCon-APP1). The configuration script will be built on a separate machine named (SPTechCon-Pull) and will be remotely applied to the servers in my farm from that machine. The figure below gives you a complete overview of the landscape of my demo.

Every server in my farm is a Windows Server 2016 Datacenter instance. As mentioned above, SPTechCon-SQL has SQL Server 2016 installed on it, and both SharePoint boxes (SPTechCon-WFE1 and SPTechCon-APP1) have the SharePoint 2016 bits installed on them (PSConfig was not run, just the SP2016 bits were installed).

As part of the demo, I will be using the following DSC configuration script to deploy my farm:

Configuration SPTechCon-OnPrem
{
    Import-DSCResource -ModuleName SharePointDSC

    $farmAccount = Get-Credential -UserName "contoso\sp_farm" -Message "Farm Account"
    $adminAccount = Get-Credential -UserName "contoso\sp_admin" -Message "Admin Account"

	Node SPTechCon-WFE1
	{
        SPFarm SPFarm 
        { 
            DatabaseServer           = "SPTechCon-SQL"
            FarmConfigDatabaseName   = "SP_Config" 
            Passphrase               = $farmAccount 
            FarmAccount              = $farmAccount
            AdminContentDatabaseName = "SP_AdminContent" 
            PsDSCRunAsCredential     = $farmAccount
            ServerRole               = "WebFrontEnd"
            Ensure                   = "Present"
            RunCentralAdmin          = $true
            CentralAdministrationPort = 7777
        }

        SPManagedAccount FarmAccount
        {
            AccountName = $farmAccount.UserName
            Account = $farmAccount
            PsDSCRunAsCredential     = $farmAccount
            DependsOn = "[SPFarm]SPFarm"
        }

        SPManagedAccount AdminAccount
        {
            AccountName = $adminAccount.UserName
            Account = $adminAccount
            PsDSCRunAsCredential     = $farmAccount
            DependsOn = "[SPFarm]SPFarm"
        }

        SPServiceAppPool SharePoint80
        {
            Name = "SharePoint - 80"
            ServiceAccount = $adminAccount.UserName
            PsDSCRunAsCredential     = $farmAccount
            DependsOn = "[SPManagedAccount]FarmAccount"
        }

        SPWebApplication RootWebApp
        {
            Name = "RootWebApp"
            ApplicationPool = "SharePoint - 80"
            ApplicationPoolAccount = $adminAccount.UserName
            Url = "http://SPTechCon-WFE1"
            DatabaseServer = "SPTechCon-SQL"
            DatabaseName = "WebApp-SharePoint-80"
            Port = 80
            PsDSCRunAsCredential = $farmAccount
            DependsOn = "[SPServiceAppPool]SharePoint80"
        }

        SPSite RootSite
        {
            Url = "http://SPTechCon-WFE1"
            OwnerAlias = $adminAccount.UserName
            Template = "STS#0"         
            PsDSCRunAsCredential = $farmAccount
            DependsOn = "[SPWebApplication]RootWebApp"
        }
	}
    Node SPTechCon-APP1
	{
        SPFarm SPFarm 
        { 
            DatabaseServer           = "SPTechCon-SQL"
            FarmConfigDatabaseName   = "SP_Config" 
            Passphrase               = $farmAccount 
            FarmAccount              = $farmAccount
            AdminContentDatabaseName = "SP_AdminContent" 
            PsDSCRunAsCredential     = $farmAccount
            ServerRole               = "Application"
            Ensure                   = "Present"
            RunCentralAdmin          = $false
        }
        SPServiceInstance BusinessDataConnectivityServiceInstance
        {
            Name = "Business Data Connectivity Service";
            Ensure = "Present";
            PsDSCRunAsCredential = $farmAccount;
        }
    }
}
$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = "SPTechCon-WFE1"
            PSDscAllowPlainTextPassword = $True
            PSDscAllowDomainUser = $true
        },
        @{
            NodeName = "SPTechCon-APP1"
            PSDscAllowPlainTextPassword = $True
            PSDscAllowDomainUser = $true
        }
    )
}
SPTechCon-OnPrem -ConfigurationData $ConfigData
Start-DSCConfiguration SPTechCon-OnPrem -Wait -Verbose -Force

In summary, that script will automatically create the SharePoint 2016 farm, assign the Web Front-End MinRole to SPTechCon-WFE1, create a Web Application on port 80, and a root site collection, add SPTechCon-APP1 to the farm with the Application MinRole and have it run the Business Connectivity Service. Nothing too complicated, again to keep the demo focused and concise. Now, if you take a look at the last two lines of the script, you see that we are passing ConfigurationData to our Configuration method to ensure passwords can be passed as plain text. In an enterprise context, you will normally want to encrypt the credentials using a certificate. In our case here, I omitted to specify a certificate for simplicity sake.

Let us now head onto our SPTechCon-Pull machine, which is noting but a windows 10 machine hosted on the same domain as our servers to be configured. You will need to make sure that this machine has the SharePointDSC modules installed on it, because it will be required in order for us to compile our MOF file from this server. Installing the module is as easy as running the following cmdlet if your machine has internet connectivity:

Install-Module SharePointDSC

If you machine doesn’t have internet connectivity, then you will need to manually copy the SharePointDSC module onto the machine, under C:\Program Files\WindowsPowerShell\Modules\.

Upon executing the script above, you will be prompted to enter the credentials for the Farm account, which is required to execute the configuration steps, as well as for the admin account’s credentials, which I use as the owner of my site collection. Using remoting, PowerShell will remotely contact the two SharePoint servers and initiate the configuration. After a few minutes, you should see that the execution completed (see figure below).

You should also be able to navigate to the site collection we created (in our case at http://sptechcon-wfe1/) or to central administration, which based on our script, is hosted on SPTechCon-WFE1 and exposed through port 7777.

What is important for you to take away from this example, is that you don’t have to physically go on each server node that are part of your DSC configuration script in order to push the configuration onto it. This can all be done remotely from a machine that is external to the farm, as long as that machine has the SharePointDSC bits installed on it.

How to use the ReverseDSC Core

The ReverseDSC.Core module is the heart of the ReverseDSC process. This module defines several functions that will help you dynamically extract the DSC configuration script for each resource within a DSC module. The ReverseDSC Core is generic, meaning that it applies to any technology, not only SharePoint. In this blog article I will describe in details how you can start using the ReverseDSC Core module today and integrate it into your existing solutions. To better illustrate the process, I will be using an example where I will be extracting the properties of a given user within Active Directory using the ReverseDSC Core.

Getting Started

If you were to take a look at the content of the ReverseDSC.Core.psm1 module (https://github.com/NikCharlebois/SharePointDSC.Reverse/blob/master/ReverseDSC.Core.psm1), you would see about a dozen functions defined. The one we are truly interested in is the Export-TargetResource one. This method takes in two mandatory parameters, the name of the DSC resource we wish to “Reverse”, and the list of mandatory parameter for the Get-TargetResource of that same resource. The mandatory parameters are essential because without them, the Get-TargetResource is not able to determine what instance of the resource we wish to obtain the current state for. The third optional parameter lets you define a DependsOn clause in the case the current instance depends on another one. However, let us not worry about that parameter for our current example.

As mentioned previously, for the sake of our example, we want to extract the information about the various users in our Active Directory. Active Directory users are represented by the MSFT_xADUser resource, so you will need to make sure the xActiveDirectory module is properly installed on the machine you are about to extract the information from.

Let us now take a look at the Get-TargetResource function of the MSFT_xADUser resource. The function only requires two mandatory parameters: DomainName and UserName.

Therefore we need to pass these two mandatory parameters to our Export-TargetResource function. Now, in my case, I do know for a fact that I have a user in my Active Directory named “John Smith”, who has a username of “contoso\JSmith”. In my case, I have a local copy of the ReverseDSC.Core.psm1 module located under c:\temp I can then initiate the ReverseDSC process for that user by calling the following lines of PowerShell:

Import-Module -Name "C:\temp\ReverseDSC.Core.psm1" -Force
$mandatoryParameters = @{DomainName="contoso.com"; UserName="JSmith"}
Export-TargetResource -ResourceName xADUser -MandatoryParameters $mandatoryParameters

Executing these lines of code will produce the following output:

Since the Export-TargetResource function simply outputs the resulting DSC resource block as a string, you would need to capture it in a variable somewhere and manually build your resulting DSC configuration. The following modifications to our script will allow us to build the resulting Desired Configuration Script and save it locally on disk, in my case under C:\temp\:

Import-Module -Name "C:\temp\ReverseDSC.Core.psm1" -Force
$output = "Configuration ReverseDSCDemo{`r`n    Import-DSCResource -ModuleName xActiveDirectory`r`n    Node localhost{`r`n"
$mandatoryParameters = @{DomainName="contoso.com"; UserName="JSmith"}
$output += Export-TargetResource -ResourceName xADUser -MandatoryParameters $mandatoryParameters
$output += "    }`r`n}`r`nReverseDSCDemo"
$output | Out-File "C:\Temp\ReverseDSCDemo.ps1"

Running this will generate the following DSC Configuration script:

Configuration ReverseDSCDemo{
Import-DSCResource -ModuleName xActiveDirectory
Node localhost{
xADUser baf586dd-3c2c-4131-9267-d4d8fb1d5d01
{
CannotChangePassword = $True;
HomePage = "http://Nikcharlebois.com";
DisplayName = "John Smith";
Description = "John's Account";
Notes = "These are my notes";
Office = "Basement of the building";
State = "Quebec";
Fax = "";
JobTitle = "Aquatic Plant Watering";
Country = "";
Division = "";
Initials = "";
POBox = "23";
HomeDirectory = "";
EmployeeID = "";
LogonScript = "";
GivenName = "John";
EmployeeNumber = "";
UserPrincipalName = "jsmith@contoso.com";
ProfilePath = "";
StreetAddress = "55 Mighty Suite";
CommonName = "John Smith";
Path = "CN=Users,DC=contoso,DC=com";
HomePhone = "555-555-5555";
City = "Gatineau";
Manager = "CN=Nik Charlebois,CN=Users,DC=contoso,DC=com";
MobilePhone = "";
Pager = "";
Company = "Contoso inc.";
HomeDrive = "";
OfficePhone = "555-555-5555";
Surname = "Smith";
Enabled = $True;
DomainController = "";
PostalCode = "J8P 2A9";
IPPhone = "";
EmailAddress = "JSmith@contoso.com";
PasswordNeverExpires = $True;
UserName = "JSmith";
DomainName = "contoso.com";
Ensure = "Present";
Department = "Plants";
}
}
}
ReverseDSCDemo

Executing the resulting ReverseDSCDemo.ps1 script will generate a MOF file that can be used with PowerShell Desired State Configuration.

Recap

The ReverseDSC Core allows you to easily extract all the parameters of an instance of a resource by simply specifying the few mandatory parameters its Get-TargetResource function requires. It does not take care of scanning all instance on an environment, this should be left to the user to create that script. It is not to say that we will not be looking at ways of automating this in the future, but the current focus is to keep it as unit calls.

For a DSC Resource to work with the ReverseDSC Core, you need to ensure it has a well written Get-TargetResource function that returns the proper parameters. This should already be the case for any well written resources out there, but it is not always the case. In the past, most of the development effort for new DSC Resource was put on the Set-TargetResource function to ensure the “Forward” DSC route was working well. However, in order for the whole DSC process to properly work, it is crucial that your Get-TargetResource function be as complete as possible. After all, the Test-TargetResource function also depends on it to check whether or not your machine has shifted away from its desired state.

SharePoint Reverse DSC

In my previous blog article I introduced the concept of Reverse DSC, which is nothing more than a dynamic way of extracting a Desired State Configuration (DSC) Script that represents the Current State of any given environment. In this blog article, I will guide you through the process of executing the Reverse DSC script against an existing SharePoint 2013 or 2016. Please note that while PowerShell v4 is supported by the SharePoint Reverse DSC, it is highly recommended that you upgrade your environment to PowerShell v5 to be able to fully leverage the goodness of the DSC engine.

While it is still not officially decided how the SharePoint Reverse DSC script will be distributed, I have taken the decision to go ahead and offer a temporary distribution via my Blog. A copy of the current script can be obtained here:

This version of the script currently supports the latest available bits of the SharePointDSC Module (1.5.0.0). The package is made of two files:

  • ReverseDSC.Util.psm1, the core ReverseDSC module which is generic to all DSC Modules (not just SharePoint)
  • SharePointDSC.Reverse.ps1, the main SharePoint specific PowerShell script responsible for extracting the current state of a SharePoint Environment.

As mentioned above, this script is optimized to run under an environment that has PowerShell v5. To determine what version of PowerShell you are using, simply run the following PowerShell command:

$PSVersionTable.PSVersion.Major

If you are running version 4, no worries, you can either upgrade to version 5 by downloading and installing the Windows Management Framework (WMF) 5.0 on your various servers (which will cause downtime). In case your organization is not yet ready to upgrade to WMF 5, you can either download and install the PackageManagement module for PowerShell 4, or simply manually install the SharePointDSC 1.5.0.0 module onto each of your servers. PackageManagement will simply be used to automatically download and install the proper version of the SharePointDSC module from the PowerShell gallery, assuming your server has internet connectivity (which it most likely won’t anyway).

How to Use

  1. Extract the content of the package onto one of the SharePoint server (Web Front-End or Application server). Make sure that both the .psm1 and .ps1 files are in the same folder.
  2. In an elevated PowerShell session (running as administrator), execute the SharePointDSC.Reverse.ps1 script.
  3. If you do not have the required version of the SharePointDSC module installed, you will be prompted to automatically download it or not. Note that this requires your server to have internet connectivity. (Note that I recommend you manually get the module v1.5.0.0. onto you server)
  4. When prompted to provide Farm admin credentials, simply enter credentials for any account that has Farm Admin privileges on your farm.
  5. The script may prompt you several times to enter credentials for various Managed Accounts in your environment. This is required in order for DSC to be able to retrieve any Password Change Schedules associated with your managed accounts. Simply provide the requested credentials for each prompt.
  6. The script will scan through all components supported by the SharePointDSC module and then compile the resulting DSC Configuration Script. Once finished, it will prompt you to specify the path to an existing folder where the resulting .ps1 DSC Configuration Script will be saved.
  7. The DSC Configuration Script will be saved with the name “SP-Farm.DSC.ps1” under the specified folder path. You can open the .ps1 file to take a close look at its content. the top comments section will provide insights about the Operating System versions, the SQL Server Versions, and all the patches installed in your farm.
  8. To validate that the Reverse DSC process was successful, simply execute the resulting SP-Farm.DSC.ps1 file. It will prompt you to pick a passphrase and will automatically compile a .meta.mof and a .mof file for each of the server in your farm.

Now that you have your resulting .MOF files, you can use them to replicate your environment to another location on-premises, upload the resulting .ps1 into Azure Automation to create a replica of your environment in the cloud, or on-board your existing environment onto DSC. The next Blog post in this series will go through the steps you need to take to on-board an existing SharePoint environment onto DSC using the ReverseDSC process.