Working with parameters in Microsoft Desired State Configuration (DSC)

Learn how to leverage parameters in Microsoft DSC configuration documents

Working with parameters in Microsoft Desired State Configuration (DSC)

If you worked with PowerShell Desired State Configuration (PSDSC), you know the pain.

Managing parameters was a nightmare. You wrote a configuration. You compiled it into an MOF file. You deployed that MOF. Then you needed different parameters for staging. So you compiled again. Then production needed different values. You compiled a third time.

Every parameter change meant recompiling. Every environment needed its own MOF file. Every deployment required managing and distributing these files across your infrastructure.

It was exhausting.

Microsoft Desired State Configuration (DSC) changes everything.

Parameters are processed at runtime. One configuration file. Multiple environments. No compilation. No MOF files. No distribution headache.

You define your configuration once. You run it with different parameters. That's it.

This article shows you how, starting with a comparison of PSDSC versus Microsoft DSC.

Comparing parameters

Before you look at how you can work with parameters in Microsoft DSC, let's see both scenarios in both systems using a real-world example: deploying a web server configuration to production with environment-specific parameters.

In a compilation workflow using PSDSC, the following configuration document illustrates what happens:

# Step 1: Define the configuration script
Configuration WebServerConfig {
    param(
        [string]$Environment = 'Development',
        [int]$Port = 8080
    )

    Import-DscResource -ModuleName PSDesiredStateConfiguration
    
    Node $ServerName {
        WindowsFeature IIS {
            Name = 'Web-Server'
            Ensure = 'Present'
        }
        
        File ConfigFile {
            DestinationPath = "C:\Config\app.config"
            Contents = "Environment: $Environment`nPort: $Port"
        }
    }
}

# Step 2: COMPILE to MOF file (parameters become hardcoded)
WebServerConfig -Environment 'Production' -Port 443 -ServerName 'WebServer01' -OutputPath 'C:\MOFs'
# Creates: C:\MOFs\WebServer01.mof with hardcoded values

# Step 3: APPLY the MOF file (separate operation)
Start-DscConfiguration -Path 'C:\MOFs' -Wait

# Problem: To use different parameters, you must:
# 1. Recompile the configuration script
# 2. Generate a new MOF file
# 3. Distribute the new MOF to target systems

As you can see, the challenge is clear: parameters are "baked into" the MOF file at compile time. Each environment needs its own MOF file. Changing values requires recompiling and redistributing.

Now let's look at how Microsoft DSC does it:

# webserver-config.dsc.yaml - One configuration for all environments
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
parameters:
  environment:
    type: string
  port:
    type: int
  configPath:
    type: string
resources:
  - name: IIS Web Server
    type: Microsoft.Windows/WindowsPowerShell
    properties:
      resources:
        - name: Web-Server Feature
          type: PsDesiredStateConfiguration/WindowsFeature
          properties:
            Name: Web-Server
            Ensure: Present
        - name: Application Config File
          type: PsDesiredStateConfiguration/File
          properties:
            DestinationPath: "[parameters('configPath')]"
            Contents: "[concat('Environment: ', parameters('environment'), '\nPort: ', string(parameters('port')))]"
            Ensure: Present

To run it for different environments, you can simply add the --parameters option when calling dsc at runtime:

# Development
dsc config get --file webserver-config.dsc.yaml --parameters '{"parameters":{"environment":"development","port":8080,"configPath":"C:\\Config\\dev-app.config"}}'

# Production
dsc config get --file webserver-config.dsc.yaml --parameters '{"parameters":{"environment":"production","port":443,"configPath":"C:\\Config\\prod-app.config"}}'

# Change values instantly - just provide different parameters
dsc config get --file webserver-config.dsc.yaml --parameters '{"parameters":{"environment":"staging","port":9000,"configPath":"C:\\Config\\staging-app.config"}}'

The improvement is clear. Parameters are evaluated at runtime. One configuration file works everywhere, and the values can change instantly without any recompilation.

Here's a quick table of the key differences between PSDSC and Microsoft DSC:

Aspect PowerShell DSC Microsoft DSC
Parameter processing Compile-time (static) Runtime (dynamic)
Workflow Write script → Compile to MOF → Apply MOF Write config → Execute with parameters
Intermediate files MOF files required No MOF files
Change parameters Recompile + redistribute Provide different values
Multi-environment One MOF per environment One config + parameter files
Parameter format PowerShell function parameters JSON/YAML files or inline

Understanding parameter definitions

Now that you see how Microsoft DSC differs from PSDSC, let’s look at the ways to pass parameters to your configurations, beginning with parameter definitions.

As you've seen in the above code snippet, parameters are defined with the parameters keyword. Take a look at the following configuration document:

# example-config.dsc.yaml
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
parameters:
  environment:
    type: string
    defaultValue: development
    allowedValues:
      - development
      - staging
      - production
  port:
    type: int
    defaultValue: 8080
    minValue: 1
    maxValue: 65535
  serverName:
    type: string
    defaultValue: myserver
    minLength: 3
    maxLength: 15
  enableSSL:
    type: bool
    defaultValue: false
  tags:
    type: array
    defaultValue:
      - web
      - production
    minLength: 1
    maxLength: 5
resources:
  - name: Echo Configuration
    type: Microsoft.DSC.Debug/Echo
    properties:
      output: "[concat('Env: ', parameters('environment'), ', Server: ', parameters('serverName'), ', Port: ', string(parameters('port')), ', SSL: ', string(parameters('enableSSL')))]"

Each parameter can have two properties:

  • type: The data type (string, int, bool, array, object, secureString, secureObject).
  • defaultValue: Used if no value is provided.

It's also possible to add optional constraints:

  • For string types: minLength, maxLength, allowedValues.
  • For int types: minValue, maxValue, allowedValues.
  • For array types: minLength, maxLength, allowedValues.
  • For all types: allowedValues (array of permitted values).

Now that you know about the parameter definitions, let's look at how you can provide parameters to the engine.

Method 1: Using parameter files

Parameter files provide a structured way to define parameter values separately from your configuration. This is the most common and recommended approach for managing parameters.

For illustration purposes, you can create it in multiple ways. Choose the one that appeals to you.

Use JSON parameter file

Create the following JSON file containing your parameters:

// parameters.json
{
  "parameters": {
    "environment": "production",
    "port": 443,
    "serverName": "web-prod-01"
  }
}

To use the parameter file, use it with your configuration:

dsc config --parameters-file parameters.json get --file example-config.dsc.yaml

Use PowerShell to create a parameters file

You can create the parameters file using PowerShell and save it as JSON:

# Create parameters as a hashtable and convert to JSON
$params = @{
    parameters = @{
        environment = 'production'
        port = 443
        serverName = 'web-prod-01'
    }
} | ConvertTo-Json

# Save to file
$params | Set-Content -Path 'parameters.json'

# Use with DSC
dsc config --parameters-file parameters.json get --file example-config.dsc.yaml

Use YAML parameter file

DSC also supports YAML parameter files, which can be more readable for complex structures:

# parameters.yaml
parameters:
  environment: production
  port: 443
  serverName: web-prod-01
  tags:
    - web
    - critical
  metadata:
    owner: platform-team
    costCenter: engineering

Then, to apply the parameter file, you'll run:

dsc config --parameters-file parameters.yaml get --file example-config.dsc.yaml

Bonus: Using YaYaml in PowerShell

If you prefer to stick with PowerShell and convert parameter files from a hashtable, use the YaYaml PowerShell module:

# Install YaYaml module if not already installed
Install-PSResource -Name YaYaml -Scope CurrentUser

# Create parameters as a hashtable
$params = @{
    parameters = @{
        environment = 'production'
        port = 443
        serverName = 'web-prod-01'
        tags = @('web', 'critical')
        metadata = @{
            owner = 'platform-team'
            costCenter = 'engineering'
        }
    }
}

# Convert to YAML and save
$params | ConvertTo-Yaml -Depth 10 | Set-Content -Path 'parameters.yaml'

# Use with DSC
dsc config --parameters-file parameters.yaml get --file example-config.dsc.yaml

Method 2: Using inline parameters

This example was showcased in the introduction. Inline parameters allow you to pass parameters directly on the command line without creating a separate file. This is useful for quick testing.

To pass parameters as a JSON string, you can use the --parameters or -p flag:

dsc config --parameters '{"parameters":{"environment":"staging"}}' get --file example-config.dsc.yaml

If you want to fallback on PowerShell to construct the proper JSON, use:

# Create parameters as hashtable and convert to JSON
$inlineParams = @{
    parameters = @{
        environment = 'staging'
        port = 9000
    }
} | ConvertTo-Json -Compress

# Pass to DSC
dsc config --parameters $inlineParams get --file example-config.dsc.yaml

As a bonus, you can also pass in YAML with the YaYaml PowerShell module:

# Convert parameters to YAML and pass inline
$inlineParams = @{
    parameters = @{
        environment = 'staging'
        port = 9000
        tags = @('web', 'staging')
    }
} | ConvertTo-Yaml

# Pass YAML string to DSC
dsc config --parameters $inlineParams get --file example-config.dsc.yaml

Method 3: Using STDIN with pipeline

DSC allows you to read parameter files from standard input (STDIN). This provides for pipeline scenarios and integration with other tools.

To read from STDIN, you can use the - as the parameter file path:

cat parameters.json | dsc config --parameters-file - get --file example-config.dsc.yaml

If you want to construct it using PowerShell, use:

$params = @{
    parameters = @{
        environment = 'production'
        port = 443
    }
} | ConvertTo-Json

$params | Set-Content -Path parameters.json

Get-Content 'parameters.json' | dsc config --parameters-file - get --file example-config.dsc.yaml

The same would imply for YAML:

# Convert parameters to YAML and pipe to DSC
$params = @{
    parameters = @{
        environment = 'production'
        port = 443
        tags = @('web', 'production')
    }
} | ConvertTo-Yaml

$params | Set-Content -Path 'parameters.yaml'

# Pipe YAML file to DSC
Get-Content 'parameters.yaml' | dsc config --parameters-file - get --file example-config.dsc.yaml

Method 4: Merging multiple parameter sources

DSC allows you to merge parameters from multiple sources, with inline parameters taking precedence over parameter files. This enables you to dynamically pass in parameters for complex deployments.

ℹ️
This feature is currently available as an unofficial release and may change before official release.

To combine a parameter file with inline overrides, use PowerShell:

# Base parameters in file
$baseParams = @{
    parameters = @{
        environment = 'production'
        port = 443
        enableSSL = $true
        logLevel = 'info'
    }
} | ConvertTo-Json

$baseParams | Set-Content -Path 'base-parameters.json'

# Override specific parameters inline
$overrideParams = @{
    parameters = @{
        port = 8443
        logLevel = 'debug'
    }
} | ConvertTo-Json -Compress

# Merged result: environment=production, port=8443, enableSSL=true, logLevel=debug
dsc config --parameters-file base-parameters.json --parameters $overrideParams get --file example-config.dsc.yaml

When merging parameters from multiple sources, DSC will apply the following precedence:

  1. Inline parameters
  2. Parameter file
  3. Default values

Best practices

You've learned the mechanics of passing parameters to DSC configurations. Now let's talk about doing it right.

These aren't theoretical guidelines. They're lessons learned from the field and real deployments. Follow them, and you'll avoid the common pitfalls that make parameter management painful.

Use parameter files for environment configuration

Store environment-specific configurations in a separate folder in version control:

parameters/
├── development.json
├── staging.json
└── production.json

Or:
parameters/
├── development.yaml
├── staging.yaml
└── production.yaml

Leverage default values

Provide sensible defaults for parameters that rarely change:

parameters:
  logLevel:
    type: string
    defaultValue: info  # Reasonable default
  port:
    type: int
    defaultValue: 8080  # Common default
  environment:
    type: string
    # No default - must be explicitly provided

Validate with constraints

Use constraints to prevent invalid configurations:

parameters:
  environment:
    type: string
    allowedValues:
      - development
      - staging
      - production
  
  port:
    type: int
    minValue: 1
    maxValue: 65535
  
  serverName:
    type: string
    minLength: 3
    maxLength: 15

Document your parameters

Each parameter declared in your configuration document can be documented using the metadata keyword:

$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
parameters:
  environment:
    type: string
    defaultValue: development
    allowedValues:
      - development
      - staging
      - production
    metadata: 
      description: Deployment environment
  port:
    type: int
    defaultValue: 8080
    minValue: 1
    maxValue: 65535
    metadata:
      description: TCP port number for the application
  serverName:
    type: string
    defaultValue: myserver
    minLength: 3
    maxLength: 15
    metadata:
      description: Server hostname or identifier
  enableSSL:
    type: bool
    defaultValue: false
    metadata:
      description: Enable SSL/TLS encryption
  tags:
    type: array
    defaultValue:
      - web
      - production
    minLength: 1
    maxLength: 5
    metadata:
      description: Resource tags for categorization
# truncated

Summary

Microsoft DSC provides flexible methods for passing parameters to configurations. You've learned four methods:

  1. Parameter files: Best for environment configurations.
  2. Inline parameters: Ideal for quick tests.
  3. STDIN: Enables pipeline input and integration scenarios.
  4. Parameter merging: Combines multiple sources with clear precedence (unofficial).

Choose the method that best fits your use case, or combine methods for flexibility. The declarative nature of Microsoft DSC configurations, combined with its parameter system, enables reusable configuration management across multiple platforms.