Files
winos-config/docs/winget-configure-vs-dsc.md
EthanShoeDev 01a35296a9 init
2025-11-30 17:52:15 -05:00

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/set lifecycle
  • 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