11 KiB
Winget Configure vs DSC CLI: In-Depth Comparison
Both winget configure and the dsc CLI can apply declarative configurations that include winget packages. This doc explains when to use each, their differences, and practical examples.
Quick Comparison
| Aspect | winget configure |
dsc CLI |
|---|---|---|
| Focus | Winget-centric configurations | General-purpose DSC (any resource) |
| Output | Human-friendly, interactive | JSON output, scriptable |
| State checking | Limited | Full (get, test, set) |
| Diffing | No built-in | Yes (dsc config test) |
| Complexity | Simpler | More powerful |
| Install | Built into winget | Separate install (winget install Microsoft.DSC) |
Entry Points
Winget Configure
# Apply a configuration
winget configure ./config.dsc.yaml
# Apply with auto-accept (for scripts)
winget configure ./config.dsc.yaml --accept-configuration-agreements
# Show what would happen (dry run) — limited support
winget configure show ./config.dsc.yaml
DSC CLI
# Check current state (what IS installed)
dsc config get --file ./config.dsc.yaml
# Test if current state matches desired (diffing)
dsc config test --file ./config.dsc.yaml
# Apply the configuration
dsc config set --file ./config.dsc.yaml
# Export current state to a config
dsc config export --resource Microsoft.WinGet.DSC/WinGetPackage
Configuration File Format
Both use the same YAML format (DSCv3 schema):
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: install-vscode
directives:
description: Install VS Code
allowPrerelease: true
settings:
id: Microsoft.VisualStudioCode
source: winget
Example Project Structures
Option A: Winget Configure (Simple Setup)
Best for: Personal machine setup, quick provisioning
my-windows-config/
├── config.dsc.yaml # Main configuration
├── README.md
└── install.ps1 # Optional wrapper script
config.dsc.yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
# Developer Tools
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Visual Studio Code
settings:
id: Microsoft.VisualStudioCode
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Git
settings:
id: Git.Git
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
directives:
description: Node.js LTS
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0" # Pinned version
install.ps1
#!/usr/bin/env pwsh
Write-Host "Applying Windows configuration..." -ForegroundColor Cyan
winget configure ./config.dsc.yaml --accept-configuration-agreements
Write-Host "Done!" -ForegroundColor Green
Usage:
.\install.ps1
# or directly:
winget configure ./config.dsc.yaml
Option B: DSC CLI (Advanced/Enterprise)
Best for: CI/CD, drift detection, mixed resource types, automation
my-windows-config/
├── configs/
│ ├── packages.dsc.yaml # Winget packages
│ ├── settings.dsc.yaml # Windows settings/registry
│ └── full.dsc.yaml # Combined config
├── scripts/
│ ├── apply.ps1
│ ├── test.ps1 # Drift detection
│ └── export.ps1 # Generate config from current state
├── lockfiles/
│ └── packages.lock.yaml # Version-locked config
└── README.md
configs/packages.dsc.yaml
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vscode
directives:
description: Visual Studio Code
settings:
id: Microsoft.VisualStudioCode
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: git
directives:
description: Git
settings:
id: Git.Git
source: winget
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: nodejs
directives:
description: Node.js LTS
settings:
id: OpenJS.NodeJS.LTS
source: winget
scripts/apply.ps1
#!/usr/bin/env pwsh
param(
[string]$Config = "./configs/packages.dsc.yaml"
)
Write-Host "Testing current state..." -ForegroundColor Cyan
$testResult = dsc config test --file $Config | ConvertFrom-Json
if ($testResult.results | Where-Object { $_.result.inDesiredState -eq $false }) {
Write-Host "Drift detected. Applying configuration..." -ForegroundColor Yellow
dsc config set --file $Config
} else {
Write-Host "System is already in desired state." -ForegroundColor Green
}
scripts/test.ps1 (Drift Detection)
#!/usr/bin/env pwsh
param(
[string]$Config = "./configs/packages.dsc.yaml"
)
Write-Host "Checking for configuration drift..." -ForegroundColor Cyan
$result = dsc config test --file $Config | ConvertFrom-Json
$drifted = $result.results | Where-Object { $_.result.inDesiredState -eq $false }
if ($drifted) {
Write-Host "`nDrift detected in:" -ForegroundColor Red
$drifted | ForEach-Object {
Write-Host " - $($_.resource.type): $($_.resource.id)" -ForegroundColor Yellow
}
exit 1
} else {
Write-Host "No drift detected. System matches desired state." -ForegroundColor Green
exit 0
}
scripts/export.ps1 (Generate Config from Current State)
#!/usr/bin/env pwsh
# Export currently installed winget packages to a DSC config
$packages = winget list --source winget | Select-Object -Skip 2 | ForEach-Object {
$parts = $_ -split '\s{2,}'
if ($parts.Length -ge 2) {
@{
name = $parts[0]
id = $parts[1]
version = if ($parts.Length -ge 3) { $parts[2] } else { $null }
}
}
} | Where-Object { $_ -ne $null }
# Output as DSC YAML format
Write-Output "# Auto-generated from current system state"
Write-Output "# yaml-language-server: `$schema=https://aka.ms/configuration-dsc-schema/0.2"
Write-Output "properties:"
Write-Output " configurationVersion: 0.2.0"
Write-Output " resources:"
foreach ($pkg in $packages) {
Write-Output " - resource: Microsoft.WinGet.DSC/WinGetPackage"
Write-Output " id: $($pkg.id.ToLower() -replace '[^a-z0-9]', '-')"
Write-Output " settings:"
Write-Output " id: $($pkg.id)"
Write-Output " source: winget"
if ($pkg.version) {
Write-Output " version: `"$($pkg.version)`""
}
Write-Output ""
}
Version Locking
With Winget Configure
Pin versions directly in the YAML:
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0" # Exact version lock
With DSC CLI
Same approach, but you can also generate a locked config:
# Get current installed versions
dsc config get --file ./config.dsc.yaml > current-state.json
# Or use winget export with versions
winget export -o winget-lock.json --include-versions
Lockfile Pattern:
Create two files:
packages.dsc.yaml— Desired packages (may float versions)packages.lock.dsc.yaml— Locked versions for reproducibility
# packages.lock.dsc.yaml - Generated/maintained with exact versions
properties:
configurationVersion: 0.2.0
resources:
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: nodejs
settings:
id: OpenJS.NodeJS.LTS
source: winget
version: "20.10.0"
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: git
settings:
id: Git.Git
source: winget
version: "2.43.0"
Diffing / Drift Detection
Winget Configure
Limited support — winget configure show gives some info but no true diff:
winget configure show ./config.dsc.yaml
DSC CLI (Full Diffing)
# Test returns JSON with inDesiredState boolean for each resource
dsc config test --file ./config.dsc.yaml
Example output:
{
"results": [
{
"resource": {
"type": "Microsoft.WinGet.DSC/WinGetPackage",
"id": "vscode"
},
"result": {
"inDesiredState": true
}
},
{
"resource": {
"type": "Microsoft.WinGet.DSC/WinGetPackage",
"id": "nodejs"
},
"result": {
"inDesiredState": false,
"diff": {
"version": {
"desired": "20.10.0",
"actual": "18.19.0"
}
}
}
}
]
}
CI/CD Integration:
# In a CI pipeline - fail if drift detected
$result = dsc config test --file ./config.dsc.yaml | ConvertFrom-Json
$hasDrift = $result.results | Where-Object { -not $_.result.inDesiredState }
if ($hasDrift) {
Write-Error "Configuration drift detected!"
exit 1
}
When to Use Which
Use winget configure when:
- ✅ You want a simple, interactive setup experience
- ✅ Your config is mostly/entirely winget packages
- ✅ You're doing one-time machine provisioning
- ✅ You don't need drift detection
- ✅ You want minimal dependencies (built into winget)
Use dsc CLI when:
- ✅ You need drift detection / compliance checking
- ✅ You're building CI/CD pipelines
- ✅ You want JSON output for automation
- ✅ You're mixing winget with other DSC resources (registry, files, etc.)
- ✅ You need the full
get/test/setlifecycle - ✅ Enterprise configuration management
Mixed Resource Example (DSC Only)
DSC can combine winget packages with other configuration types:
# yaml-language-server: $schema=https://aka.ms/configuration-dsc-schema/0.2
properties:
configurationVersion: 0.2.0
resources:
# Install VS Code
- resource: Microsoft.WinGet.DSC/WinGetPackage
id: vscode
settings:
id: Microsoft.VisualStudioCode
source: winget
# Configure VS Code settings (after install)
- resource: Microsoft.Windows.Developer/DeveloperMode
id: enable-dev-mode
settings:
Ensure: Present
# Set environment variable
- resource: Microsoft.Windows.Developer/EnvironmentVariable
id: set-editor
settings:
Name: EDITOR
Value: code
Target: User
This is where DSC really shines — you can configure the entire system state, not just packages.
Summary
| Task | Winget Configure | DSC CLI |
|---|---|---|
| Apply config | winget configure ./config.yaml |
dsc config set --file ./config.yaml |
| Check current state | ❌ | dsc config get --file ./config.yaml |
| Diff/test | Limited | dsc config test --file ./config.yaml |
| Output format | Human text | JSON (scriptable) |
| Version locking | Manual in YAML | Manual in YAML + export tools |
| Multiple resource types | Winget only | Any DSC resource |