SharePointDSC Starter Template for SharePoint 2019

This article provides a base template that will allow you to deploy a SharePoint 2019 farm using PowerShell Desired State Configuration (DSC). This article is meant to be an evolutive process, and will get updated to always reflect the latest version of the SharePointDSC module. It currently uses the v3.0-development branch that is currently in preview and which is the only branch that supports SharePoint 2019 at the moment. The public version of SharePointDSC that is available on the PowerShell Gallery does not offer support for SharePoint 2019. For more information about this SharePointDSC preview, please refer to my Introducing SharePointDSC Support for SharePoint 2019 article.

The script was originally written to target Azure Automation, but can easily be used for on-premises deployments. The only modifications required for on-premises support would be to change the Get-AutomationPSCredential calls to Get-Credential ones instead. This script currently assumes that you have a 3-servers environment: 1 Web Front-End, 1 Application server and 1 Search server. If you wish to deploy such an environment in your Azure subscription, I’d recommend you using the following ARM templates: SharePoint Blank Templates. Those are the ARM templates I always use when doing workshops, demos or conferences. They will take care of automatically create a domain controller, a SQL Server, and the 3 SharePoint servers, with nothing installed on them. The following users will be created at deployment time:

  • U: lcladmin P: Pass@word!123
  • U: sp_setup P: Pass@word!123
  • U: sp_farm P: Pass@word!123
  • U: sp_services P: Pass@word!123
  • U: sp_search P: Pass@word!123
  • U: sharepointadmin P: Pass@word!123

The DSC script will take care of downloading all prerequisites locally, install the French and Dutch language packs, create a Web Application with its site collection, and create services applications, all by leveraging the power of PowerShell Desired State Configuration

The Script

configuration Starter3Servers
{
    $credsLocalAdmin  = Get-AutomationPSCredential -Name "LocalAdmin"
    $credsDomainAdmin = Get-AutomationPSCredential -Name "DomainAdmin"
    $credsSPFarm      = Get-AutomationPSCredential -Name "FarmAccount"
    $credsSPSetup     = Get-AutomationPSCredential -Name "SetupAccount"
    $credsSPServices  = Get-AutomationPSCredential -Name "SPServices"
    $credsSPSearch    = Get-AutomationPSCredential -Name "SPSearch"
    $credsSPAdmin     = Get-AutomationPSCredential -Name "SharePointAdmin"

    Import-DscResource -ModuleName "SharePointDSC"  -Moduleversion "3.0.0.0"
    Import-DscResource -ModuleName "xDownloadFile"  -ModuleVersion "1.0"
    Import-DscResource -ModuleName "xDownloadISO"   -ModuleVersion "1.0"
    Import-DscResource -ModuleName "xPendingReboot" -ModuleVersion "0.4.0.0"

    Node $AllNodes.NodeName
    {
        xDownloadISO DownloadBits
        {
            SourcePath               = "https://download.microsoft.com/download/8/1/4/8144DA0D-FB9A-48B8-B56E-2C12E0C30079/en-us/16.0.10711.37301_OfficeServer_none_ship_x64_en-us_dvd/officeserver_en-us.img"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadISO FrenchLPISO
        {
            SourcePath               = "https://spdsctap.blob.core.windows.net/spdsc/LP-French.iso"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "LP\French\"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadISO DutchLPISO
        {
            SourcePath               = "https://spdsctap.blob.core.windows.net/spdsc/LP-Deutch.iso"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "LP\Dutch"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile AppFabricKBDL
        {
            SourcePath               = "https://download.microsoft.com/download/F/1/0/F1093AF6-E797-4CA8-A9F6-FC50024B385C/AppFabric-KB3092423-x64-ENU.exe"
            FileName                 = "AppFabric-KB3092423-x64-ENU.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            PsDscRunAsCredential     = $credsLocalAdmin
            DependsOn                = "[xDownloadISO]DownloadBits"
        }

        xDownloadFile MicrosoftIdentityExtensionsDL
        {
            SourcePath               = "http://download.microsoft.com/download/0/1/D/01D06854-CA0C-46F1-ADBA-EBF86010DCC6/rtm/MicrosoftIdentityExtensions-64.msi"
            FileName                 = "MicrosoftIdentityExtensions-64.msi"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]AppFabricKBDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile MSIPCDL
        {
            SourcePath               = "https://download.microsoft.com/download/3/C/F/3CF781F5-7D29-4035-9265-C34FF2369FA2/setup_msipc_x64.exe"
            FileName                 = "setup_msipc_x64.msi"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]MicrosoftIdentityExtensionsDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile SQLNCLIDL
        {
            SourcePath               = "https://download.microsoft.com/download/B/E/D/BED73AAC-3C8A-43F5-AF4F-EB4FEA6C8F3A/ENU/x64/sqlncli.msi"
            FileName                 = "sqlncli.msi"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]MSIPCDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile WcfDataServices56DL
        {
            SourcePath               = "http://download.microsoft.com/download/1/C/A/1CAA41C7-88B9-42D6-9E11-3C655656DAB1/WcfDataServices.exe"
            FileName                 = "WcfDataServices56.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]SQLNCLIDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile AppFabricDL
        {
            SourcePath               = "http://download.microsoft.com/download/A/6/7/A678AB47-496B-4907-B3D4-0A2D280A13C0/WindowsServerAppFabricSetup_x64.exe"
            FileName                 = "WindowsServerAppFabricSetup_x64.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]WcfDataServices56DL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile DotNet472
        {
            SourcePath               = "https://go.microsoft.com/fwlink/?linkid=863265"
            FileName                 = "NDP472-KB4054530-x86-x64-AllOS-ENU.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]AppFabricDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile SynchronizationDL
        {
            SourcePath               = "http://download.microsoft.com/download/E/0/0/E0060D8F-2354-4871-9596-DC78538799CC/Synchronization.msi"
            FileName                 = "Synchronization.msi"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]DotNet472"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile MSVCRT141
        {
            SourcePath               = "https://aka.ms/vs/15/release/vc_redist.x64.exe"
            FileName                 = "vc_redist.x64.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath +  "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]SynchronizationDL"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        xDownloadFile MSVCRT11
        {
            SourcePath               = "https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe"
            FileName                 = "vcredist_x64.exe"
            DestinationDirectoryPath = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles"
            DependsOn                = "[xDownloadFile]MSVCRT141"
            PsDscRunAsCredential     = $credsLocalAdmin
        }

        Group GrantSetupAccountLocalAdmin
        {
            GroupName            = "Administrators"
            Ensure               = "Present"
            MembersToInclude     = $credsSPSetup.UserName
            PsDscRunAsCredential = $credsLocalAdmin
            DependsOn            = "[xDownloadFile]MSVCRT11"
        }

        SPInstallPrereqs SharePointPrereqInstall
        {
            IsSingleInstance     = "Yes"
            InstallerPath        = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstaller.exe"
            OnlineMode           = $false
            SQLNCli              = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\sqlncli.msi"
            Sync                 = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\Synchronization.msi"
            AppFabric            = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\WindowsServerAppFabricSetup_x64.exe"
            IDFX11               = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\MicrosoftIdentityExtensions-64.msi"
            MSIPCClient          = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\setup_msipc_x64.msi"
            WCFDataServices56    = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\WcfDataServices56.exe"
            MSVCRT11             = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\vcredist_x64.exe"
            MSVCRT141            = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\vc_redist.x64.exe"
            KB3092423            = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\AppFabric-KB3092423-x64-ENU.exe"
            DotNet472            = $ConfigurationData.SharePoint.Settings.BinaryPath + "prerequisiteinstallerfiles\NDP472-KB4054530-x86-x64-AllOS-ENU.exe"
            Ensure               = "Present"
            DependsOn            = "[Group]GrantSetupAccountLocalAdmin"
            # For On-prem - SXSPath = "D:\sources\sxs"
            PsDscRunAsCredential = $credsSPSetup
        }

        xPendingReboot AfterPrereqInstall
        {
            Name                 = "AfterPrereqInstall"
            DependsOn            = "[SPInstallPrereqs]SharePointPrereqInstall"
            PsDscRunAsCredential = $credsDomainAdmin
        }

        SPInstall SharePointInstall
        {
            IsSingleInstance     = "Yes"
            BinaryDir            = $ConfigurationData.SharePoint.Settings.BinaryPath
            ProductKey           = $ConfigurationData.SharePoint.Settings.ProductKey
            Ensure               = "Present"
            DependsOn            = "[xPendingReboot]AfterPrereqInstall"
            PsDscRunAsCredential = $credsSPSetup
        }

        SPInstallLanguagePack DutchLanguagePack
        {
            BinaryDir            = $ConfigurationData.SharePoint.Settings.BinaryPath + "LP\Dutch"
            DependsOn            = "[SPInstall]SharePointInstall"
            PsDscRunAsCredential = $credsSPSetup
        }

        SPInstallLanguagePack FrenchLanguagePack
        {
            BinaryDir            = $ConfigurationData.SharePoint.Settings.BinaryPath + "LP\French"
            DependsOn            = "[SPInstall]SharePointInstall"
            PsDscRunAsCredential = $credsSPSetup
        }

        xPendingReboot AfterSPInstall
        {
            Name                 = "AfterSPInstall"
            DependsOn            = "[SPInstall]SharePointInstall"
            PsDscRunAsCredential = $credsDomainAdmin
        }

        SPFarm SharePointFarm
        {
            IsSingleInstance          = "Yes"
            Ensure                    = "Present"
            FarmConfigDatabaseName    = "SP_Config"
            DatabaseServer            = $ConfigurationData.SharePoint.Settings.DatabaseServer
            FarmAccount               = $credsSPFarm
            Passphrase                = $credsSPFarm
            AdminContentDatabaseName  = "SP_Admin"
            RunCentralAdmin           = $Node.RunCentralAdmin
            CentralAdministrationPort = "7777"
            ServerRole                = $Node.ServerRole
            PSDSCRunAsCredential      = $credsSPSetup
            DependsOn                 = "[SPInstall]SharePointInstall"
        }

        if ($node.RunCentralAdmin -eq $true)
        {
            SPManagedAccount SPFarmAccount
            {
                AccountName            = $credsSPFarm.UserName
                Account                = $credsSPFarm
                PSDSCRunAsCredential   = $credsSPSetup
                DependsOn              = "[SPFarm]SharePointFarm"
            }

            SPManagedAccount SPServices
            {
                AccountName            = $credsSPServices.UserName
                Account                = $credsSPServices
                PSDSCRunAsCredential   = $credsSPSetup
                DependsOn              = "[SPFarm]SharePointFarm"
            }

            SPManagedAccount SPSearch
            {
                AccountName            = $credsSPSearch.UserName
                Account                = $credsSPSearch
                PSDSCRunAsCredential   = $credsSPSetup
                DependsOn              = "[SPFarm]SharePointFarm"
            }

            SPManagedAccount SPAdmin
            {
                AccountName            = $credsSPAdmin.UserName
                Account                = $credsSPAdmin
                PSDSCRunAsCredential   = $credsSPSetup
                DependsOn              = "[SPFarm]SharePointFarm"
            }

            SPWebApplication Root
            {
                Ensure                 = "Present"
                Name                   = "Root"
                ApplicationPool        = "SharePoint - 80"
                ApplicationPoolAccount = $credsSPFarm.UserName
                WebAppUrl              = "http://root.contoso.com"
                DatabaseServer         = $ConfigurationData.SharePoint.Settings.DatabaseServer
                DatabaseName           = "Root_Content_DB"
                HostHeader             = "root.contoso.com"
                AllowAnonymous         = $false
                PSDSCRunAsCredential   = $credsSPSetup
                DependsOn              = "[SPManagedAccount]SPFarmAccount"
            }

            SPSite RootSite
            {
                Name                     = "Root Site Collection"
                Url                      = "http://root.contoso.com"
                OwnerAlias               = "contoso\lcladmin"
                ContentDatabase          = "Root_Content_DB"
                Description              = "Root Site Collection"
                Template                 = "STS#0"
                PSDSCRunAsCredential     = $credsSPSetup
                DependsOn                = "[SPWebApplication]Root"
            }

            SPWeb SubWeb1
            {
                Name                  = "Subweb1"
                Url                   = "http://root.contoso.com/subweb1"
                AddToQuickLaunch      = $true
                AddToTopNav           = $true
                Description           = "This is a subsite"
                UseParentTopNav       = $true
                UniquePermissions     = $true
                Template              = "STS#0"
                PSDSCRunAsCredential  = $credsSPSetup
                DependsOn             = "[SPSite]RootSite"
            }
        }
    }
}

The Configuration Data

$ConfigData = @{
    AllNodes = @(
        @{
            NodeName        = "SPWFE.contoso.com"
            RunCentralAdmin = $false
            ServerRole      = "WebFrontEnd"
        },
        @{
            NodeName        = "SPApp.contoso.com"
            RunCentralAdmin = $true
            ServerRole      = "Application"
        },
        @{
            NodeName        = "SPSearch.contoso.com"
            RunCentralAdmin = $false
            ServerRole      = "Search"
        },
        @{
            NodeName                    = "*"
            PSDSCAllowPlainTextPassword = $true
            PSDSCAllowDomainUser        = $true
        }
    )
    SharePoint = @{
        Settings = @{
            DatabaseServer = "SPSQL"
            BinaryPath     = "C:\SP2019\"
            ProductKey     = "M692G-8N2JP-GG8B2-2W2P7-YY7J6"
        }
    }
}

4 thoughts on “SharePointDSC Starter Template for SharePoint 2019

  1. Any way to use PowerShell to get the latest SharePointDsc? Currently published is only 2.4.0.0 and I can’t find a method to install the package. I could manually download from Github of course, but is there a better method to get the 3.0.0.0?

  2. In the SPFarm SharePointFarm configuration, I think it should be: ServerRole = $node.ServerRole
    There is also no DependsOn in the SPFarm section, do I need it, or does it detect automatically that it can only create the farm after SharePoint binaries are installed?

    1. Hans, you are absolutely right regarding the SPFarm section. The article has been updated accordingly. As for the DependsOn clause for SPFarm, you could add it if you wished. If the Binaries are not able to install, then SPFarm would indeed throw an error with the current script. Because of the items order in the config script, SPInstall will always be executed before SPFarm, so the Depends Clause would simply help in reducing the overall number of errors in case it fails. Best practice wants you to have proper dependencies defined, so again here it was a miss in my original script. Thanks for pointing it out.

Leave a Reply

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