SharePointDSC Documentation Improvements

Striving hard to make it easier for folks to get started with SharePointDSC, we are making changes in our documentation to categorize every resource within the module into 4 main categories:

  • Common: Resources that needs to be defined on every node in the farm with the same values for each parameters. This category includes resources such as SPInstall and SPInstallPrereqs which are responsible for installing the binaries on the server. These need to be installed on every server in a SharePoint farm, and therefore need to be defined on every node in the configuration with the same parameters’ values.
  • Distributed: This category includes the major part of the resources in the module. Distributed resources should only appear on one node in the configuration. Identify one of the node in your farm to be the master node and define all the Distributed resources on it. SharePoint being a distributed system in nature, it uses databases in the back-end to store artefacts and make them available to all servers in the farm. If Distributed resources were to be defined on more then one node, each node would write the same entries back to the database, overwriting one another, or triggering unnecessary Test-TargetResource calls which can affect performance. To better explain this concept, think about how we currently create a new SharePoint Web Application using PowerShell today; we would use the New-SPWebApplication cmdlet. You would log to one server in the farm, and run that cmdlet from there. You would not got on every single server in the farm and run the cmdlet. The same concept applies to SharePointDSC. The key take away from this change in documentation is that Distributed resources should never appear on more than one node in the configuration!
  • Specific: Specific resources need to be defined on every node in the farm, but which will most likely have different parameters’ values. For example, the SPFarm resource needs to be defined on every node in order for the servers to be joined to the farm, but each server can have different values for the RunCentralAdministration and ServerRole parameters.
  • Utility: These resources do not create or configure an actual artefact on the farm, they simply execute a sanity check or trigger an external process. For example, the SPConfigWizard is the equivalent of running PSConfig on the server. Defining this resource in your configuration does not create anything, it simply launches that external process.

the following table shows the classification of every resources in SharePointDSC as of release 2.2.0.0:

Common Distributed Specific Utility
  • SPInstall
  • SPInstallLanguagePack
  • SPInstallPrereqs
  • SPProductUpdate
  • SPAccessServiceApp
  • SPAlternateUrl
  • SPAntivirusSettings
  • SPAppCatalog
  • SPAppDomain
  • SPAppManagementServiceApp
  • SPAppStoreSettings
  • SPBCSServiceApp
  • SPCacheAccounts
  • SPContentDatabase
  • SPCreateFarm
  • SPDatabaseAAG
  • SPDesignerSettings
  • SPDiagnosticLoggingSettings
  • SPDiagnosticsProvider
  • SPExcelServiceApp
  • SPFarmAdministrators
  • SPFarmPropertyBag
  • SPFarmSolution
  • SPFeature
  • SPHealthAnalyzerRuleState
  • SPInfoPathFormsServiceConfig
  • SPIrmSettings
  • SPLogLevel
  • SPMachineTranslationServiceApp
  • SPManagedAccount
  • SPManagedMetaDataServiceApp
  • SPManagedMetaDataServiceAppDefault
  • SPManagedPath
  • SPOfficeOnlineServerBinding
  • SPOutgoingEmailSettings
  • SPPasswordChangeSettings
  • SPPerformancePointServiceApp
  • SPProjectServerAdditionalSettings
  • SPProjectServerADResourcePoolSync
  • SPProjectServerGlobalPermissions
  • SPProjectServerGroup
  • SPProjectServerLicense
  • SPProjectServerPermissionMode
  • SPProjectServerServiceApp
  • SPProjectServerTimeSheetSettings
  • SPProjectServerUserSyncSettings
  • SPProjectServerWssSettings
  • SPPublishServiceApplication
  • SPQuotaTemplate
  • SPRemoteFarmTrust
  • SPSearchAuthoritivePage
  • SPSearchContentSource
  • SPSearchCrawlerImpactRule
  • SPSearchCrawlMapping
  • SPSearchCrawlRule
  • SPSearchFileType
  • SPSearchIndexPartition
  • SPSearchResultSource
  • SPSearchServiceApp
  • SPSearchTopology
  • SPSecureStoreServiceApp
  • SPSecurityTokenServiceConfig
  • SPServiceAppPool
  • SPServiceAppProxyGroup
  • SPServiceAppSecurity
  • SPServiceIdentity
  • SPSessionStateService
  • SPShellAdmins
  • SPSite
  • SPStateServiceApp
  • SPSubscriptionSettingsServiceApp
  • SPTimerJobState
  • SPTrustedIdentityTokenIssuer
  • SPUsageApplication
  • SPUserProfileProperty
  • SPUserProfileSection
  • SPUserProfileServiceApp
  • SPUserProfileServiceAppPermissions
  • SPUserProfileSyncConnection
  • SPVisioServiceApp
  • SPWeb
  • SPWebAppAuthentication
  • SPWebAppBlockedFileTypes
  • SPWebAppGeneralSettings
  • SPWebApplication
  • SPWebApplicationAppDomain
  • SPWebApplicationExtension
  • SPWebAppPeoplePickerSettings
  • SPWebAppPermissions
  • SPWebAppPolicy
  • SPWebAppProxyGroup
  • SPWebAppSiteUseAndDeletion
  • SPWebAppSuiteBar
  • SPWebAppThrottlingSettings
  • SPWebAppWorkflowSettings
  • SPWordAutomationServiceApp
  • SPWorkflowService
  • SPWorkManagementServiceApp
  • SPBlobCacheSettings
  • SPDistributedCacheService
  • SPFarm
  • SPServiceInstance
  • SPUserProfileSyncService
  • SPConfigWizard
  • SPMinRoleCompliance

These changes in documentation will officially made public with the release of SharePointDSC 2.3.0.0 scheduled to be released on May 2nd 2018.

InfoPath Forms Services is not turned on

While working on the InfoPath Forms Service DSC Resource for SharePoint DSC, I ran into an issue where I couldn’t get access to the InfoPath Forms Service configuration page in Central Administration (General Application Settings > Configure InfoPath Forms Services). Every time I would click that link, I would get presented with a SharePoint error page with the message InfoPath Forms Services is not turned on. (see screenshot below). This was happening for both SharePoint 2013 and 2016.

InfoPath Forms Services is not turned on.

Resolution

The issue was caused by me having set a name on the InfoPath Forms Service instance while playing around with PowerShell. In order to obtain a reference to the InfoPath Forms Service in SharePoint, you need to run the following PowerShell cmdlet:

Get-SPInfoPathFormsService

Get-SPInfoPathFormsService

The picture above shows what you should get by default with a clean SharePoint installation after you run the cmdlet. In my case, I had written the DSc resource so that it can also modify the name of the instance. Note how in the picture above, we can see that the name property is empty by default. After playing with the service instance, running the same cmdlet would give me the following result:

Invalid SharePoint InfoPath Forms Service

We can see from the picture above, that I had renamed my InfoPath Forms Service to have the name “My InfoPath Forms Service“, which is what was causing my issue. Luckily for me, I still had a variable reference to my service instance in the current PowerShell session (in my case, a variable named $is). Running the following PowerShell lines emptied back the Name property of the service instance, and the InfoPath Forms Service automatically went back online.

$is = Get-SPInfoPathFormsService
$is.Name = ""
$is.Update()

After running the above lines of code, I was able to go back in Central Administration and access my InfoPath Forms Service configuration page as normal.

InfoPath Forms Service in Central Administration

Nik Charlebois

RunAs Radio Interview on ReverseDSC

Last month I had the privilege of being interviewed by Richard Campbell on the RunAs Radio podcast. Richard and I talked about the ReverseDSC project I am working on. If you are interested in the tools I am building within Microsoft, it is a great introduction to what ReverseDSC is all about. The interview goes on for about 32 minutes, and covers everything from the concept of what ReverseDSC is, to some of the new cool tools I am working on including VisualDSC which lets you visualize a DSC configuration within Visio.

Deploy an Office Online Server Environment using PowerShell DSC

In this article we will cover the process of automating the configuration of an Office Online Server 2016 (OOS) using PowerShell Desired State Configuration. The end result will be a fully working OOS environment that has the French Language Pack installed on. Language pack support is something new I recently added to the OfficeOnlineServerDSC module and which will be available starting with version 1.1.0.0 scheduled to be released on December 20th of 2017.

Prerequisites

In my case, I will be starting with a brand new Windows Server 2016 virtual machine on which I downloaded the installation binaries for OOS, as well as the French language pack. The installation media will be stored in a folder right at the root of my c:\ drive at C:\OOSInstall\:

The Language Packs executables need to be extracted in order for the new OfficeOnlineServerInstallLanguagePack resource to be able to do its job. In my case, I downloaded the French Language Pack from MSDN and stored it into C:\OOSLP\:

Office Online Server Language Pack Binaries

Office Online Server Language Pack Binaries

In order to extract the content of the Language Packs, we need to call the .exe file with /extract:. In my case I will be calling the following:

C:\OOSLP\fr_office_online_server_language_pack_march_2017_x64_10267265.exe /extract:C:\OOSLP

this will result in the following set of files to be extracted in our folder:

Extracted Office Online Server Language Pack

Extracted Office Online Server Language Pack

IMPORTANT!!!At the time of publishing this article, OfficeOnlineServerDSC 1.1.0.0 is not yet available on the PowerShell Gallery, therefore you will need to manually acquire it from https://www.GitHub.com/PowerShell/OfficeOnlineServerDSC.

DSC Script

One we have this in place, we are ready to go ahead and execute our DSC configuration script and let the magic happen:

Install-PackageProvider -Name NuGet -Force -Confirm:$false
Install-Module OfficeOnlineServerDSC -RequiredVersion 1.1.0.0 -Force -Confirm:$false -EA SilentlyContinue

Configuration OOS
{
    Import-DscResource -ModuleName OfficeOnlineServerDSC -ModuleVersion 1.1.0.0

    Node localhost
    {
        WindowsFeature WebServer
        {
            Name = "Web-Server"
            Ensure = "Present"
            IncludeAllSubFeature = $true
        }

        OfficeOnlineServerInstall OOSInstall
        {
            Ensure = "Present"
            Path = "C:\OOSInstall\setup.exe"
            DependsOn = "[WindowsFeature]WebServer"
        }

        OfficeOnlineServerInstallLanguagePack FrenchLP
        {
            Ensure = "Present"
            BinaryDir = "C:\OOSLP\"
            Language = "fr-fr"
            DependsOn = "[OfficeOnlineServerInstall]OOSInstall"
        }

        OfficeOnlineServerFarm FarmConfig
        {
            InternalUrl = "http://localhost"
            AllowHttp = $true
        }
    }
}

OOS

Start-DSCConfiguration OOS -Wait -Force -Verbose

Patching a Multi-Server Farm using PowerShell DSC and Cross-Node Dependency

While I already have an article that covers the details of how you can use PowerShell Desired State Configuration (DSC) to patch a SharePoint Farm, I wanted to write a second article that covers the process of patching a multi-server farm, while respecting a sequential upgrade logic. Starting with PowerShell 5.0 we can now specify cross-node dependencies, which mean a node can wait for a specific resource to be in the desired state on another server before proceeding with the configuration of a given resource on the local node. By combining this new feature with the SharePointDSC module, we can fully automate our sequential upgrade sequence.

Scenario

The scenario is the following: the client has a 4 servers SharePoint 2013 Farm and wishes to upgrade it to the November 2017 Cumulative Update (CU). The client’s maintenance window is on Saturdays and Sundays, between 2AM and 8AM Eastern time. The SharePoint farm had 2 Web Front-End servers (SPWFE1 & SPWFE2), and 2 Application servers (SPAPP1 & SPAPP2). The upgrade sequence should be as follow:

SharePoint Upgrade Sequence

SharePoint Upgrade Sequence

If we were to put this into words, we should be able to install the binaries on all servers in parallel. The installation process may take longer to complete on some servers compared to the others. Once that is completed on all servers, then we should be running PSConfig on one server in the farm, and whenever the process finishes on that first server, then we can go an run it on all other servers in parallel. The orange dotted lines on the diagram represent “logic gates”. The first one, indicates that you should not be running PSConfig on the first server until the binaries installation process has completed on every server in the farm. The second one, specifies that we need to wait for the PSConfig process to finish on the first server before attempting to run it on the another server. These dotted lines will be represented by the Cross-Node Dependency resources WaitForAll in our DSC script.

Script

We will start off by defining our Node structure in our configuration script. Since SPWFE2, SPAPP1 & SPApp2 all follow the same “logic”, we will put them all 3 in the same basket. The other server, SPWFE1 has unique logic and therefore needs to have its own Node definition. The skeleton for this will look like the following:

Configuration SPFarmUpdate
{
    $CredsSPFarm = Get-Credential -UserName "contoso\sp_farm" -Message "Farm Account"
    Import-DscResource -ModuleName SharePointDSC -ModuleVersion 1.9.0.0
    Node "SPWFE1"
    {
        # First Node
    }

    Node @("SPWFE2", "SPAPP1", "SPAPP2")
    {
        # Other Nodes
    }
}

Since we can go ahead and install the binaries on all 4 servers at the same time, we can put the same logic block in both Node sections. The DSC resource used to install binaries update is SPProductUpdate. Because my client’s maintenance window is on Saturdays and Sundays between 2 and 8 AM, I will make sure to specify that window in my resource block.I will also specify that I wish to shutdown all services on my server to speed up the installation process. This will cause and outage of the SharePoint server. Just to keep things clear however, I will give them different names:

Configuration SPFarmUpdate
{
    $CredsSPFarm = Get-Credential -UserName "contoso\sp_farm" -Message "Farm Account"
    Import-DscResource -ModuleName SharePointDSC -ModuleVersion 1.9.0.0
    Node "SPWFE1"
    {
        SPProductUpdate November2017CUFirstNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }
    }

    Node @("SPWFE2", "SPAPP1", "SPAPP2")
    {
        SPProductUpdate November2017CUSecondaryNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }
    }
}

Once the binaries have been installed on the servers, we need to use the SPConfigWizard DSC resource to execute PSConfig and actually apply the update to the servers and databases. Once again, I will be specifying my maintenance window to ensure DSC doesn’t trigger an upgrade during business hours, and I will give the two DSC resource block different names to better illustrate the components involved. Our script now looks like the following at this stage:

Configuration SPFarmUpdate
{
    $CredsSPFarm = Get-Credential -UserName "contoso\sp_farm" -Message "Farm Account"
    Import-DscResource -ModuleName SharePointDSC -ModuleVersion 1.9.0.0
    Node "SPWFE1"
    {
        SPProductUpdate November2017CUFirstNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }

        SPConfigWizard PSConfigFirstNode
        {
            Ensure = "Present"
            DatabaseUpgradeDays = @("sat", "sun")
            DatabaseUpgradeTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }
    }

    Node @("SPWFE2", "SPAPP1", "SPAPP2")
    {
        SPProductUpdate November2017CUSecondaryNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }

        SPConfigWizard PSConfigSecondaryNode
        {
            Ensure = "Present"
            DatabaseUpgradeDays = @("sat", "sun")
            DatabaseUpgradeTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }
    }
}

Since by default, everything in DSC is executed sequentially at the node level, there is a risk that whatever server finishes installing its binaries automatically calls PSConfig even if the other servers are still in the middle of installing their binaries, or that multiple servers try calling PSConfig at the same time. To prevent this from happening, we will use the WaitForAll DSC resource which allows us to tell a node to wait for something else to complete on another node. In our case, we will tell the First server to wait for the binaries to be done installing on all servers in the farm and we will also tell the Secondary servers not to start their PSConfig process until it is first completed on the first node. In order for us to tell a DSC resource not to execute until something else on a different server has completed, we will use the DependsOn clause, and will “point” it to the associated WaitForAll instance. I will also tell the cross-node dependency to retry and check every minutes, for a maximum of 30 minutes to see if the dependency on the other server has completed. By doing so, we now have a complete script that looks like the following:

Configuration SPFarmUpdate
{
    $CredsSPFarm = Get-Credential -UserName "contoso\sp_farm" -Message "Farm Account"
    Import-DscResource -ModuleName SharePointDSC -ModuleVersion 1.9.0.0
    Node "SPWFE1"
    {
        SPProductUpdate November2017CUFirstNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }

        WaitForAll ServersToHaveBinariesInstalled
        {
            ResourceName = "[SPProductUpdate]November2017CUSecondaryNode"
            NodeName = @("SPWFE2", "SPAPP1", "SPAPP2")
            RetryIntervalSec = 60
            RetryCount = 30
            DependsOn = "[SPProductUpdate]November2017CUFirstNode"
        }

        SPConfigWizard PSConfigFirstNode
        {
            Ensure = "Present"
            DatabaseUpgradeDays = @("sat", "sun")
            DatabaseUpgradeTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
            DependsOn = "[WaitForAll]ServersToHaveBinariesInstalled"
        }
    }

    Node @("SPWFE2", "SPAPP1", "SPAPP2")
    {
        SPProductUpdate November2017CUSecondaryNode
        {
            SetupFile = "C:\Patch\November2017CU.exe"
            ShutdownServices = $true
            BinaryInstallDays = @("sat", "sun")
            BinaryInstallTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
        }

        WaitForAll PSConfigToBeFinishedOnFirstServer
        {
            ResourceName = "[SPConfigWizard]PSConfig"
            NodeName = @("SPWFE1")
            RetryIntervalSec = 60
            RetryCount = 30
            DependsOn = "[SPProductUpdate]November2017CUSecondaryNode"
        }

        SPConfigWizard PSConfigSecondaryNode
        {
            Ensure = "Present"
            DatabaseUpgradeDays = @("sat", "sun")
            DatabaseUpgradeTime = "2:00am to 8:00am"
            PsDscRunAsCredential = $CredsSPFarm
            DependsOn = "[WaitForAll]PSConfigToBeFinishedOnFirstServer"
        }
    }
}
$ConfigData = @{
    AllNodes = @(
        @{
            NodeName = "SPWFE1"
            PSDscAllowPlainTextPassword = $true;
            PSDscAllowDomainUser = $true;
        },
        @{
            NodeName = "SPWFE2"
            PSDscAllowPlainTextPassword = $true;
            PSDscAllowDomainUser = $true;
        },
        @{
            NodeName = "SPAPP1"
            PSDscAllowPlainTextPassword = $true;
            PSDscAllowDomainUser = $true;
        },
        @{
            NodeName = "SPAPP2"
            PSDscAllowPlainTextPassword = $true;
            PSDscAllowDomainUser = $true;
        }
    )
}

SPFarmUpdate -ConfigurationData $ConfigData