Desired State Configuration - Advanced PowerShell - PowerShell in Depth, Second Edition (2015)

PowerShell in Depth, Second Edition (2015)

Part 4. Advanced PowerShell

Chapter 41. Desired State Configuration

This chapter covers

· Overview of DSC

· Installing and configuring DSC

· Creating resources

· Using DSC

Desired State Configuration (DSC) was introduced in Windows PowerShell v4 as part of the Windows Management Framework 4.0. That means it’s available on Windows Server 2008 R2 and later, although as you’ll learn in this chapter its usefulness is determined by the compatible resources available on a given computer. DSC is heavily tilted toward server management rather than client management, but as you’ll see, it has applications for clients as well.

41.1. What is DSC?

DSC is an attempt by Microsoft to provide declarative configuration management in a standards-based fashion. There’s an important distinction here: with declarative configuration, you give a computer a description of what you want that computer to look like. You don’t worry about how it gets that way—you just tell it what you want. That’s different from the more familiar imperative configuration, where you write a script that makes whatever changes necessary to make a computer look the way you want.

Here’s a noncomputer example: with imperative management, you might tell your teenaged son to go outside, get in the car, start the car, drive out of the driveway, turn left at the stop light, park at the convenience store, go inside, get a container of milk, and return to the house. You have to detail each step, and your ability to do so depends on your son knowing how to follow each step. For example, if he isn’t sure about what type of milk to buy, then you’ll have to dig even deeper and lay out the exact steps needed to do so. Imperative management makes it easy to get buried in implementation details. When the underlying technology changes—say, you buy a new car that has a pushbutton start—you have to rewrite the directions.

With declarative management, you’d simply tell your son, “Make sure there’s always at least a quarter-full container of milk in the fridge.” Your son’s underlying intelligence (hah) would kick in and make sure that was true, including running out to the store when necessary to replenish the milk. Bet you’re wishing your son had declarative management now, right?

Traditionally, Windows administrators have written scripts in the imperative mode, meaning each script had to be finely crafted and tuned, and often changed when versions of Windows changed, to perform each task. It was time-consuming as heck. With DSC, we’re shifting over to declarative management, meaning we write “scripts” that tell the computer what to be, and the computer takes care of it. And they’re not “scripts” in the traditional sense, because they contain little programming. They use a specialized set of instructions that both you and the computer can understand.

41.2. DSC architecture

DSC consists of two main layers. The configuration script is the declarative statement of what you want the computer to look like. “I want you to be a web server, I want you to have this set of files, and I want your firewall to have these ports open.” DSC compiles that script into a Managed Object Format (MOF) file. A MOF file is a text-based file that represents information in the Common Information Model (CIM), an open standard managed by the Distributed Management Task Force (DMTF). The DMTF is an industry working group, meaning we’re working in the world of open standards that can, potentially, work across platforms.

The MOF file is then deployed to the computers that you want it to apply to. We’ll cover how that happens later in this chapter, but the short story is that either a computer can pull the MOF file from a URL, or you can push the MOF file to the computer in a one-time operation.

Note

It is also possible to pull the MOF file from an SMB share but most people we’ve talked to use a URL.

Once the computer has the MOF file, DSC evaluates it on a schedule. If you pushed the file, that’s every 15 minutes by default; if you have the computer pulling the file, it’ll check every 30 minutes. That runs as a Windows Scheduled Task.

When the computer evaluates the MOF file, the second main layer of DSC comes into play: DSC resources. Each resource is, essentially, a PowerShell module that implements three specific functions. So, if the configuration MOF says, “You should be a web server,” then a DSC resource capable of installing roles and features will load up, determine whether the computer meets that criteria, and install the IIS role if needed. A DSC resource has the ability to test a configuration element as well as remediate it, or set it to a specific state (such as installed or not installed).

These resources are where the magic happens—they’re the imperative part of the equation that implements the declarative configuration that you provide. Microsoft and third-party vendors can provide these resources, so you don’t have to worry about how to implement specific configurations—you just have to tell DSC what you want done. You can also author your own DSC resources as PowerShell script modules if you like; we’ll cover that later.

Note

The usefulness of DSC depends on the availability of resources that can configure whatever it is you need. For example, DSC can’t do anything with Microsoft Exchange Server unless you have an Exchange-related set of resources. So DSC will get more useful over time, as more resources are developed and released.

Windows Management Framework 4.0 shipped with a number of basic DSC resources:

· ArchiveUncompresses zip files.

· EnvironmentManages environment variables.

· FileManages files and folders.

· GroupManages local groups.

· LogWrites to the Microsoft-Windows-Desired State Configuration/Analytic log but only when you are using push mode. In pull mode, an event appears in the log to state that the resource is writing to the log but the message doesn’t get written.

· PackageInstalls and manages Windows Installer and Setup.exe packages.

· ProcessConfigures Windows processes.

· RegistryManages Registry keys and values.

· RoleAdds and removes Windows roles and features.

· ScriptRuns a PowerShell script.

· ServiceManages Windows services.

· UserManages local user accounts.

Microsoft is releasing additional DSC resources “out-of-band,” meaning they’ll be released for download rather than made part of a future OS release. You can find the out-of-band resources on TechNet: http://gallery.technet.microsoft.com/site/search?query=dsc%20resource%20kit&f%5B0%5D.Value=dsc%20resource%20kit&f%5B0%5D.Type=SearchText&ac=2. The resources are released in waves; the appearance of a new wave is announced on the PowerShell team blog (http://blogs.msdn.com/b/powershell/). Look for posts about the DSC Resource Kit.

Note

The resources in the DSC Resource Kit are supplied as is with no warranty and the caveat that support for the Resource Kit may be withdrawn in future versions of Windows if these resources become part of the Windows Server install.

The DSC resources in the resource kit all have an “x” (for experimental) as a prefix to indicate that they are part of the Resource Kit. Another source of DSC resources, in some cases modified versions of those in the Resource Kit, can be found at https://github.com/PowerShellOrg/DSC. The resources here are all prefixed with a “c” (for community).

Note

If you’re using the experimental, or community, resources, remember that they’re supplied as is and it’s your responsibility to test them thoroughly in your environment.

Future versions of Windows and other Microsoft products may also include resources, and third-party software vendors can include or provide resources. Note that a resource may have a dependency on a specific product version. For example, if Exchange Server 2016 (assuming there is such a product) ships with a bunch of DSC resources, those might be compatible only with that version of Exchange. It’ll all depend on the product.

So how do you deploy resources once you get them? You don’t! Just as a computer can pull its MOF from a URL, DSC knows how to check a URL for resources that it’s missing. It can download them—as zip files, which it knows how to uncompress—and install them locally when it needs them. That means you get the desired MOF (or MOFs) to your computers, and they’ll take care of the rest, including grabbing any DSC resources referenced by those MOFs.

Note

You can find the official documentation for resources here: http://technet.microsoft.com/en-us/library/dn282125.aspx. The site includes a list of built-in resources as well as information on developing custom resources.

41.3. Writing the configuration script

So it all begins—assuming you have the resources you need—with a configuration script. Remember, you’re using PowerShell syntax, but the result will be translated into a MOF file, because that’s what DSC uses under the hood.

Why MOF?

This is probably a good time to explore why Microsoft decided to do the PowerShell-to-MOF conversion, rather than simply letting DSC use a PowerShell “script” directly. Why the middleman format?

Mainly because, believe it or not, Microsoft is trying hard to play better with others in the datacenter. The protocol used by PowerShell Remoting, WS-Management, is an open standard, and in theory you could use Remoting to send commands to a Linux box or an IBM AS/400, assuming someone had implemented the protocol on those operating systems. And someone could do that, if they wanted, without violating any Microsoft patents or other intellectual property—that’s what “open standard” means.

So it is with MOF. You can write a configuration script in PowerShell, turn it into a MOF, and send it to a Linux machine, provided you’re using DSC through WMF 5.0 and have loaded the appropriate packages on your Linux machine. Once WMF 5.0 is complete, you can do cross-platform managing right from PowerShell. Similarly, DSC can consume a MOF generated by something other than PowerShell, too, which means you could potentially use non-Microsoft, cross-platform software to generate MOFs and send them to a Windows computer, and DSC could follow the instructions in that MOF to configure the computer.

The investment in MOF is, therefore, all about enabling better cross-platform management of heterogeneous datacenters. That might seem totally un-Microsoft-like, but these days it’s where the company is headed.

The following listing shows a sample configuration “script.”

Listing 41.1. Sample DSC Configuration

Configuration IISWebsite

{

Node @("Server1","Server2")

{

WindowsFeature IIS

{

Ensure = "Present"

Name = "Web-Server"

}

WindowsFeature ASP

{

Ensure = "Present"

Name = "Web-Asp-Net45"

}

}

}

This example is straight from the PowerShell team’s blog (http://blogs.msdn.com/b/powershell/archive/2013/11/01/configuration-in-a-devops-world-windows-powershell-desired-state-configuration.aspx), and it’s a great introduction to writing these scripts.

It starts with the Configuration keyword. Everything related to this configuration will be contained within the Configuration construct. Within the construct, you can define one or more Node sections, and each node defines the computers that it’ll apply to. In this case, there’s one node, and it applies to two computers: Server1 and Server2. If you specify more than one computer for the node, you need to use an explicit array as you did in listing 41.1.

What’s neat is that you can also provide a script block instead of a static list of computer names. Even better, you can parameterize the node name. Here’s an example:

Configuration IISWebsite

{

Param($NodeList)

Node $NodeList

{

In this snippet, you’ve replaced the static array of computer names with a parameter, and you’ve defined that parameter in a Param() block. When you run this configuration to translate it to MOF, you can provide a list of computer names, perhaps queried from Active Directory or a database of some kind.

Within the Node block, you can have one or more configuration items. Each starts with a keyword that maps to a DSC resource—in the example, WindowsFeature. This example uses the arbitrary names IIS and ASP—DSC doesn’t care what you name the sections. Inside, each one provides the name of a role and specifies that each of them is to be present on the computer. Exactly what you put into these configuration items will vary a bit, because different resources may require different pieces of information. The practical upshot of this entire example is that the ASP.NET 4.5 and IIS roles will be installed on the server, if they aren’t already. If someone later removes one of the roles, DSC will reinstall them in its next scheduled evaluation of the MOF, if you’ve configured DSC to do so.

DSC behavior

You can configure DSC to behave in one of three ways:

· ApplyOnly—You apply the configuration to the node once and DSC has no further interest in the machine unless you create a new configuration for that machine.

· ApplyAndMonitor—You apply the configuration to the node once and DSC will monitor any changes to that configuration but won’t do anything about them.

· ApplyAndAutoCorrect—The configuration is applied to the node and DSC will periodically test the configuration (the default is 15 minutes). If it finds that the configuration has changed, it will reapply the DSC configuration to correct any changes.

These behaviors apply to both push and pull modes.

Next, you have to compile the script—technically, it’s translating, not “compiling” in the software development sense—into a MOF. To do so, you just run the configuration like you’d run a function. Assuming you’re in the PowerShell ISE, run the PowerShell configuration script, or dot-source it. Like workflows, configurations are a new command type:

PS C:\> Get-Command -CommandType Configuration

CommandType Name ModuleName

----------- ---- ----------

Configuration IISWebsite

The command even has help (see figure 41.1)!

Figure 41.1. DSC Configuration Help

Now you can run it like any other command. Assuming you left the NodeList parameter in the example, you might run this:

IISWebsite –NodeList (Get-ADComputer –filter *

–SearchBase "ou=WebServers,dc=domain,dc=pri" |

Select-Object –ExpandProperty Name)

That code would compile the MOF and target every computer in the WebServers organizational unit (OU) of the domain.pri Active Directory domain. This example assumes that your computer has the Microsoft ActiveDirectory module installed, which can be found in the Remote Server Administration Tools (RSAT).

By default PowerShell will create a subdirectory named after the configuration and store the new MOF files there. The directory is created at your current location. If you’d run the previous example in the root of C:\, you’d have seen a folder called IISWebSite and MOF for every computer from your Active Directory query. You can specify a different path using the OutPath parameter, which you can see from the help screen. The bottom line is pay attention to where you’re executing the configuration.

There’s a bit more you can put into the configuration if you like. For example, if you intend for DSC to periodically review and reapply your configuration, you can control some of its behavior for doing so. Within a Node construct, add a section of instructions to the Local Configuration Manager (or LCM, the piece of DSC that does all the actual work):

Node $NodeList

{

LocalConfigurationManager

{

RebootNodeIfNeeded = $True

ConfigurationMode = "ApplyAndAutoCorrect"

ConfigurationModeFrequencyMins = 15

}

}

Here, you’ve said that you want the final MOF applied, and then reapplied every 15 minutes. You want the target computer rebooted automatically, if that’s required to complete the configuration. This Local Configuration Manager section applies to the entire node, and it’s not unusual to place this in a Node construct of its own. You can’t have a different setting for some configuration items; whatever you set the LCM to do will be in effect for all DSC configurations targeted at that computer. If you have the LCM set to use a pull server and then manually push a configuration to that server, the node’s behavior will switch accordingly.

Note

There’s a more substantial configuration example at http://blogs.technet.com/b/keithmayer/archive/2013/11/06/why-r2-automated-server-self-provisioning-and-remediation-with-desired-state-configuration-dsc-in-powershell-4-0.aspx#.UpzV66UbM_4, if you’re interested in seeing something that installs roles, manages services, and even runs a custom script.

41.4. Getting the configuration to the computer

You have two ways of getting the MOF files to their intended computers: push or pull. Push is the easiest to explain, so we’ll start with it.

Assuming that you’ve enabled PowerShell Remoting between the computers on your network, and assuming you have permission to remote into the targeted computers, you can deploy the MOFs as follows:

Start-DSSCConfiguration –Path .\IISWebsite –Wait –Verbose –Force

This will look in the IISWebsite subfolder of the current folder for MOF files, read them to figure out who they’re supposed to go to, and then transfer them to those computers. (If you’ve specified a different output path, change the command accordingly.) The targeted computers’ LCM will then process the MOF files and start configuring the computer to look the way the MOF file demands.

Note

The IISWebsite folder name came from the name of our original Configuration element. In it, a MOF file will exist for each computer targeted. So, following our example, that would be each computer in the WebServers OU.

If you’re deploying an LCM configuration, you’ll deploy that part somewhat differently:

Set-DscLocalConfigurationManager –Path .\IISWebsite –Verbose

This code reads the MOF that you created on your computer, contacts the target computers, and updates their LCM configuration. Because this is a separate step, you’ll often build this into a completely independent Configuration element so that you can apply it to an independent block of servers.

The push model has some downsides and general considerations:

· Each configuration generates one MOF file per targeted computer, so you can wind up with a load of MOF files floating around. It’s perfectly fine to send multiple MOFs to a single target—they’ll all apply.

· The dependency on WS-Management for communications can be problematic, especially across domain boundaries where the protocol requires a lot of extra setup (like SSL certificates).

· If you want to change a configuration, you have to go through this whole process: update your script, compile the MOFs, and redeploy the MOFs. That can be a pain in the neck if you’re doing it a lot.

In most environments you’ll probably want to investigate the pull model.

Note

Microsoft made some last-minute changes to DSC between the initial release of Windows Server 2012 R2 and the general availability release. If you try pushing a configuration to a server that’s running a pre-General Availability (GA) version of Windows Server 2012 R2, you may get errors about missing DSC resources. If so, you’ll need to download and install the patch for KB2883200. See http://support.microsoft.com/kb/2883200 for more details.

41.4.1. Pull model

With the pull model, you get several advantages over the push model, and there’s only a small amount of additional setup:

· Communications happen over HTTP(S), because the pull server is just a web server.

· It’s easier for managed nodes to find and download DSC resources, because they can be hosted on the same web server.

· Multiple computers can pull from a single MOF file, making file management easier.

· Computers can periodically check their MOF files for changes, making it easier to reconfigure a bunch of computers at once.

· Computers become responsible for getting their MOFs, rather than you being responsible for making sure the push operation completed successfully.

In the pull model, you still run your configuration to produce a MOF file. That MOF simply needs to live on a web server. One difference in the configuration file—the only difference—is that you provide a globally unique identifier (GUID) instead of node names. That’s because a MOF intended for the pull model doesn’t target specific computers; it just sits on the web server waiting to be pulled. You then tell your computers which MOFs to grab, and the way you do that is by matching up the GUIDs. “Hey, servers 1 through 52, you go get configuration 1C707B86-EF8E-4C29-B7C1-34DA2190AE24, okay?” That means your node names, inside the configuration script, tend to be static:

Configuration SimpleConfigurationForPullSample

{

Node 1C707B86-EF8E-4C29-B7C1-34DA2190AE24

{

Note

If you want to create a GUID, you can use the Guidgen.exe tool. Search the internet for it (it’s free) if it isn’t already on your computer. If you know a bit about programming, you can also use the NewGuid() method of the .NET Framework Guid class.

That gets you your MOF files, suitable for pulling. How do you tell computers to pull them? Follow these steps:

1. Run your configuration, which will produce a folder that has your configuration name as the folder name, and a file that also has the GUID as the filename and that has a .MOF filename extension.

2. You need to generate a checksum for the MOF file, which will allow remote computers’ LCM to verify the integrity of the file. To do so, in PowerShell run the following command (note that MyExampleConfiguration should be replaced with your configuration folder name):

New-DSCCheckSum –Configuration .\MyExampleConfiguration
-Outpath .\MyExampleConfiguration

3. Configure each computer to pull the MOF (and its checksum) from the pull server. To do so, you have to push a MOF to the target, and that MOF has to configure the LCM. We call this the meta-configuration MOF. The following listing shows an example a meta-configuration MOF (we’ll cover these options in a bit).

Listing 41.2. A meta-configuration MOF

instance of MSFT_KeyValuePair as $keyvaluepair1
{
key = "ServerUrl";
value = "http://pullserver:8080/PSDSCPullServer/PSDSCPullServer.svc";
};

instance of MSFT_KeyValuePair as $keyvaluepair2
{
key = "AllowUnsecureConnection";
value = "true";
};

instance of MSFT_DSCMetaConfiguration
{
ConfigurationID = "1C707B86-EF8E-4C29-B7C1-34DA2190AE24";
RefreshMode="PULL";
DownloadManagerName="WebDownloadManager";
RebootNodeIfNeeded=True;
RefreshFrequencyMins = 15;
ConfigurationModeFrequencyMins = 30;
ConfigurationMode = "ApplyAndAutoCorrect";
DownloadManagerCustomData = {$keyvaluepair1,$keyvaluepair2};
};

instance of OMI_ConfigurationDocument
{
Version="1.0.0";
Author="DonJ";
};

4. Now you need to set up a pull server. That’s a normal server that has the DSC Pull Service installed (run Add-WindowsFeature Dsc-Service to install it; it’ll install IIS automatically if needed). The example we’re using uses port 8080 (so it won’t conflict with a web server that might be installed on that computer) and doesn’t require SSL. The server also needs an IIS endpoint for the DSC Pull Service (this is all handled by IIS at the end of the day).

5. Put the MOF and checksum files onto the pull server. That’s usually in C:\Program- Files\WindowsPowerShell\DscService\Configuration. Both the MOF and the checksum need to have the same base filename (e.g., My.MOF and My.MOF.checksum).

6. With your LCM meta-configuration MOF in hand (from step 3), run the following to push the LCM configuration to the targeted computers. Notice in this example that we’ve manually copied the MOF file to the computer, and we’re running this locally, so we just use localhost for the computer name and assume that the MOF file is in the current folder and named localhost.meta.mof:

Set-DscLocalConfigurationManager –ComputerName localhost
–Path . –Verbose

Note

There’s nothing stopping you from copying a meta-configuration MOF via WS-Management, but if that’s not enabled, then this one-time process is required to get pull working. If you come up with a standard MOF, you could even include that in your master image for new virtual machine deployments, making it easy to script this step.

Okay, let’s dive into that meta-configuration MOF for a bit. Here’s the first section:

instance of MSFT_KeyValuePair as $keyvaluepair1

{

key = "ServerUrl";

value =

"http://pullserver:8080/PSDSCPullServer/PSDSCPullServer.svc";

};

This code is pretty boilerplate, with the exception of pullserver:8080. That obviously needs to be the URL (and, if it isn’t port 80, the port number) of your pull server. This assumes the IIS endpoint was named PSDSCPullServer. On to the next section:

instance of MSFT_KeyValuePair as $keyvaluepair2

{

key = "AllowUnsecureConnection";

value = "true";

};

This code permits non-SSL connections. Next is the good stuff:

instance of MSFT_DSCMetaConfiguration

{

ConfigurationID = "1C707B86-EF8E-4C29-B7C1-34DA2190AE24";

RefreshMode="PULL";

DownloadManagerName="WebDownloadManager";

RebootNodeIfNeeded=True;

RefreshFrequencyMins = 15;

ConfigurationModeFrequencyMins = 30;

ConfigurationMode = "ApplyAndAutoCorrect";

DownloadManagerCustomData = {$keyvaluepair1,$keyvaluepair2};

};

That ConfigurationID is the GUID you made up when you created your configuration in the first place—this is how you tell the computer which configuration to grab. You’ll leave most of this section alone otherwise, although you can obviously adjust the refresh intervals if you like.

The creation of this meta-configuration MOF and the need to manually deploy it is the weak link in DSC right now. For new servers, it’s not that significant. For existing servers, it’s no big deal if WS-Management is working between them all. In an environment without WS-Management (e.g., PowerShell Remoting) set up, pushing this MOF to all your servers can be a bit of a hassle.

Note

You’ll find another excellent walkthrough of setting up a pull server here: http://powershell.org/wp/2013/10/03/building-a-desired-state-configuration-pull-server/. That example uses different values for things like the configuration folder, so you can see how to change those things if you like. And over at http://blogs.msdn.com/b/powershell/archive/2013/11/21/powershell-dsc-resource-for-configuring-pull-server-environment.aspx, you’ll find a DSC configuration script (and MOF) that completely handles setting up a pull server.

If you set up a DSC pull server, you’ll notice that the default folder C:\Program Files\ Windows-PowerShell\DSCService has a subfolder for modules. That’s where you put your DSC resources so that pull clients can find any resources they’re missing.

41.5. Creating and testing a DSC pull server

As we explained in the previous section, you can create a server from which other machines can pull their configuration. The pull server can be used to provide the configuration information to initially configure your server and, if that’s the way you want to operate your environment, enforce that configuration. But how do you set up a pull server?

You have three choices for creating a DSC pull server:

· Perform all the steps manually, using web-based documentation and blog posts to give you guidance.

· Run the scripts and follow the instructions in this section.

· Use the DSC Resource Kit download from http://gallery.technet.microsoft.com/xPSDesiredStateConfiguratio-417dc71d to use DSC to configure the pull server.

We recommend that you create at least one DSC pull server in a lab environment before you turn to the DSC Resource Kit. This approach will give you a full understanding of the steps being performed and aid in any troubleshooting you may need to perform.

Listing 41.3 shows the control script for creating the DSC pull server and installing the Windows Backup feature on a server to test that everything is working correctly. You could install part of RSAT if you want to use something less intrusive for your test. The script in listing 41.3 calls a number of other scripts. We’ll describe this main script first so that you have an overview of the process and then provide code and descriptions of the other scripts.

Listing 41.3. SetupDSC.ps1, the control script for creating a DSC pull server

The script starts by defining two functions. If you plan on making heavy use of DSC, you should think about making these functions always available in your PowerShell sessions. The first function creates a hash of a file by creating a value that represents the file. If the file is changed, the hash of the changed file won’t match the original and DSC will reject the file.

The function accesses .NET classes to compute the hash value. An alternative is to use the Get-FileHash cmdlet:

PS C:\> Get-FileHash -Path logv0.6.txt | fl *

Algorithm : SHA256

Hash :

A486B8873D1000541D95756F78D7256FF6971A714C58DFE3B959A6278D9A95DC

Path : C:\MyData\SkyDrive\Data\scripts\logv0.6.txt

By default, Get-FileHash uses the SHA256 algorithm as used in the script. If you just want the hash value, use the following:

PS C:\> (Get-FileHash -Path logv0.6.txt).Hash

A486B8873D1000541D95756F78D7256FF6971A714C58DFE3B959A6278D9A95DC

The second function creates a checksum file that contains the hash value you calculated in the New-Hash function. System.IO.File is used rather than the PowerShell cmdlets to ensure there isn’t any whitespace in the file that would stop it from being effective.

The main part of the script does five things through scripts that it calls:

1. Configures the DSC pull server (listing 41.4)

2. Generates a MOF file for installing Windows Backup (listing 41.5)

3. Copies the MOF file to the configuration folder

4. Creates a checksum file for the MOF file

5. Pushes the MOF file to the server

A final step is to use WMI to force the configuration on to the remote server. The WMI namespace is new for DSC. You can see the other methods on the class by using Get-CimClass:

Get-CimClass `

-Namespace root/microsoft/windows/desiredstateconfiguration `

-ClassName MSFT_DscLocalConfigurationManager |

select -ExpandProperty CimClassMethods

You can test whether the remote server has pulled, and applied, the configuration:

Get-WindowsFeature -Name Windows-Server-Backup -Computer MEMBER2

Now that you have an overview of the process, let’s look at the details, starting with configuring the pull server (including the initial installation of the DSC service).

41.5.1. Configuring a DSC pull server

You can configure a DSC pull server by using the InstallPullServerConfig.ps1 script in listing 41.4.

Note

The script in listing 41.4 uses the PSWSIISEndpoint.psm1 module. We’ll discuss the appropriate functions from that module, but we won’t cover everything in the module. The module will be available in the download for the book, as will the scripts in this chapter.

Listing 41.4. InstallPullServerConfig.ps1, a script for creating a DSC pull server

The script starts with a set of parameters . The parameters have defaults; a switch parameter always defaults to $false. The other parameters define the port for the website and the paths for the website virtual directory and DSC data folder.

A set of variables is defined . The variables specify:

· The path to source files

· Commands that perform the DSC installation

· Website and data folder configuration

Install the DSC service if required , and then create the website . Create-PSWSEndpoint is in the PSWSIISEndpoint.psm1 module (see listing 41.5). With the site created, it’s time to configure the application directory and define the DSC configuration repository . The configuration database is copied into the configuration folder.

A final set of modifications to the web config file can be made when testing. Set-Webconfig-Appsettings is also available in the PSWSIISEndpoint.psm1 module (see listing 41.6)

We mentioned the Create-PSWSEndpoint function earlier. This has been extracted from the PSWSIISEndpoint.psm1 module, as you can see in listing 41.5.

Listing 41.5. Function that creates a website

function Create-PSWSEndpoint

{

param (

# Unique Name of the IIS Site

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $site,

# Physical path for the IIS Endpoint on the machine

(under inetpub/wwwroot)

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $path,

# Web.config file

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $cfgfile,

# Port # for the IIS Endpoint

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[Int] $port,

# IIS Application Name for the Site

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $app,

# IIS App Pool Identity Type - must be one of LocalService,

LocalSystem, NetworkService, ApplicationPoolIdentity

[ValidateSet('LocalService', 'LocalSystem', 'NetworkService',

'ApplicationPoolIdentity')]

[String] $applicationPoolIdentityType,

# WCF Service SVC file

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $svc,

# PSWS Specific MOF Schema File

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $mof,

# Global.asax file [Optional]

[ValidateNotNullOrEmpty()]

[String] $asax,

# PSWS Specific Dispatch Mapping File [Optional]

[ValidateNotNullOrEmpty()]

[String] $dispatch,

# PSWS Test Specific RBAC Config File [Optional when using

the Pass-Through Plugin]

[ValidateNotNullOrEmpty()]

[String] $rbac,

# Any dependent binaries that need to be deployed to the

IIS endpoint, in the bin folder

[ValidateNotNullOrEmpty()]

[String[]] $dependentBinaries,

# Any dependent PowerShell Scipts/Modules that need to

be deployed to the IIS endpoint application root

[ValidateNotNullOrEmpty()]

[String[]] $psFiles,

# True to remove all files for the site at first, false otherwise

[Boolean]$removeSiteFiles = $false)

$script:wshShell = New-Object -ComObject wscript.shell

$script:appCmd = "$env:windir\system32\inetsrv\appcmd.exe"

$script:SrvMgr = $null

$script:netsh = "$env:windir\system32\netsh.exe"

Log ("Setting up test site at

http://$env:COMPUTERNAME.$env:USERDNSDOMAIN:$port")

ParseCommandLineAndSetupResouce -site $site -path $path

-cfgfile $cfgfile -port $port -app $app

-applicationPoolIdentityType $applicationPoolIdentityType

-svc $svc -mof $mof -asax $asax -dispatch $dispatch

-rbac $rbac -dependentBinaries $dependentBinaries

-psFiles $psFiles -removeSiteFiles $removeSiteFiles

CreateFirewallRule $port

PerformActionOnAllSites start

}

The function uses appcmd.exe to perform the configuration. You could also use the IIS cmdlets. If you don’t know your way round the IIS cmdlets, we recommend Jason Helmick’s Learn Windows IIS in a Month of Lunches (Manning, 2014).

The other function from the PSWSIISEndpoint.psm1 module you need to see is Set-Webconfig-Appsettings (listing 41.6).

Listing 41.6. Function that configures the website

function Set-Webconfig-AppSettings

{

param (

# Physical path for the IIS Endpoint on the machine

(possibly under inetpub/wwwroot)

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $path,

# Key to add/update

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $key,

# Value

[parameter(mandatory = $true)]

[ValidateNotNullOrEmpty()]

[String] $value

)

Log ("Setting options at $path")

$webconfig = Join-Path $path "web.config"

[bool] $Found = $false

if (Test-Path $webconfig)

{

$xml = [xml](get-content $webconfig);

$root = $xml.get_DocumentElement();

foreach( $item in $root.appSettings.add)

{

if( $item.key -eq $key )

{

$item.value = $value;

$Found = $true;

}

}

if( -not $Found)

{

$newElement = $xml.CreateElement("add");

$nameAtt1 = $xml.CreateAttribute("key")

$nameAtt1.psbase.value = $key;

$newElement.SetAttributeNode($nameAtt1);

$nameAtt2 = $xml.CreateAttribute("value");

$nameAtt2.psbase.value = $value;

$newElement.SetAttributeNode($nameAtt2);

$xml.configuration["appSettings"].AppendChild($newElement);

}

}

$xml.Save($webconfig)

}

The function reads the web configuration file as an XML document and then performs the required modifications before writing the new file.

Once you have your DSC pull server configured, you need to create your DSC configuration files.

41.5.2. Sample configuration

The configuration file in listing 41.7 installs the Windows Backup feature on to a server called MEMBER2.

Listing 41.7. DSC config file that installs Windows Backup

Configuration PullDemo

{

Node MEMBER2

{

WindowsFeature Backup

{

Ensure = 'Present'

Name = 'Windows-Server-Backup'

}

}

}

PullDemo

The final piece in the DSC configuration puzzle is to ensure your client machines use the pull server.

41.5.3. Configuring a machine to use the pull server

Listing 41.8 shows how to configure a machine to use the pull server.

Listing 41.8. Configuring your DSC client to use the pull server

configuration SetPullMode

{

Node MEMBER2

{

LocalConfigurationManager

{

ConfigurationMode = 'ApplyOnly'

ConfigurationID = 'e528dee8-6f0b-4885-98a1-1ee4d8e86d82'

RefreshMode = 'Pull'

DownloadManagerName = 'WebDownloadManager'

DownloadManagerCustomData = @{

ServerUrl = 'http://pull1.lab.pri:8080/PSDSCPullServer.svc';

AllowUnsecureConnection = 'true' }

RefreshFrequencyMins = 15

}

}

}

SetPullMode

Set-DSCLocalConfigurationManager -Computer MEMBER2 -Path ./SetPullMode -Verbose

The important parts of the configuration are the URL of the server and ConfigurationID, which has to be a GUID. If you’re creating new machines, you could create a new GUID using the following:

PS C:\> [Guid]::NewGuid()

Guid

----

5ef37a69-35ab-4331-ab28-6ee9f5ee8146

Alternatively, for an existing machine that’s already in Active Directory, you could use the ObjectGUID property from the computer object. You need to use Set-DSCLocalConfigurationManager to configure the machine. WMF 4 must be installed on a machine that runs Windows Server 2012 or Windows Server 2008 R2.

DSC has some resources available when you install. Others are available through the Resource Kit. You’ll end up writing your own if what you need isn’t available.

41.6. Writing DSC resources

So let’s say you need to configure a server and you don’t have a DSC resource that can do it. Maybe you want to use DSC to manage some internal line-of-business application, for example. Microsoft won’t provide a resource for that, of course, but you can author your own.

Note

We’ll provide a high-level walkthrough of DSC resource authoring. Most of the complexity of authoring comes from whatever it is you’re trying to configure; we’ll focus only on the resource structure. We’ll take the bare-bones approach to authoring; you can also check outhttp://blogs.msdn.com/b/powershell/archive/2013/11/19/resource-designer-tool-a-walkthrough-writing-a-dsc-resource.aspx for an overview of the DSC Resource Designer Tool, which can take care of some of the nitty-gritty for you.

DSC resources are merely PowerShell script modules, which we’ve covered elsewhere in this book. What makes them different is that they have to follow a specific internal pattern, providing specific, predefined functions that DSC knows to execute. Within those functions, you do whatever is necessary to implement whatever it is you’re configuring.

Note

If you’re using the documentation at http://technet.microsoft.com/en-us/library/dn249927.aspx, you’ll see the word provider a lot. Before releasing PowerShell v4 and DSC, Microsoft referred to DSC resources as providers. The term changed by the time it was released, but the docs haven’t been updated as of this writing.

41.6.1. Create the MOF Schema

You’ll need to start with a MOF schema, which defines the properties of the resource. When you use your resource in a configuration script, you assign values to these properties. In the examples in this chapter, for example, we had properties like:

WindowsFeature IIS

{

Ensure = "Present"

Name = "Web-Server"

}

Ensure and Name are both properties that are defined in the MOF schema. So your MOF might look something like this (pulled from the official documentation):

[ClassVersion("1.0.0"), FriendlyName("Website")]

class Demo_IISWebsite : OMI_BaseResource

{

[Key] string Name;

[write] string PhysicalPath;

[write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string

Ensure;

[write,ValueMap{"Started","Stopped"},Values{"Started", "Stopped"}] string

State;

[write,ValueMap{"http", "https"},Values{"http", "https"}] string

Protocol[];

[write] string BindingInfo[];

[write] string ApplicationPool;

[read] string ID;

};

Each line in that construct—let’s call it Schema.mof—is essentially a property, or a piece of information that your resource needs to operate. Name is a string, and it’s the key, meaning whatever values go into Name will uniquely identify something. For example, in our previous example,Web-Server was a value provided to a Name property, because it uniquely identified the role to install.

Everything else gets tagged as readable or writable, and you’ll typically see mostly writable properties. Those are ones that can accept values from your configuration scripts. Many will be simple strings; others, as in this example, are value maps. Notice this one:

[write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string

Ensure;

This value map offers two options, Present and Absent, which map to the internal values Present and Absent. In this case, the options and their underlying values are the same; sometimes it might map options like Started and Stopped to internal values like 0 and 1. It all depends how you’re using the data. In that example, if someone used your resource in a configuration and passed Stopped, your code would see 1.

The FriendlyName property at the top of the MOF defines the name that a configuration script would use to call on this resource. In our previous configuration script examples, you saw WindowsFeature, which is a friendly name defined by the Role resource. So, to use this “Website” resource in a configuration script, you might see:

Configuration My-Config {

Node WWW1 {

Website {

Name = 'www'

PhysicalPath = 'c:\inetpub\www3\'

Ensure = 'Present'

State = 'Started'

Protocol = 'http'

}

}

}

There’s the Website friendly name, and within its construct are the properties defined in the schema (well, most of them), along with values for each.

Note

Microsoft-authored resources use an Ensure property, which usually accepts the values Present and Absent, as a way of indicating whether the configuration should be installed. You don’t have to follow that pattern, but doing so will make your resources more consistent.

41.6.2. Create the script module

Your schema MOF defines your resource’s friendly name and the properties it accepts from a configuration script. You also need a script module, which takes those property values and implements the configuration. Resource script modules must implement three functions:

· Test-TargetResource—This function does the same thing as Get-TargetResource, but returns $True or $False, depending on whether the configuration item is configured as specified in the parameters.

· Set-TargetResource—This function does whatever is necessary to put the configuration item in whatever state is specified by the parameter values. For example, if a website doesn’t exist, it would be created. If it exists, it would be updated to reflect the passed-in configuration.

· Get-TargetResource—This function tests the current state of the configuration item. If the item exists, the returned object must have all the properties defined in the schema MOF, with values filled in for the existing configuration item. Most of these properties are empty if the configuration doesn’t exist.

Each of those functions must have a parameter set that matches the properties defined in your schema MOF—that is, if you were using the website example schema MOF, your three functions would each need a parameter set like this:

param

(

[ValidateSet("Present", "Absent")]

[string]$Ensure = "Present",

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$Name,

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$PhysicalPath,

[ValidateSet("Started", "Stopped")]

[string]$State = "Started",

[string]$ApplicationPool,

[string[]]$BindingInfo,

[string[]]$Protocol

)

This parameter set matches the properties in the schema MOF. When DSC runs the function, it’ll take the values assigned to the properties (in the configuration script) and match them up to these parameters. In this way, the functions get the data they need to implement the configuration.

Save the module file as ResourceName.psm1, replacing ResourceName with the name of your resource.

41.6.3. Create the module manifest

You use PowerShell’s New-ModuleManifest command to create a new manifest. Name it the same as the script module, only with a .psd1 filename extension—Website.psd1 to go with Website.psm1, for example. When creating the manifest:

· You can use the –RequiredModules parameter to specify the names of any PowerShell modules that must be loaded in order for your module to work properly.

· Use –NestedModules to specify the name of your script module—Website.psm1, for example.

· It’s okay for your module to include functions beyond the three required ones, and that’s a good way to modularize any functionality you need the module to perform. If you do this, use the –FunctionsToExport parameter to export Get-TargetResource, Set-TargetResource, and Test-TargetResource.

41.6.4. Putting it all together

Keep in mind that you’ll never run this module except to test it! These functions are all called by DSC. Here’s how it works: Suppose you create a resource using the schema MOF we outlined earlier. You complemented it with a script module and manifest, implementing the three required functions and the necessary parameter sets. Assume the MOF looked like listing 41.9.

Listing 41.9. Demo_IISWebsite.schema.mof

[ClassVersion("1.0.0"), FriendlyName("Website")]

class Demo_IISWebsite : OMI_BaseResource

{

[Key] string Name;

[write] string PhysicalPath;

[write,ValueMap{"Present", "Absent"},Values{"Present", "Absent"}] string

Ensure;

[write,ValueMap{"Started","Stopped"},Values{"Started", "Stopped"}] string

State;

[write,ValueMap{"http", "https"},Values{"http", "https"}] string

Protocol[];

[write] string BindingInfo[];

[write] string ApplicationPool;

[read] string ID;

};

Note

For consistency, we’re using the same example as in the documentation at http://technet.microsoft.com/en-us/library/dn249927.aspx. Notice that the schema MOF filename is in a particular format: resourcename.schema.mof. The class name in the MOF uses the same resource name.

You could then back that up with a script module, as shown in listing 41.10.

Listing 41.10. Demo_IISWebsite.psm1

function Get-TargetResource

{

param

(

[ValidateSet("Present", "Absent")]

[string]$Ensure = "Present",

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$Name,

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$PhysicalPath,

[ValidateSet("Started", "Stopped")]

[string]$State = "Started",

[string]$ApplicationPool,

[string[]]$BindingInfo,

[string[]]$Protocol

)

$getTargetResourceResult = $null;

<# YOUR CODE HERE – assume $Website is a valid object #>

$getTargetResourceResult = @{

Name = $Website.Name;

Ensure = $ensureResult;

PhysicalPath = $Website.physicalPath;

State = $Website.state;

ID = $Website.id;

ApplicationPool = $Website.applicationPool;

Protocol = $Website.bindings.Collection.protocol;

Binding = $Website.bindings.Collection.bindingInformation

}

$getTargetResourceResult;

}

function Set-TargetResource

{

[CmdletBinding(SupportsShouldProcess=$true)]

param

(

[ValidateSet("Present", "Absent")]

[string]$Ensure = "Present",

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$Name,

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$PhysicalPath,

[ValidateSet("Started", "Stopped")]

[string]$State = "Started",

[string]$ApplicationPool,

[string[]]$BindingInfo,

[string[]]$Protocol

)

<# Your code here #>

}

function Test-TargetResource

{

[CmdletBinding(SupportsShouldProcess=$true)]

param

(

[ValidateSet("Present", "Absent")]

[string]$Ensure = "Present",

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$Name,

[Parameter(Mandatory)]

[ValidateNotNullOrEmpty()]

[string]$PhysicalPath,

[ValidateSet("Started", "Stopped")]

[string]$State = "Started",

[string]$ApplicationPool,

[string[]]$BindingInfo,

[string[]]$Protocol

)

<# Your code here #>

}

Note

These three functions aren’t complete—remember, we’re just trying to show you the structure of a resource. In reality, Microsoft already gives you a resource that handles websites.

You could then create a manifest like the one shown in listing 41.11.

Listing 41.11. Demo_IISWebsite.psd1

@{

# Script module or binary module file associated with this manifest.

# RootModule = ''

# Version number of this module.

ModuleVersion = '1.0'

# ID used to uniquely identify this module

GUID = '6AB5ED33-E923-41d8-A3A4-5ADDA2B301DE'

# Author of this module

Author = 'Contoso'

# Company or vendor of this module

CompanyName = 'Contoso'

# Copyright statement for this module

Copyright = 'Contoso. All rights reserved.'

# Description of the functionality provided by this module

Description = 'This Module is used to support the creation and

configuration of IIS Websites through Get, Set and Test API

on the DSC managed nodes.'

# Minimum version of the Windows PowerShell engine required by this module

PowerShellVersion = '4.0'

# Minimum version of the common language runtime (CLR) required by this module

CLRVersion = '4.0'

# Modules that must be imported into the global environment prior to

# importing this module

RequiredModules = @("WebAdministration")

# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess

NestedModules = @("Demo_IISWebsite.psm1")

# Functions to export from this module

FunctionsToExport = @("Get-TargetResource", "Set-TargetResource",

"Test-TargetResource")

# Cmdlets to export from this module

#CmdletsToExport = '*'

# HelpInfo URI of this module

# HelpInfoURI = ''

With all of that in place, you could then write a configuration script that uses this resource, as shown in listing 41.12.

Listing 41.12. MyConfig.ps1

Configuration MyConfig {

Node WWW1 {

Website {

Name = 'Sample Website'

PhysicalPath = 'C:\Webfiles\Sample'

Ensure = 'Present'

State = 'Started'

Protocol = 'http'

ApplicationPool = 'MyAppPool'

}

}

}

Let’s say you then ran the configuration to create MyConfig\WWW1.MOF, and you pushed that MOF file to the computer WWW1. For the sake of argument, let’s also assume that you’d already deployed Demo_IISWebsite.psm1, Demo_IISWebsite.schema.mof, and Demo_IISWebsite.psd1 to the computer WWW1, putting them in the correct paths. That computer’s LCM would read the MOF, see that the Website resource was needed, and go find your Demo_IISWebsite.psm1 module.

The LCM would pull the values from the WWW1.MOF—the site name, physical path, state, protocol, and all that. It’d then run Test-TargetResource, passing along those values to the parameters of Test-TargetResource. If Test-TargetResource returned $True, the LCM would stop, because nothing else would need to be done. If it returned $False, the LCM would call Set-TargetResource, again passing in the MOF property values to the function’s parameters. Set-TargetResource would then need to do whatever was necessary to get things configured as specified.

41.7. DSC vs. Group Policy

We’re often asked about the relationship between DSC and Group Policy, and it’s an easy answer: there isn’t one.

Group Policy is still the winner for client configuration. Clients typically belong to a domain, and Group Policy depends on the domain to target computers and apply policy. Client computers move around a lot: they’re laptops, they get reassigned, their users relocate, and so on. Group Policy is built to understand that, and it has flexible options for applying policy to various computers based on detailed criteria. The PowerShell product team hasn’t—so far—spent much time making DSC better than Group Policy when it comes to client management. Group Policy is declarative, after all, so it already meets a similar need.

But servers are a little different. Servers are static—they don’t move around much, and we don’t reassign them much. Servers tend to live in one place, and we know about every single one of them. Servers are less likely to belong to a domain than a client machine, because there are numerous scenarios—think public web servers—where belonging to a domain is impractical. DSC has no dependency on a domain: as long as you can communicate between computers, it’ll work, and those communications can be configured to work across domain and workgroup boundaries.

This is why, at present, most of the focus on DSC is for servers, especially servers in mass-scale environments like cloud hosting providers. That isn’t to say DSC can’t replace Group Policy, but right now Group Policy definitely has some capabilities that are difficult to re-create in DSC. Eventually, we can foresee DSC picking up some of Group Policy’s richer targeting and application capabilities and eventually supplanting Group Policy, but that might be a little ways off. DSC does have a few things going for it that Group Policy doesn’t: First, DSC resources are easy to create and deploy, whereas extending Group Policy is extremely complex. Second, DSC’s ability to operate without domain dependencies can be beneficial. Third, a DSC script is more human-readable than a raw Group Policy file, making it easy to version-control configurations, test them offline, and so on.

41.8. Summary

This chapter was written for the first release of DSC, which was in the Windows Management Framework 4. We already know that the PowerShell product team is actively working on the next version of DSC, so stay tuned for more. There’s also an active community building around DSC—you’ll find some at PowerShell.org, and there’s a DSC-related coding project happening on PowerShell.org’s GitHub repository at https://github.com/PowerShellOrg/DSC. DSC is bound to be a big part of your future, so make sure you’re connecting and keeping up!