Writing Configuration Scripts
A configuration script consists of three main items:
- The main configuration container, which contains everything else
- One or more node sections that specify specific target computers and contain the configuration items
- One or more configuration items per node section, which specify whatever configuration you want to be in effect on those nodes
The PowerShell ISE understands configuration syntax, and is a good editor. You do need to be careful with it: when you run a configuration, it's going to need to create a folder and one or more MOF files. It'll need to do that in whatever folder the ISE is currently pointed at. So before you begin writing, you may want to switch to the ISE's console pane and change directories. Otherwise, you may end up writing files to \Windows\System32, which might not be desirable.
Here's a basic configuration script, which you might save as MyConfig.ps1:
Configuration MonitoringSoftware
{
param(
[string[]]$ComputerName="localhost"
)
Node $ComputerName
{
File MonitoringInstallationFiles
{
Ensure = "Present"
SourcePath = "\\dc01\Software\Monitoring"
DestinationPath = "C:\Temp\Monitoring"
Type = "Directory"
Recurse = $true
}
}
}
MonitoringSoftware
There are several important things happening here:
The top-level Configuration construct acts a lot like a PowerShell function. It contains the entire configuration, and it's actually considered a kind of PowerShell command. The name of the configuration, MonitoringSoftware, is arbitrary - you can name it whatever you like.
The configuration accepts a -ComputerName parameter, which defaults to localhost. So, when you run the configuration, you specify the computer name that you want it to apply to. This is one alternative to hardcoding a list of computer names into the configuration. Note that, like parameters in functions, configuration parameters can have a [Parameter()] attribute and various validation attributes. These can be used to validate parameter input, make parameters mandatory, and so on. Read the about_functions_advanced_parameters help file in PowerShell for more information.
The Node element specifies the computer(s) that this configuration will apply to. Here, we're inserting the value of the -ComputerName parameter.
The File keyword refers to a specific DSC resource - the File resource that ships with WMF 4. This configuration item has a name, MonitoringInstallationFiles, that you can make up. We could have called it Fred, for example, and it would still work.
The configuration item includes 5 settings: Ensure, SourcePath, DestinationPath, Type, and Recurse. These are specific to the File DSC resource; other resources would have different settings. In this case, we're asking DSC to ensure that a specific folder hierarchy is present on the target node. If it isn't, we've told DSC where to find it, so that it can copy it over. You could also copy individual files, if you needed to.
A configuration is a command, so the last line of the script actually runs the command. We don't provide a -ComputerName parameter, so it'll default to creating a MOF file for localhost. We'll actually get a folder named MonitoringSoftware (the name of the configuration), and it'll contain Localhost.mof.
Notice that the -ComputerName parameter is declared as [string[]] , meaning it accepts multiple values. We could provide multiple values, and generate a MOF for each. For example:
MonitoringSoftware -ComputerName (
Get-ADComputer -Filter \* -SearchBase "ou=clients,dc=domain,dc=pri" |
Select-Object -Expand Name
)
That would produce a MOF file for each computer in the Clients OU of the Domain.pri domain.
Multiple Nodes in a Configuration
Most online examples you see will only include a single Node section inside the Configuration section. But there's a valid scenario to include multiple nodes in a configuration. For example, suppose you're setting up a lab environment that includes several servers, a few clients, and so on. You could include every computer in the lab inside one configuration script, giving you a single source for managing the entire configuration. When you run the configuration, PowerShell will produce a MOF for each node that's mentioned inside. We've included an example of a multi-node configuration later in this guide.
Discovering Resources
The above example should bring up one, or maybe two questions:
How do I know what resources I have installed?
How do I know what settings a DSC resource requires?
Both are excellent questions, and easily answered. Start by running Get-DSCResource (if the command doesn't work, you're probably not running PowerShell v4). That will list all resources that are loaded into memory, which will include any that were shipped with Windows. The output of the command includes the Name that you use to refer to the resource in a configuration. Once you've narrowed down a resource you want - let's say, "Package," - you can get more details about it. Run Get-DSCResource -Name Package | Select -Expand Properties (swapping out "Package" for the name of the resource you're interested in). You'll get a complete list of properties, and the type of data each one expects. In some cases, you'll see allowable values. For example, the "Ensure" property usually accepts "Absent" and "Present," depending on whether you want the item there, or want it to not be there.
Another option, using the File module as an example, would be to run Get-DSCResource -Name File -Syntax. This produces similar output, showing you all of the resource's settings, the kind of value they expect, and also including accepted values for some settings.
Beyond that, PowerShell doesn't necessarily offer a ton of documentation on every resource, so you may end up doing some Googling, but at least Get-DSCResource provides you with a good starting point.
Things get tricky with resources that you've added yourself. Although they're technically packaged as PowerShell script modules, they don't behave quite like modules, so you have to take a few extra steps. First of all, they should go into \Program Files\WindowsPowerShell\Modules. Here's an example folder hierarchy for a single DSC resource:
Here's what you're looking at:
The module name is xPSDesiredStateConfiguration. That folder contains a script module and a module manifest - although not every resource you look at will have all of that. There's also a DSCResources subfolder.
The DSCResources subfolder doesn't contain any files. It contain a subfolder for each resource that the module provides.
The MSFT_xDSCWebService subfolder represents a single DSC resource. It contains, at a minimum, a .psm1 script module file and a .schema.mof file. Both are required to use the resource.
Things get a little tricky because you don't really import this into the shell as a normal module. There's an Import-DSCResource keyword, but it only works inside Configuration scripts - you can't use it right at the shell prompt. So part of your "finding what resources I have" process is going to necessarily involve browsing the file system a bit to see what's installed.
Sequencing Configuration Items
It's possible that configuration items might need to happen in a particular order. For example, suppose you're installing an application on a computer. You might need to first copy the install source to the computer, then run the installer. That's easy to do, as demonstrated in this configuration item:
PackageMonitoringSoftware
{
Ensure ="Present"
Path ="$Env:SystemDrive\Temp\Monitoring\7z920-x64.msi"
Name ="7-Zip"
ProductId ="23170F69-40C1-2702-0920-000001000000"
DependsOn ="[File]MonitoringInstallationFiles"?
}
Notice the DependsOn setting? That setting is common to most resources, and in this case it's saying, "hey, go to the File -type setting named MonitoringInstallationFiles before you do me." In this case, that makes sure the application's installer has been copied over before the Package resource is called upon to actually run it.
By the way, the ProductID setting isn't something to worry about for the purposes of understanding DSC. That's a setting that's unique to the Package resource. It contains the unique identifier for the particular piece of software (7-Zip, in this example) we're asking DSC to install. The Package resource probably uses the product ID to check and see if the thing is already installed or not. The point is that it's necessary for the Package resource to work, but it doesn't have anything to do with DSC per se.
Similarly, Path and Name are other settings required by the Package resource. The Path setting lets you specify the path to the installer, so that DSC knows where to find it if the software isn't already installed on the target node. In this case, we're pointing to a local file path - so we need to make darn well sure that the MSI file exists in that location. That's why this configuration items DependsOn our earlier File configuration item. The File item copies the MSI file over, so that the Package item can run the MSI if needed.
Forcing a Configuration Evaluation
There's a quick trick to force a computer to re-evaluate its configuration. If configured to pull its config, this will also force the pull to happen. Just run this on the computer:
$params = @{
Namespace = 'root/Microsoft/Windows/DesiredStateConfiguration'
ClassName = 'MSFT_DSCLocalConfigurationManager'
MethodName = 'PerformRequiredConfigurationChecks'
Arguments = @{
Flags = [uint32] 1
}
}
Invoke-CimMethod @params