Feature Flags
Boolean parameters can act as feature flags in a DSC configuration, letting you author opt-in capabilities once and activate them progressively through the scope system. This document explains the pattern and walks through two common canary strategies.
Note
For a direct comparison with prerelease rollout, see Prerelease vs Feature Flags.
Overview
The idea is straightforward:
- Author the feature in the configuration - wrap the resource block in a conditional controlled by a boolean parameter.
- Default the flag to
false- the feature is off everywhere unless explicitly enabled. - Enable the flag at a specific scope - use the scope hierarchy to turn the feature on for a canary ring (an environment, a region, a dedicated canary scope value, or individual nodes) before promoting it globally.
This decouples configuration authoring from feature rollout. A new capability can ship in the configuration at any time, remain dormant in production, and be evaluated against a subset of nodes without requiring a separate configuration version.
Declaring Feature Flag Parameters
Define every feature flag in the configuration's parameters block with
type: boolean and defaultValue: false.
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
parameters:
# --- Feature flags (opt-in, off by default) ---
enableHttps:
type: boolean
defaultValue: false
description: Enable HTTPS binding and obtain a TLS certificate.
enableWaf:
type: boolean
defaultValue: false
description: Install and configure the Web Application Firewall module.
enableDiagnosticsExtension:
type: boolean
defaultValue: false
description: Install the diagnostics agent for extended telemetry.
# --- Stable settings always applied ---
siteName:
type: string
defaultValue: DefaultSite
appPoolName:
type: string
defaultValue: DefaultPool
resources:
# Always-on: core IIS site
- name: IIS Site
type: OpenDsc.Windows/Service
properties:
name: W3SVC
state: Running
# Feature-gated: HTTPS binding
- name: HTTPS Binding
type: OpenDsc.Windows/Environment
condition: "[parameters('enableHttps')]"
properties:
name: WEBSITE_HTTPS_ENABLED
value: "1"
scope: Machine
# Feature-gated: WAF module
- name: WAF Module
type: OpenDsc.Windows/OptionalFeature
condition: "[parameters('enableWaf')]"
properties:
name: IIS-RequestFiltering
state: Enabled
# Feature-gated: diagnostics agent
- name: Diagnostics Agent
type: OpenDsc.Windows/Service
condition: "[parameters('enableDiagnosticsExtension')]"
properties:
name: DiagnosticsAgent
state: Running
Tip
All feature flag parameters share a consistent naming convention
(enable<Feature>) and are grouped together at the top of the parameters
block. This makes it easy to scan which capabilities the configuration
supports.
Scope Hierarchy for Progressive Rollout
The Pull Server merges parameters from broad to narrow scope, with narrower scopes winning. This merge order makes it natural to leave feature flags disabled at the Default scope and enable them at a narrower scope for canary evaluation.
flowchart LR
Default["Default<br/>(flags: false)"] --> Canary["Canary/Environment"]
Canary --> Node["Node"]
Strategy 1 — Environment Scope Type
Use your existing Environment scope type when your canary boundary aligns
with an environment (e.g., Staging before Production).
Scope setup:
| Scope Type | Precedence | Scope Values |
|---|---|---|
| Default | 0 | (no values) |
| Environment | 1 | Development, Staging, Production |
| Node | 2 | (per-node overrides) |
Default parameters — flags are false everywhere:
# Default/parameters.yaml
enableHttps: false
enableWaf: false
enableDiagnosticsExtension: false
siteName: DefaultSite
appPoolName: DefaultPool
Staging parameters — enable the canary feature:
# Environment/Staging/parameters.yaml
enableHttps: true # validate HTTPS end-to-end in staging
enableWaf: false # not yet ready for WAF
Production parameters — only promote once staging validation passes:
# Environment/Production/parameters.yaml
enableHttps: true # promoted after staging sign-off
enableWaf: false # still gated
All nodes tagged with the Staging environment scope value receive
enableHttps: true; production nodes remain unaffected until the production
parameter file is updated.
Strategy 2 — Dedicated Canary Scope Type
Create a custom Canary scope type when you want a canary ring that cuts across environments or regions. This is useful for soak-testing a feature on a hand-picked set of nodes regardless of where they live.
Scope setup:
| Scope Type | Precedence | Scope Values |
|---|---|---|
| Default | 0 | (no values) |
| Environment | 1 | Development, Staging, Production |
| Canary | 2 | early-access, soak |
| Node | 3 | (per-node overrides) |
Because Canary has higher precedence than Environment, a node tagged
with both Production and early-access will have its canary parameters win
over the environment parameters.
Canary/early-access parameters:
Tagging a node into the canary ring (via the API or web UI):
POST /api/v1/node-tags
{
"nodeId": "<node-guid>",
"scopeTypeId": "<canary-scope-type-guid>",
"scopeValueId": "<early-access-scope-value-guid>"
}
The merge result for a node tagged Production + early-access is:
| Parameter | Default | Environment/Production | Canary/early-access | Merged |
|---|---|---|---|---|
enableHttps |
false | true | (not set) | true |
enableWaf |
false | false | true | true |
enableDiagnosticsExtension |
false | false | true | true |
All other production nodes receive enableWaf: false and
enableDiagnosticsExtension: false.
Rollout Workflow
sequenceDiagram
participant Author as Configuration Author
participant Default as Default Parameters
participant Canary as Canary Parameters
participant Prod as Production Parameters
Author->>Default: Add enableWaf: false (feature authored, dormant)
Author->>Canary: Set enableWaf: true (enable for canary ring)
Note over Canary: Canary nodes apply WAF, monitoring begins
alt Canary validation passes
Author->>Prod: Set enableWaf: true (promote to production)
Note over Prod: All production nodes receive the feature
Author->>Default: Optionally flip default to true (clean-up)
else Canary validation fails
Author->>Canary: Set enableWaf: false (roll back silently)
Note over Canary: Feature disabled, no production impact
end
Step-by-Step
-
Author the feature - Add the conditional resource block and the
false-defaulted parameter to the configuration. Publish a new configuration version. -
Enable in canary - Set the flag to
truein the appropriate scope parameters file (Canary scope value or a lower environment). No configuration version change required. -
Monitor - Observe compliance reports from canary nodes in the Pull Server dashboard (Reports → filter by scope value or node tag).
-
Promote or revert:
- Promote: Set the flag to
truein the production (or Default) parameters. Canary nodes are unaffected (alreadytrue). -
Revert: Set the flag back to
falsein the canary parameters. Production is never touched. -
Clean up - Once a feature is fully promoted and stable, you can remove the
conditionfrom the resource block and delete the flag parameter in a subsequent configuration version.
Best Practices
Naming and Organization
- Prefix all feature flag parameters with
enablefor discoverability. - Group feature flags together in the
parametersblock, separated from operational settings by a comment. - Keep flag names stable across configuration versions so existing parameter files remain valid.
Default to Off
Always set defaultValue: false. A flag that defaults to true is no longer a
controlled rollout — it is a default-on feature that must be actively disabled
everywhere you don't want it, which inverts the safety model.
One Flag Per Feature
Avoid coupling multiple features under a single flag. Independent flags let you promote and revert each capability separately without unintentionally affecting others.
Parameter Schema Validation
Define your feature flag parameters in the configuration's parameter schema so
the Pull Server can validate parameter files before activation. This prevents
typos (enableWaf: true) from silently failing to enable features.
# parameter-schema.yaml (reference schema for the configuration)
$schema: https://json-schema.org/draft/2020-12/schema
type: object
properties:
enableHttps:
type: boolean
default: false
enableWaf:
type: boolean
default: false
enableDiagnosticsExtension:
type: boolean
default: false
additionalProperties: false
Canary Ring Size
Start small. Tag 1–5 nodes in the canary scope value for the initial soak period. Widen the ring by tagging additional nodes or promoting to a higher-traffic scope value before full production rollout.
Compliance Reporting
Use the Pull Server's compliance reports to confirm canary nodes have applied
the new feature before promoting. A healthy canary shows _inDesiredState: true
for the feature-gated resource blocks.
Example: Single Configuration, Three Rollout Stages
web-server (v2.0.0)
├── Default/parameters.yaml enableWaf: false ← baseline
├── Canary/soak/parameters.yaml enableWaf: true ← soak ring
├── Environment/Staging/parameters.yaml ← inherits Default (false)
└── Environment/Production/parameters.yaml ← inherits Default (false)
Week 1 — 3 soak-ring nodes in production receive WAF. All other production and staging nodes are unaffected.
Week 2 — Monitoring shows no regressions. Promote:
All production nodes now receive WAF. The Canary/soak entry can be left
as-is or removed (it would resolve to true from both scopes).