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.
- Getting started with DSC 3.0 – Part 1: What is DSC and what changed?
- Getting started with DSC 3.0 – Part 2: Managing a Windows service end to end
- Getting started with DSC 3.0 – Part 3: Capturing the current state with dsc config export
- Getting started with DSC 3.0 – Part 4: Configuring IIS with multiple resources and dependsOn (this post)
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
directivessyntax used below was introduced in 3.2 - Windows Server, because the
WindowsFeatureresource 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:
dependsOntakes 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
nameof the instance in the document (IIS), not a property likeNameinsideproperties. - 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:
requireAdapteron 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: elevatedon 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,setandtestoperations
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.