# 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 ```powershell # 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 ```powershell # 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 # 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 # 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** ```powershell #!/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:** ```powershell .\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 # 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** ```powershell #!/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) ```powershell #!/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) ```powershell #!/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: ```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: ```powershell # 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 ```yaml # 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: ```powershell winget configure show ./config.dsc.yaml ``` ### DSC CLI (Full Diffing) ```powershell # Test returns JSON with inDesiredState boolean for each resource dsc config test --file ./config.dsc.yaml ``` **Example output:** ```json { "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:** ```powershell # 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`/`set` lifecycle - ✅ Enterprise configuration management --- ## Mixed Resource Example (DSC Only) DSC can combine winget packages with other configuration types: ```yaml # 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 |