PowerShell Desired State Configuration

Instead of speaking about PowerShell 5.0 which is offered to us by Microsoft since twodays, I’ll talk today about Desired State Configuration (DSC) the killer feature of PowerShell 4.0. I won’t talk about the rythm of the PowerShell releases 😀

What’s DSC ?

Do you know puppet ? i’m not talking about one of the scariest Stephen King book: Chucky, but the automation tools to deliver and configure IT infrastructure. Desired State Configuration is the Microsoft version of puppet 

Why you should use DSC ?

System administrators worries daily work is ensure that Windows Servers are always up t o date, are always secured and nothing is doing wrong a screwed delivery of installation of Windows.

Theses bring many actions, many processes some of them can be automatized, some of them no. Hopefully since the start of PowerShell most of them can be autmatized, but building a script to handle the configuration and the provisionning of a server is fastidious and every little change in the delivery cost longs hours of work scripting.

Also the check of this configuration is important to be sure that users have not installed crappy tools, and to ensure that your IIS farm is not corrupted.

More specifically, the use of DSC to deliver cloud infrastructure, is in my opinion where you’ll have the more improvement. It’ll cost less hours/days/months of scripting to ensure a proper system configuration why DSC resources reusables for all customers. Just use a Windows vhd/vmdk and with DSC you’ll have proper configuration.

How DSC works ?

Here we go ! Let’s talk about PowerShell 🙂

At first, let’s see cmdlets for DSC

Get-Command -Module PSDesiredStateConfiguration
CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Function        Configuration                                      PSDesiredStateConfiguration
Function        Get-DscConfiguration                               PSDesiredStateConfiguration
Function        Get-DscLocalConfigurationManager                   PSDesiredStateConfiguration
Function        Get-DscResource                                    PSDesiredStateConfiguration
Function        New-DSCCheckSum                                    PSDesiredStateConfiguration
Function        Restore-DscConfiguration                           PSDesiredStateConfiguration
Function        Test-DscConfiguration                              PSDesiredStateConfiguration
Cmdlet          Set-DscLocalConfigurationManager                   PSDesiredStateConfiguration
Cmdlet          Start-DscConfiguration                             PSDesiredStateConfiguration

We have functions and cmdlets available. To start a DSC configuration, we first need to build a MOF file which will contains the DS Configuration. To ensure this task, we will use the Configuration function. DSC effectively use Managed Object Format file, a standard created by DMTF (Distributed Management Task Force) – if you read this blog ofently, you already have heard of this – in order to standardize the automation for ALL plateforms, this means, you’ll can in the future handle non Windows deployments, well maybe not in the few weeks 😉

If you launch the following one-liner, you’ll see that MOF are closely connected to CIM

Get-DscResource | Select Name, Properties | ft -AutoSize

DSC2

By the way, why MOF and CIM are related ? The reason is explained on the DMTF site:

The Common Information Model (CIM) is described in the DMTF’s Managed Object Format (MOF), a language based on IDL (the Object Management Group’s Interface Definition Language). The MOF syntax is a way to describe object-oriented class and instance definitions in textual form, with the goals of human readability and parsing by a compiler. The main components of a MOF specification are textual descriptions of element qualifiers (meta-data about classes, properties, methods, etc.), comments and compiler directives, and the specific class and instance definitions that convey the semantics of the CIM Schema.

By default the resources availables for creating a MOF file for DSC in Configuration function are the followings:

Name           Properties
----           ----------
File           {DestinationPath, Attributes, Checksum, Contents...}
Archive        {Destination, Path, Checksum, DependsOn...}
Environment    {Name, DependsOn, Ensure, Path...}
Group          {GroupName, Credential, DependsOn, Description...}
Log            {Message, DependsOn}
Package        {Name, Path, ProductId, Arguments...}
Registry       {Key, ValueName, DependsOn, Ensure...}
Script         {GetScript, SetScript, TestScript, Credential...}
Service        {Name, BuiltInAccount, Credential, DependsOn...}
User           {UserName, DependsOn, Description, Disabled...}
WindowsFeature {Name, Credential, DependsOn, Ensure...}
WindowsProcess {Arguments, Path, Credential, DependsOn...}

Microsoft Server products releasing “waves” of additional resources, for today, 3 kits are availables :

  • Resource kit wave 1: http://blogs.msdn.com/b/powershell/archive/2013/12/26/holiday-gift-desired-state-configuration-dsc-resource-kit-wave-1.aspx
  • Resource kit wave 2: http://blogs.msdn.com/b/powershell/archive/2014/02/07/need-more-dsc-resources-announcing-dsc-resource-kit-wave-2.aspx
  • Resource kit wave 3: http://blogs.msdn.com/b/powershell/archive/2014/03/28/dsc-resource-kit-wave-3.aspx

/!\ Resources have to be installed on every server/computer you want to configure !

PowerShell team specify that it’s in the best practice to copy DSC modules in “C:\Program Files\WindowsPowerShell\Modules\PSDesiredStateConfiguration”. Once copied, the Get-Resource has improved is result !

Get-DscResource | Format-wide -Property Name -Column 3
File                                    Archive                                 Environment
Group                                   Log                                     Package
Registry                                Script                                  Service
User                                    WindowsFeature                          WindowsProcess
xADDomain                               xADDomainController                     xADUser
xWaitForADDomain                        xComputer                               xDatabase
xDBPackage                              xCluster                                xWaitForCluster
MSFT_xFileDirectory                     xVHD                                    xVhdFile
xVMHyperV                               xVMSwitch                               xDNSServerAddress
xFirewall                               xIPAddress                              xArchive
xDSCWebService                          xPackage                                xPSEndpoint
xRemoteFile                             xService                                xWindowsProcess
xRDRemoteApp                            xRDSessionCollection                    xRDSessionCollectionConfiguration
xRDSessionDeployment                    xSmbShare                               xSqlHAEndPoint
xSqlHAGroup                             xSqlHAService                           xSqlServerInstall
xWaitForSqlHAGroup                      MSFT_xWebBindingInformation             xWebApplication
xWebAppPool                             xWebConfigKeyValue                      xWebsite
xWebVirtualDirectory

I won’t explain each of the resource, they are explained in PowerShell Team blog 😉

How to create configuration script ?

As we said earlier in this post, the goal is to “execute” MOF file on computer/server. To create this MOF file, we have to write a configuration script.

Basically, a configuration script is just a function. Parameters are possible, modules can be loaded, etc…

Each resource block can have properties, to list all, nothing complicated here. In bonus, Microsoft gave us for each resource the syntax to use and work with !

(Get-DscResource -Name file).Properties
Name                          PropertyType                                    IsMandatory Values
----                          ------------                                    ----------- ------
DestinationPath               [string]                                               True {}
Attributes                    [string[]]                                            False {Archive, Hidden, ReadOnly...
Checksum                      [string]                                              False {CreatedDate, ModifiedDate...
Contents                      [string]                                              False {}
Credential                    [PSCredential]                                        False {}
DependsOn                     [string[]]                                            False {}
Ensure                        [string]                                              False {Absent, Present}
Force                         [bool]                                                False {}
MatchSource                   [bool]                                                False {}
Recurse                       [bool]                                                False {}
SourcePath                    [string]                                              False {}
Type                          [string]                                              False {Directory, File}
Get-DscResource -Name file -Syntax
File [string] #ResourceName
{
    DestinationPath = [string]
    [ Attributes = [string[]] { Archive | Hidden | ReadOnly | System }  ]
    [ Checksum = [string] { CreatedDate | ModifiedDate | SHA-1 | SHA-256 | SHA-512 }  ]
    [ Contents = [string] ]
    [ Credential = [PSCredential] ]
    [ DependsOn = [string[]] ]
    [ Ensure = [string] { Absent | Present }  ]
    [ Force = [bool] ]
    [ MatchSource = [bool] ]
    [ Recurse = [bool] ]
    [ SourcePath = [string] ]
    [ Type = [string] { Directory | File }  ]
}

Ok, now that i know the syntax for a file block,  let’s script a configuration to check the presence of a specific file.

configuration Software {

    param (
        [String[]]$Server=$env:computerName
    )

    Node $Server {

        File SetupFile {

            Ensure          = "present"
            SourcePath      = "F:\Setup.exe"
            DestinationPath = "C:\Temp"
            Type            = "File"

        }
    }
}

Software -Server ("srv01","srv02")

Note that i declare the parameter $Server as a strings array, so we can’t procude MOF file for multiple server with one command, pretty cool isn’t it ?

To generate MOF files for each server just execute the ps1 file with this script

PS C:\PowerShell> .\dsc.ps1

    Répertoire : C:\PowerShell\Software

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        06/04/2014     09:41       1322 srv01.mof
-a---        06/04/2014     09:41       1322 srv02.mof

Both MOF files are created, one for each node.

In addition, you can create depencies between blocks in your configuration script with the property DependsOn !

 

 How execute configuration ?

Nothing complicated here.

  1. Be sure that PowerShell remoting is enable on target(s)
  2. Ensure that execution policy is set correctly
  3. Execute the following command DSC Configuration
Start-DscConfiguration -Path C:\PowerShell\Software -verbose
Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
14     Job14           Configuratio... Running       True            PC-MAISON            Start-DscConfiguration...
COMMENTAIRES : Le temps nécessaire à l'exécution du travail de configuration est de 0.008 secondes

As you see, it’ll create a job and execute it, so you can all long during the configuration, control the flow of the job and monitoring it.

Another option exists: DSC Pull Server. DSC Pull Server will be the main place where server deployed will get their configuration to be executed locally.

To do this, we’ll use DSC to configure the Pull Server

configuration PullServer {

    Import-DSCResource -ModuleName xPSDesiredStateConfiguration

    Node srvpull.test.lab {

        WindowsFeature DSCServiceFeature {

            Ensure = "Present"
            Name   = "DSC-Service"

        }

        xDscWebService PSDSCPullServer  {

            Ensure                  = "Present"
            EndpointName            = "DSCPullServer"
            Port                    = 8080
            PhysicalPath            = "$env:SystemDrive\inetpub\wwwroot\PSDSCPullServer"
            CertificateThumbPrint   = "AllowUnencryptedTraffic"
            ModulePath              = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Modules"
            ConfigurationPath       = "$env:PROGRAMFILES\WindowsPowerShell\DscService\Configuration"
            State                   = "Started"
            DependsOn               = "[WindowsFeature]DSCServiceFeature"

        }

        xDscWebService PSDSCComplianceServer {

            Ensure                  = "Present"
            EndpointName            = "DSCComplianceServer"
            Port                    = 8081
            PhysicalPath            = "$env:SystemDrive\inetpub\wwwroot\PSDSCComplianceServer"
            CertificateThumbPrint   = "AllowUnencryptedTraffic"
            State                   = "Started"
            IsComplianceServer      = $true
            DependsOn               = ("[WindowsFeature]DSCServiceFeature","[xDSCWebService]PSDSCPullServer")

        }
    }
}

PullServer

The MOF files will have a CheckSum, this will be used by client to ensure the validity of the file, alos the MOF file have to be a GUID.

First generate the MOF file on the remote server.

configuration Interface {

    Node srv02.test.lab {

        WindowsFeature GuiShell {

			Ensure = 'Present'
			Name   = 'Server-Gui-Shell'

		}

        WindowsFeature GuiMgmt {

            Ensure = 'Present'
            Name   = 'Server-Gui-Mgmt-Infra'
        }

    }
}

Interface

After that rename it with a GUID name.

$guid = [guid]::NewGuid()

And now copy it to the “configuration” directory of pull server, it’s now the time to regenerate a Checksum on the pull server.

New-DscCheckSum $guid.mof

And now, execute the configuartion file to tell the node to get his configuration from the pull server.

Configuration PullMode {

	param(
        [string]$guid
    )

	Node srv02.test.lab	{

        LocalConfigurationManager {

			ConfigurationMode = 'ApplyOnly'
			ConfigurationID = $guid
			RefreshMode = 'Pull'
			DownloadManagerName = 'WebDownloadManager'
			DownloadManagerCustomData = @{
				ServerUrl               = 'http://srvpull.test.lab:80/PSDSCPullServer.svc';
				AllowUnsecureConnection = 'true' }

		}
	}
}

PullMode –guid $guid
Set-DSCLocalConfigurationManager -Computer srv02.test.lab -Path ./PullMode

Here we go, every 15 minutes, the node will get his configuration updated accordingly to the GUID specified to he MOF file.

It’s also possible to use HTTPS or even a SMB share for a pull server you can find grea resources about them on the internet or check the links at the end of the post 🙂

How test configuration

Ok, we have seen how to push a configuration to a server, but how test if our server match our configuration ? And how re-push this config is case of ?

It’s very easy to use…

Test-DSCConfiguration -verbose
COMMENTAIRES : Un appel de méthode du Gestionnaire de configuration local est arrivé de l'ordinateur PC-MAISON avec le
SID utilisateur S-1-5-21-388858434-313059335-2835630657-1001.
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Début  Test     ]
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Début  Ressource]  [[File]SetupFile]
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Début  Test     ]  [[File]SetupFile]
COMMENTAIRES : [PC-MAISON] :                            [[File]SetupFile] Le fichier/répertoire associé est : C:\Temp.
COMMENTAIRES : [PC-MAISON] :                            [[File]SetupFile] Création de la liste de fichiers à partir du
cache.
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Fin    Test     ]  [[File]SetupFile]  en 0.0130
secondes.
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Fin    Ressource]  [[File]SetupFile]
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Fin    Test     ]     Opération test terminée.
L'opération a renvoyé la valeur False.
COMMENTAIRES : [PC-MAISON] : Gestionnaire de configuration local :  [ Fin    Test     ]    en 0.0280 secondes.
False

The cmdlet return True or False depends of the success 😉

If you want to test a remote server, you’ll have to declare a CIM Session and use it.

# An exemple from Get-Help
PS C:\> $Session = New-CimSession -ComputerName "Server01" -Credential ACCOUNTS\PattiFuller
PS C:\> Test-DscConfiguration -CimSession $Session

As you seen, to ensure stability on all your datacenter it’s an easy way, you’ll just have to schedule a Test task with remoting 🙂

This is all for DSC for today, i hope you’ll get time to test it: you’ll love it for sure right after 😀 DSC is a KILLER feature, and this is just the start in my option, every Microsoft products will have their ressources and be sure, that your time spent on deployments will drastrically drop down !

Regards,

Ressources that helped me for this post:

  • http://powershell.org/wp/2013/10/02/building-a-desired-state-configuration-infrastructure/
  • https://github.com/PowerShellOrg/ebooks/tree/master/DSC