Skip to main content

Getting started with DSC 3.0 – Part 4: Configuring IIS with multiple resources and dependsOn

Until now we configured one resource at a time. A real server needs several, and they depend on each other. You can't create a website before IIS is installed, and you can't attach a web application to a website that doesn't exist yet.

In this post we set up IIS, an application pool, a website and a web application in a single configuration document, and use dependsOn to control the order.

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

What we are building

Six resource instances, with these dependencies:

  • IIS (Windows feature): no dependencies
  • Site folder and App folder: no dependencies
  • Demo app pool: needs IIS
  • Demo site: needs the app pool and the site folder
  • Demo application: needs the site, the app pool and the app folder

What you need

  • DSC v3.2 or later. The directives syntax used below was introduced in 3.2
  • Windows Server, because the WindowsFeature resource needs the Server Manager module
  • The WebAdministrationDsc module, installed for Windows PowerShell

Run this in a Windows PowerShell (not PowerShell 7) session:

Install-Module -Name WebAdministrationDsc -Scope AllUsers

Remark: the Microsoft.Adapter/WindowsPowerShell adapter runs PSDSC resources in Windows PowerShell (powershell.exe), so the module must be available there. You can check what the adapter discovers with dsc resource list --adapter Microsoft.Adapter/WindowsPowerShell.

The configuration document

Save this as iis.dsc.yaml:

$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
directives:
  securityContext: elevated
resources:
- name: IIS
  type: PSDesiredStateConfiguration/WindowsFeature
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    Name: Web-Server
    Ensure: Present
- name: Site folder
  type: PSDesiredStateConfiguration/File
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    DestinationPath: C:\inetpub\demo\site
    Type: Directory
    Ensure: Present
- name: App folder
  type: PSDesiredStateConfiguration/File
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    DestinationPath: C:\inetpub\demo\app
    Type: Directory
    Ensure: Present
- name: Demo app pool
  type: WebAdministrationDsc/WebAppPool
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    Name: DemoAppPool
    Ensure: Present
    State: Started
    managedRuntimeVersion: v4.0
  dependsOn:
  - "[resourceId('PSDesiredStateConfiguration/WindowsFeature', 'IIS')]"
- name: Demo site
  type: WebAdministrationDsc/WebSite
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    Name: DemoSite
    Ensure: Present
    State: Started
    PhysicalPath: C:\inetpub\demo\site
    ApplicationPool: DemoAppPool
    BindingInfo:
    - Protocol: http
      Port: 8080
  dependsOn:
  - "[resourceId('WebAdministrationDsc/WebAppPool', 'Demo app pool')]"
  - "[resourceId('PSDesiredStateConfiguration/File', 'Site folder')]"
- name: Demo application
  type: WebAdministrationDsc/WebApplication
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    Website: DemoSite
    Name: app
    WebAppPool: DemoAppPool
    PhysicalPath: C:\inetpub\demo\app
    Ensure: Present
  dependsOn:
  - "[resourceId('WebAdministrationDsc/WebSite', 'Demo site')]"
  - "[resourceId('WebAdministrationDsc/WebAppPool', 'Demo app pool')]"
  - "[resourceId('PSDesiredStateConfiguration/File', 'App folder')]"

Quite a bit of YAML, so let's look at the important parts.

How dependsOn works

If you used PowerShell DSC before, you know this syntax:

DependsOn = '[WindowsFeature]IIS'

That's gone. In DSC 3.0 a dependency is a resourceId() call with two arguments: the resource type and the instance name:

dependsOn:
- "[resourceId('PSDesiredStateConfiguration/WindowsFeature', 'IIS')]"

A few things to keep in mind:

  • dependsOn takes a list, so one resource can depend on several others. The web application waits for the site, the app pool and the app folder.
  • The instance name is the name of the instance in the document (IIS), not a property like Name inside properties.
  • The order in the document doesn't matter. DSC sorts by dependencies. I listed everything top-down for readability, but you can move the web application to the top and get the same result.

Remark: with the old adapter you had to nest the PSDSC resources inside the adapter instance, and dependsOn only worked between neighbors inside it. With requireAdapter every instance is top-level, so dependencies just work across the document.

The other directives

Two directives in the document are worth a short explanation:

  • requireAdapter on each instance tells DSC which adapter runs the resource. You can skip it, and DSC then uses the first adapter that can run the resource. I prefer being explicit.
  • securityContext: elevated on the document level states that this document needs an elevated session.

The adapters

We use requireAdapter on every instance in this document, so it's worth understanding what it does.

DSC's own resources are self-contained. A resource manifest tells DSC which executable to call and which properties the resource has. DSC can call it directly.

PowerShell DSC resources like WindowsFeature, File and WebSite don't work like that. They are PowerShell modules, with a MOF schema or a PowerShell class, and they only run inside a PowerShell engine. DSC can't call them directly.

An adapter is the bridge. It's a resource whose job is to:

  • Discover the PSDSC resources installed on the machine
  • Tell DSC which properties each of them has
  • Invoke them for the get, set and test operations

You can see them in the resource list, as a separate kind:

dsc resource list

Look at the Kind column. Normal resources show up as Resource, adapters as Adapter.

Which adapters are there?

  • Microsoft.Adapter/WindowsPowerShell: runs PSDSC resources in Windows PowerShell 5.1, on top of the PSDSC 1.1 engine. It supports script-based, MOF-based and class-based resources. Windows only. This is the one we use in this post.
  • Microsoft.Adapter/PowerShell: runs class-based PSDSC resources in PowerShell 7. Works on Windows, Linux and macOS.
  • Microsoft.Windows/WMI: exposes WMI classes as resources.

Remark: before DSC 3.2 the first two were called Microsoft.Windows/WindowsPowerShell and Microsoft.DSC/PowerShell. They still work, but they are deprecated and will be removed in DSC 4.0. If you see the old names somewhere, that's why.

Apply it

Open an elevated terminal and run:

dsc config set --file iis.dsc.yaml

This takes a while on a fresh machine. IIS needs to be installed first, and everything else follows.

Remark: The adapter runs Windows PowerShell DSC resources through the Local Configuration Manager (LCM), which requires a local WinRM connection. If WinRM is disabled, you should enable it first.

To check the result, use appcmd, which comes with IIS:

& "$env:windir\system32\inetsrv\appcmd.exe" list site
& "$env:windir\system32\inetsrv\appcmd.exe" list app
& "$env:windir\system32\inetsrv\appcmd.exe" list apppool

Now you can run the test operation to confirm everything is in the desired state:

dsc config test --file iis.dsc.yaml

Troubleshooting

Using the example above didn't work as I would expect. First, it requires the WebAdministrationDSC module to be installed:

Install-Module -Name WebAdministrationDsc -RequiredVersion 4.2.1

But even after the module got installed, the execution of the configuration failed.

ERROR PID 6452: Script failed with terminating error at line 0 for statement `` - System.Management.Automation.MethodInvocationException: Exception calling "Invoke" with "4" argument(s)

Further investigation showed that the problem was related to the website configuration. It seems that DSC 3.0 at the moment gets into trouble when you have child properties configured like we have in the BindingInfo for the website:

$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
directives:
  securityContext: elevated
resources:
- name: Demo site
  type: WebAdministrationDsc/WebSite
  directives:
    requireAdapter: Microsoft.Adapter/WindowsPowerShell
  properties:
    Name: DemoSite
    Ensure: Present
    State: Started
    PhysicalPath: C:\inetpub\demo\site
    ApplicationPool: DemoAppPool
    BindingInfo:
    - Protocol: http
      Port: 8080
  dependsOn:
  - "[resourceId('WebAdministrationDsc/WebAppPool', 'Demo app pool')]"
  - "[resourceId('PSDesiredStateConfiguration/File', 'Site folder')]"

Removing the BindingInfo fixed the issue but doesn't count for me as a real solution.

As a workaround, I switched to raw powershell scripts to create the sites. I'll add a general troubleshooting post at the end of this series with all the details.

dependsOn is about order, not state

This is an important one. dependsOn only decides in which order DSC processes the resources. It does not check that the dependency is actually in the desired state before moving on.

Remark: if you need a real gate, there is the Microsoft.DSC/Assertion resource. That's for the next post.

More information

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’: ...