puppetlabs-dsc

dsc

Table of Contents

  1. Description - What is the dsc module and what does it do
  2. Prerequisites
  3. Setup
  4. Usage
  5. Reference
  6. Limitations
  7. Development - Guide for contributing to the module
  8. Places to Learn More About DSC
  9. License

Description

The Puppet dsc module manages Windows PowerShell DSC (Desired State Configuration) resources.

This module generates Puppet types based on DSC Resources MOF (Managed Object Format) schema files.

In this version, the following DSC Resources are already built and ready for use:

Windows System Prerequisites

Setup

puppet module install puppetlabs-dsc

See known issues for troubleshooting setup.

Usage

Using DSC Resources with Puppet

You can use a DSC Resource by prefixing each DSC Resource name and parameter with ‘dsc_’ and lowercasing the values.

So a DSC resource specified in PowerShell…

WindowsFeature IIS {
  Ensure = 'present'
  Name   = 'Web-Server'
}

…would look like this in Puppet:

dsc_windowsfeature {'IIS':
  dsc_ensure => 'present',
  dsc_name   => 'Web-Server',
}

All DSC Resource names and parameters have to be in lowercase, for example: dsc_windowsfeature or dsc_name.

You can use either ensure => (Puppet’s ensure) or dsc_ensure => (DSC’s Ensure) in your manifests for Puppet DSC resource types. If you use both in a Puppet DSC resource, dsc_ensure overrides the value in ensure, so the value for ensure is essentially ignored.

We recommend that you use dsc_ensure instead of ensure, as it is a closer match for converting the DSC properties to Puppet DSC resources. It also overrides ensure, so there is less confusion if both are accidentally included.

Note: While you can use either ensure => (Puppet’s ensure) or dsc_ensure => (DSC’s Ensure) in your manifests, there is currently a known issue where ensure => absent reports success but does nothing. See MODULES-2966 for details. Until this issue is resolved, we recommend using dsc_ensure exclusively.

Handling Reboots with DSC

Add the following reboot resource to your manifest. It must have the name dsc_reboot for the dsc module to find and use it.

reboot { 'dsc_reboot' :
  message => 'DSC has requested a reboot',
  when => 'pending'
}

Installing Packages with DSC

Install MSIs or EXEs with DSC using the Puppet type dsc_package, which maps to the Package DSC Resource.

dsc_package{'installpython'
  dsc_ensure    => 'Present',
  dsc_name      => 'Python 2.7.10',
  dsc_productid => 'E2B51919-207A-43EB-AE78-733F9C6797C2'
  dsc_path      => 'C:\\python.msi',
}

The Package DSC Resource requires the following information to install an MSI:

You can obtain this information in a variety of ways.

function Get-MsiDatabaseInfo{
  param ([IO.FileInfo]$FilePath)

  $productName = Invoke-MSIQuery -FilePath $filePath.FullName -Query "SELECT Value FROM Property WHERE Property = 'ProductName'"
  $productCode = Invoke-MSIQuery -FilePath $filePath.FullName -Query "SELECT Value FROM Property WHERE Property = 'ProductCode'"

  return [PSCustomObject]@{
    FullName    = $FilePath.FullName
    ProductName = ([string]$productName).TrimStart()
    ProductCode = ([string]$productCode).Replace("{","").Replace("}","").TrimStart()
  }
}

function Invoke-MSIQuery{
  param($FilePath, $Query)
  try{
    $windowsInstaller = New-Object -com WindowsInstaller.Installer
    $database = $windowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $Null, $windowsInstaller, @($FilePath, 0))
  }catch{
    throw "Failed to open MSI file. The error was: {0}." -f $_
  }

  try{
    $View = $database.GetType().InvokeMember("OpenView", "InvokeMethod", $Null, $database, ($query))
    $View.GetType().InvokeMember("Execute", "InvokeMethod", $Null, $View, $Null)

    $record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $Null, $View, $Null)
    $property = $record.GetType().InvokeMember("StringData", "GetProperty", $Null, $record, 1)

    $View.GetType().InvokeMember("Close", "InvokeMethod", $Null, $View, $Null)

    return $property
  }catch{
    throw "Failed to read MSI file. The error was: {0}." -f $_
  }
}

Using Hashes

Supply a hash to any parameter that accepts PowerShell hashes, and Puppet handles creating the appropriate values for you.

dsc_example_resource { 'examplefoo':
  dsc_ensure         => present,
  dsc_hash_parameter => {
    'key1' => 'value1',
    'key2' => 'value2'
  },
}

Using Credentials

DSC uses MSFT_Credential objects to pass credentials to DSC Resources. Supply a hash to any credential parameter, and Puppet handles creating the credential object for you.

Optionally use the Puppet Sensitive type to ensure logs and reports redact the password.

dsc_user { 'jane-doe':
  dsc_username             => 'jane-doe',
  dsc_description          => 'Jane Doe user',
  dsc_ensure               => present,
  dsc_password             => {
    'user' => 'jane-doe',
    'password' => Sensitive('jane-password')
  },
  dsc_passwordneverexpires => false,
  dsc_disabled             => true,
}

Setting Registry Values

Creating and modifying Registry keys and values is done with the dsc_registry Puppet type which maps to the Registry DSC Resource.

Registry Example: Simple

Set simple values by specifying key-value pairs.

dsc_registry {'registry_test':
  dsc_ensure    => 'Present'
  dsc_key       => 'HKEY_LOCAL_MACHINE\SOFTWARE\ExampleKey'
  dsc_valuename => 'TestValue'
  dsc_valuedata => 'TestData'
}

Registry Example: Binary

The ‘Binary’ data type expects hexadecimal in a single string.

dsc_registry {'registry_test':
  dsc_ensure => 'Present',
  dsc_key => 'HKEY_LOCAL_MACHINE\SOFTWARE\TestKey',
  dsc_valuename => 'TestBinaryValue',
  dsc_valuedata => 'BEEF',
  dsc_valuetype => 'Binary',
}

Registry Example: Dword and Qword

The ‘Dword’ and ‘Qword’ data types expect signed integer values, as opposed to hexadecimal or unsigned.

dsc_registry {'registry_test':
  dsc_ensure => 'Present',
  dsc_key => 'HKEY_LOCAL_MACHINE\SOFTWARE\TestKey',
  dsc_valuename => 'TestDwordValue',
  dsc_valuedata => '-2147483648',
  dsc_valuetype => 'Dword',
}

Note: DSC Resources are executed under the SYSTEM context by default, which means you are unable to access any user level Registry key without providing alternate credentials.

Adding or Removing Windows Features

You can add or remove Windows Features using Puppet type dsc_windowsfeature which maps to the WindowsFeature DSC Resource.

Add a Windows Feature

dsc_windowsfeature {'featureexample':
  dsc_ensure = 'present'
  dsc_name = 'Web-Server'
}

Remove a Windows Feature

dsc_windowsfeature {'featureexample':
  dsc_ensure = 'absent'
  dsc_name = 'Web-Server'
}

Finding Windows Feature Names

You can find the name to use when adding or removing Windows Features by executing the Get-WindowsFeature cmdlet and using the Name property.

[PS]> Get-WindowsFeature

Website Installation Example

An end-to-end example installation of a test website.

class fourthcoffee(
  $websitename        = 'FourthCoffee',
  $zipname            = 'FourthCoffeeWebSiteContent.zip',
  $sourcerepo         = 'https://github.com/msutter/fourthcoffee/raw/master',
  $destinationpath    = 'C:\inetpub\FourthCoffee',
  $defaultwebsitepath = 'C:\inetpub\wwwroot',
  $zippath            = 'C:\tmp'
){

  $zipuri  = "${sourcerepo}/${zipname}"
  $zipfile = "${zippath}\\${zipname}"

  # Install the IIS role
  dsc_windowsfeature {'IIS':
    dsc_ensure => 'present',
    dsc_name   => 'Web-Server',
  } ->

  # Install the ASP .NET 4.5 role
  dsc_windowsfeature {'AspNet45':
    dsc_ensure => 'present',
    dsc_name   => 'Web-Asp-Net45',
  } ->

  # Stop an existing website (set up in Sample_xWebsite_Default)
  dsc_xwebsite {'Stop DefaultSite':
    dsc_ensure       => 'present',
    dsc_name         => 'Default Web Site',
    dsc_state        => 'Stopped',
    dsc_physicalpath => $defaultwebsitepath,
  } ->

  # Create tmp folder
  dsc_file {'tmp folder':
    dsc_ensure          => 'present',
    dsc_type            => 'Directory',
    dsc_destinationpath => $zippath,
  } ->

  # Download the site content
  dsc_xremotefile {'Download WebContent Zip':
    dsc_destinationpath => $zipfile,
    dsc_uri             => $zipuri,
  } ->

  # Extract the website content 
  dsc_archive {'Unzip and Copy the WebContent':
    dsc_ensure      => 'present',
    dsc_path        => $zipfile,
    dsc_destination => $destinationpath,
  } ->

  # Create a new website
  dsc_xwebsite {'BackeryWebSite':
    dsc_ensure       => 'present',
    dsc_name         => $websitename,
    dsc_state        => 'Started',
    dsc_physicalpath => $destinationpath,
  }
}

As you can see, you can mix and match DSC resources with common Puppet resources. All Puppet metaparameters are also supported.

Reference

Types

A comprehensive list of all types included in the dsc module is available in the types document. This list maps each Puppet resource (for example, dsc_xcertreq) to the corresponding DSC resource.

Because types are built from the source code of each DSC Resources MOF schema files, the name of the DSC resource in the types document links to a local copy of that resource code (in this case, xCertReq), so that you can see how the code is applied to your system.

Where available, a link to the external GitHub repo of each resource is also included. The DSC resources are third-party resources that may or may not be documented in their repositories. Available DSC resources and parameters are subject to change.

Limitations

Known Issues

Running Puppet and DSC without Administrative Privileges

While there are avenues for using Puppet with a non-administrative account, DSC is limited to only accounts with administrative privileges. The underlying CIM implementation DSC uses for DSC Resource invocation requires administrative credentials to function.

The Puppet agent on a Windows node can run DSC with a normal default install. If the Puppet agent was configured to use an alternate user account, that account must have administrative privileges on the system in order to run DSC.

Troubleshooting

When Puppet runs, the dsc module takes the code supplied in your puppet manifest and converts that into PowerShell code that is sent to the DSC engine directly using Invoke-DscResource. You can see both the commands sent and the result of this by running puppet interactively, e.g. puppet apply --debug. It will output the PowerShell code that is sent to DSC to execute and the return data from DSC. For example:

Notice: Compiled catalog for win2012r2 in environment production in 0.82 seconds
Debug: Creating default schedules
Debug: Loaded state in 0.03 seconds
Debug: Loaded state in 0.05 seconds
Info: Applying configuration version '1475264065'
Debug: Reloading posix reboot provider
Debug: Facter: value for uses_win32console is still nil
Debug: PowerShell Version: 5.0.10586.117
$invokeParams = @{
  Name          = 'ExampleDSCResource'
  Method        = 'test'
  Property      = @{
    property1 = 'value1'
    property2 = 'value2'
  }
  ModuleName = @{
    ModuleName      = "C:/puppetlabs/modules/dsc/lib/puppet_x/dsc_resources/ExampleDSCResource/ExampleDSCResource.psd1"
    RequiredVersion = "1.0"
  }
}
############### SNIP ################
Debug: Waited 50 milliseconds...
############### SNIP ################
Debug: Waited 500 total milliseconds.
Debug: Dsc Resource returned: {"rebootrequired":false,"indesiredstate":false,"errormessage":""}
Debug: Dsc Resource Exists?: false
Debug: ensure: present
############### SNIP ################
$invokeParams = @{
  Name          = 'ExampleDSCResource'
  Method        = 'set'
  Property      = @{
    property1 = 'value1'
    property2 = 'value2'
  }
  ModuleName = @{
    ModuleName      = "C:/puppetlabs/modules/dsc/lib/puppet_x/dsc_resources/ExampleDSCResource/ExampleDSCResource.psd1"
    RequiredVersion = "1.0"
  }
}
############### SNIP ################\
Debug: Waited 100 total milliseconds.
Debug: Create Dsc Resource returned: {"rebootrequired":false,"indesiredstate":true,"errormessage":""}
Notice: /Stage[main]/Main/Dsc_exampledscresource[foober]/ensure: created
Debug: /Stage[main]/Main/Dsc_exampledscresource[foober]: The container Class[Main] will propagate my refresh event
Debug: Class[Main]: The container Stage[main] will propagate my refresh event
Debug: Finishing transaction 56434520
Debug: Storing state
Debug: Stored state in 0.10 seconds
############### SNIP ################

This shows us that there wasn’t any problem parsing your manifest and turning it into a command to send to DSC. It also shows that there are two commands/operations for every DSC Resource executed, a SET and a test. DSC operates in two stages, it first tests if a system is in the desired state, then it sets the state of the system to the desired state. You can see the result of each operation in the debug log.

By using the debug logging of a puppet run, you can troubleshoot the application of DSC Resources during the development of your puppet manifests.

Development

Puppet Inc modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve.

We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things.

For more information, see our module contribution guide.

Version Strategy

This module generally follows Semantic Versioning for choosing an appropriate release version number with the following exception:

A minor change may also include rebuilding the DSC resource types. Puppet wants to keep pace with the released DSC Resources from the PowerShell team repository, but this engenders risk as Puppet adopts third party code. Normally this would mean making major version bumps, but since this is anticipated to be frequent that would be too much churn.

Contributors

To see who’s already involved, see the list of contributors.

Learn More About DSC

You can learn more about PowerShell DSC from the following online resources:

There are several books available as well. Here are some selected books for reference:

License