Skip to main content

Getting started with DSC 3.0 – Part 5: Guarding your configuration with Assertion

At the end of the previous post I said that dependsOn only decides the order in which resources are processed. It doesn't check that the resource you depend on is actually in the desired state.

Sometimes that's not enough. You want to say "only touch this machine if it's Windows Server 2025", or "only configure the website if IIS is really installed". That's what the Microsoft.DSC/Assertion resource is for.

Note: this post is part of a bigger series on Microsoft Desired State Configuration.

What is an assertion?

An assertion is a group resource. It contains a nested configuration document and runs the test operation on every resource inside it. It never changes anything.

  • If every nested resource is in the desired state, the assertion passes.
  • If one of them isn't, the assertion fails, and DSC doesn't invoke the resources that depend on it.

Only the resources that dependsOn the assertion are affected. The rest of the document runs as usual.

Remark: resources that only implement get, like Microsoft/OSInfo, are a natural fit for assertions. They can read the state but not change it.

A minimal example

Save this as assertion.dsc.yaml:

$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Demo key
  type: Microsoft.Windows/Registry
  properties:
    keyPath: HKCU\Software\Demo
    _exist: true
  dependsOn:
  - "[resourceId('Microsoft.DSC/Assertion', 'Windows only')]"
- name: Windows only
  type: Microsoft.DSC/Assertion
  properties:
    $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
    resources:
    - name: Operating system
      type: Microsoft/OSInfo
      properties:
        family: Windows

Two things to notice:

  • The assertion has its own $schema and resources, nested inside properties
  • The registry key depends on the assertion with the same resourceId() syntax we used in Part 4, with Microsoft.DSC/Assertion as the type and the instance name

Apply it:

dsc config set --file assertion.dsc.yaml

The operating system is Windows, so the assertion passes and DSC creates the key. 

Check it:

Test-Path HKCU:\Software\Demo


Let it fail

Remove the key first:

Remove-Item HKCU:\Software\Demo

Now change family: Windows to family: Linux in the document and run the same command again:

dsc config set --file assertion.dsc.yaml

The assertion fails, so DSC doesn't invoke the registry resource. Run Test-Path again and the key is not there.

Remark: the registry resource is listed before the assertion in the document. Like we saw in Part 4, the order in the document doesn't matter. The dependency does.

A more realistic case

A common use is applying settings depending on the Windows Server version. The OS version check is the gate:

$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: Baseline tag
  type: Microsoft.Windows/Registry
  properties:
    keyPath: HKLM\SOFTWARE\Contoso
    valueName: Baseline
    valueData:
      String: Server2025
  dependsOn:
  - "[resourceId('Microsoft.DSC/Assertion', 'Server 2025 check')]"
- name: Server 2025 check
  type: Microsoft.DSC/Assertion
  properties:
    $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
    resources:
    - name: Server 2025
      type: Microsoft/OSInfo
      properties:
        version: "10.0.26100"

Add one pair like this per OS version and each machine only gets the settings that fit.

Here the version is an exact match. DSC 3.3 added version comparison to Microsoft/OSInfo, so you can assert a constraint instead. 

Remark: this one writes to HKLM, so run it from an elevated terminal.

Back to our previous example

In Part 4, dependsOn made sure IIS was installed before the web resources. But dsc config test on a clean machine could still fail, because the IIS cmdlets weren't there yet.

An assertion that checks the Web Server role fixes that. Add this to the document from Part 4:

- name: IIS is installed
  type: Microsoft.DSC/Assertion
  properties:
    $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
    resources:
    - name: Web server role
      type: PSDesiredStateConfiguration/WindowsFeature
      directives:
        requireAdapter: Microsoft.Adapter/WindowsPowerShell
      properties:
        Name: Web-Server
        Ensure: Present
  dependsOn:
  - "[resourceId('PSDesiredStateConfiguration/WindowsFeature', 'IIS')]"

And let the web resources depend on the assertion. For the application pool:

  dependsOn:
  - "[resourceId('Microsoft.DSC/Assertion', 'IIS is installed')]"

Give the website and the web application the same dependency, next to the ones they already have.

The flow is now: install IIS, check that IIS is installed, and only then configure the pool, the site and the application. If the check fails, DSC doesn't touch them.

Things to keep in mind

  • An assertion only gates the resources that depend on it
  • It never changes anything. If you want DSC to fix something, that's a normal resource
  • The assertion itself must be a top-level instance. A resource inside a group can only depend on its neighbors in the same group, so the IIS install and the assertion stay at the top

We are not there yet. In our next and probably last post about DSC, we'll add AI into the mix. 

Popular posts from this blog

Podman– Command execution failed with exit code 125

After updating WSL on one of the developer machines, Podman failed to work. When we took a look through Podman Desktop, we noticed that Podman had stopped running and returned the following error message: Error: Command execution failed with exit code 125 Here are the steps we tried to fix the issue: We started by running podman info to get some extra details on what could be wrong: >podman info OS: windows/amd64 provider: wsl version: 5.3.1 Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM Error: unable to connect to Podman socket: failed to connect: dial tcp 127.0.0.1:2655: connectex: No connection could be made because the target machine actively refused it. That makes sense as the podman VM was not running. Let’s check the VM: >podman machine list NAME         ...

Cache stampede: when our cache turned against us

While investigating some performance issues, we ran into an ASP.NET Core API that cached a fairly expensive aggregation query for 60 seconds. Under normal load, that was fine: one request rebuilds the cache, everyone else reads from it. Under peak load, dozens of requests would arrive in that same expiry window, all see a cache miss, and all fire the same expensive query in parallel. The database didn't like that. That was the moment when our caching layer stopped helping and started hurting. A burst of requests comes in at the same time, all miss the cache, and all go hammer the database or the downstream API at once. That's a cache stampede . The cache was supposed to protect our backend, and for a few hundred milliseconds it did the opposite. Why this happens IMemoryCache.GetOrCreate (and its async sibling) looks like it protects you, but it doesn't add any locking on its own. Look at the naive version: public async Task<Report> GetReportAsync(string key) ...

VS Code Planning mode

After the introduction of Plan mode in Visual Studio , it now also found its way into VS Code. Planning mode, or as I like to call it 'Hannibal mode', extends GitHub Copilot's Agent Mode capabilities to handle larger, multi-step coding tasks with a structured approach. Instead of jumping straight into code generation, Planning mode creates a detailed execution plan. If you want more details, have a look at my previous post . Putting plan mode into action VS Code takes a different approach compared to Visual Studio when using plan mode. Instead of a configuration setting that you can activate but have limited control over, planning is available as a separate chat mode/agent: I like this approach better than how Visual Studio does it as you have explicit control when plan mode is activated. Instead of immediately diving into execution, the plan agent creates a plan and asks some follow up questions: You can further edit the plan by clicking on ‘Open in Editor’: ...