Script

PowerShell DSC vs Ansible vs Terraform: Infrastructure as Code Comparison (2026)

19 min read

Search “infrastructure as code” and you’ll find PowerShell DSC, Ansible, and Terraform mentioned in the same breath. It sounds like they compete for the same job. They don’t. Terraform provisions infrastructure: VMs, networks, storage. Ansible and PowerShell DSC configure whatever’s already running on that infrastructure. Mix up those two jobs and you’ll pick the wrong tool. Then you’ll get frustrated when it doesn’t behave the way you expected.

Quick verdict: Building or tearing down cloud or virtualization resources? Use Terraform. Nothing else here tracks state the way it does. Configuring a mixed fleet of Windows and Linux servers that already exist? Ansible’s agentless SSH/WinRM model is your most flexible option. Running a pure-Windows or Azure-only shop that just needs native OS-level desired-state enforcement? Skip the extra orchestration layer. PowerShell DSC, especially the newer cross-platform DSC v3 engine, is a legitimate, often simpler choice. Most teams end up running Terraform + Ansible together. They save DSC for shops that are Windows-only top to bottom.

Overview

These three tools get lumped together because they all promise “declarative infrastructure.” But they solve different layers of the stack. Terraform answers one question: does this resource exist, and does it match what I declared? Ansible and DSC answer a different question: is this running server configured the way I want? You can use all three in the same environment without conflict. Just make sure each one owns a distinct layer.

What We’re Comparing

ToolDescriptionBest ForPrice
TerraformDeclarative provisioning tool that creates, updates, and destroys infrastructure resources using a tracked state fileMulti-cloud/hybrid VM, network, and storage provisioningFree CLI (BSL 1.1); HCP Terraform from $0.10/resource/month
AnsibleAgentless configuration management and orchestration tool using SSH (Linux) and WinRM (Windows)Configuring existing mixed Windows/Linux fleets at scaleFree (open source, ansible-core)
PowerShell DSCMicrosoft’s native declarative configuration engine using MOF files and a push/pull model; now cross-platform via DSC v3Pure-Windows or Azure-centric configuration managementFree (Microsoft tool, no license fee)

Feature Comparison

At a Glance

FeatureTerraformAnsiblePowerShell DSC
Primary jobProvisioningConfiguration managementConfiguration management
Agent requiredNo (state file instead)No (agentless)No (push) / lightweight pull client (pull mode)
Connection methodProvider APIs (cloud/HTTP)SSH (Linux), WinRM (Windows)Local execution or pull server sync
Config languageHCL (declarative)YAML playbooksPowerShell-based DSL compiled to MOF
State trackingYes terraform.tfstateNo built-in state filePartial; current state queried live per resource
Cross-platformYes (via providers)Yes (control node: Linux/macOS/WSL; managed nodes: Linux + Windows)Yes as of DSC v3 (Windows, Linux, macOS)
IdempotencyYesYes (per-module)Yes
Native Windows integrationVia provider onlyVia WinRMNative
Learning curveModerate (HCL + state concepts)Low (YAML)Moderate (PowerShell + MOF concepts)
Best paired withAnsible or DSCTerraformStandalone or with Azure Automation

Provisioning vs. Configuration Management: The Core Distinction

This is the idea that trips up nearly everyone new to IaC, worth spelling out clearly.

Provisioning means creating, resizing, or destroying the infrastructure resource itself: the VM, the virtual network, the DNS record, the storage bucket. Before provisioning, the resource doesn’t exist. After provisioning, it exists with the specs you declared: CPU, RAM, disk, network attachment. Provisioning is Terraform’s whole reason for existing. It keeps a state file that records exactly what it created. On every terraform plan, it compares your declared configuration against reality. Then it tells you exactly what would change.

Configuration management means changing the software and settings inside a resource that already exists. Installing packages, writing config files, creating local users, setting registry keys, enforcing a security baseline: none of that requires creating or destroying the VM. Ansible and PowerShell DSC both live here. Neither one tracks “did I create this VM.” That’s not their job. They assume the target host is already reachable. Their focus is reconciling its internal state.

Winner: neither. This is a category, not a competition. The mistake to avoid: making Terraform manage in-guest configuration, or making Ansible/DSC provision cloud resources from scratch. Terraform’s provisioner blocks technically let you do the former. HashiCorp’s own docs discourage it, for good reason. Neither Ansible nor DSC tracks state the way a dedicated provisioning tool does. Use them to spin up VMs from nothing, and you give up the one thing Terraform is actually built for.

PowerShell DSC: Push/Pull Model and MOF Files

PowerShell DSC compiles a declarative configuration script into a MOF (Managed Object Format) file: a standardized, machine-readable description of a node’s desired state. That MOF file gets applied one of two ways:

  • Push mode: You run Start-DscConfiguration (classic DSC) or dsc config set (DSC v3) directly against a target node from your admin workstation. Good for small numbers of servers or ad hoc changes.
  • Pull mode: Nodes check in periodically with a central pull server, such as an SMB share, an Azure Automation DSC endpoint, or a custom pull server, and download their MOF automatically. This is how DSC scales past a handful of machines without needing a separate orchestration engine.

As of PowerShell 7.2, the PSDesiredStateConfiguration module no longer ships in the box. Install it separately from the PowerShell Gallery if you need classic DSC resources. Microsoft’s newer direction is DSC v3, a standalone, cross-platform binary maintained in the PowerShell/DSC GitHub repository. It runs natively on Windows, Linux, and macOS. That’s a real shift from DSC’s historically Windows-only footprint.

Microsoft Learn official DSC v3 overview page showing the product description and cross-platform positioning
GitHub repository page for PowerShell/DSC showing the current release tag and repository structure

Winner: PowerShell DSC for pure-Windows or Azure environments. Nothing else here integrates as tightly with Windows-native concepts like registry keys, local users, and Windows Features without a translation layer.

Ansible: Agentless Architecture and Playbook Basics

Ansible’s defining trait is simple: no permanent agent on managed nodes. It connects, runs its Python-based modules (or PowerShell modules for Windows), and disconnects. Nothing persists on the target host between runs. Connection method depends on the OS:

  • Linux/Unix nodes: SSH, using standard key-based or password authentication. Managed nodes need Python 3.9–3.14 installed.
  • Windows nodes: WinRM (Windows Remote Management), authenticated via NTLM, Kerberos, or certificate-based auth. Managed nodes need PowerShell 5.1–7.0.

The control node itself (where you run ansible-playbook) requires Python 3.12–3.14 for the current Ansible 14 release line.

A minimal inventory file looks like this:

# inventory.ini
[linux_servers]
web01.lab.local ansible_user=deploy

[windows_servers]
dc01.lab.local ansible_connection=winrm ansible_port=5986

And a basic playbook that installs a package on Linux hosts and a Windows feature on Windows hosts:

# site.yml
- name: Configure Linux web servers
  hosts: linux_servers
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

- name: Configure Windows domain controllers
  hosts: windows_servers
  tasks:
    - name: Ensure DNS Server feature is installed
      ansible.windows.win_feature:
        name: DNS
        state: present

Run it against your inventory:

ansible-playbook -i inventory.ini site.yml

PLAY [Configure Linux web servers] ********************************

TASK [Gathering Facts] *********************************************
ok: [web01.lab.local]

TASK [Install nginx] ***********************************************
changed: [web01.lab.local]

PLAY RECAP **********************************************************
web01.lab.local : ok=2 changed=1 unreachable=0 failed=0

One task, one change, zero surprises. That PLAY RECAP line is basically Ansible’s report card, and you’ll come to rely on it more than you’d expect.

Terminal output of a basic `ansible-playbook` run against an inventory file, showing task results and the ok/changed/failed play recap summary

Winner: Ansible for any environment that includes more than one OS. It’s the only tool of the three built from the ground up to treat Linux and Windows as equal citizens.

Terraform: Declarative Provisioning and State Management

Terraform’s configuration files use HCL (HashiCorp Configuration Language): a declarative syntax for describing what infrastructure should exist. On every run, Terraform builds an execution plan. It compares your .tf files against its state file. That’s terraform.tfstate by default, or a remote backend like an S3 bucket or HCP Terraform for team use.

A minimal example provisioning a VM (provider details vary):

# main.tf
terraform {
  required_providers {
    libvirt = {
      source  = "dmacvicar/libvirt"
      version = "~> 0.8"
    }
  }
}

resource "libvirt_domain" "test_vm" {
  name   = "test-vm-01"
  memory = 2048
  vcpu   = 2
}

Initialize and plan:

terraform init

Initializing the backend…
Initializing provider plugins…
– Finding dmacvicar/libvirt versions matching “~> 0.8″…
– Installing dmacvicar/libvirt v0.8.1…
Terraform has been successfully initialized!

terraform plan

Terraform will perform the following actions:

# libvirt_domain.test_vm will be created
+ resource “libvirt_domain” “test_vm” {
+ id = (known after apply)
+ memory = 2048
+ name = “test-vm-01”
+ vcpu = 2
}

Plan: 1 to add, 0 to change, 0 to destroy.

One resource, clearly flagged with a plus sign. That’s the entire point of terraform plan, so no surprises when you hit apply.

Official HashiCorp Terraform releases page listing available stable versions for Windows, macOS, and Linux

Because Terraform tracks state, it can safely tear down what it created (terraform destroy) without touching resources it didn’t manage. That’s true as long as nobody makes manual changes outside of Terraform. If someone does, you get drift. That’s why terraform plan is worth running on a schedule, in addition to before deployments.

Winner: Terraform for anything involving creating or destroying infrastructure resources across one or more clouds or hypervisors. Ansible and DSC have no equivalent to a state file. They don’t track “did I create this,” only “is this configured correctly right now.”

Agent and Connectivity Model Comparison

This is the differentiator that actually matters when you’re filling out a firewall change request.

AspectTerraformAnsiblePowerShell DSC
Agent on targetNone (talks to cloud/hypervisor API)None (SSH/WinRM per run)None in push mode; lightweight LCM/pull client in pull mode
Required open portsProvider API endpoints (usually HTTPS/443)TCP 22 (SSH) / TCP 5985|5986 (WinRM)Local execution, or pull server endpoint (custom port)
Central control pointTerraform state (local or remote backend)Ansible control node + inventoryPull server (optional) or ad hoc push
Drift detectionterraform plan diffRe-run playbook (no persistent tracking)Local Configuration Manager (LCM) consistency checks

Pricing Comparison

All three tools are free at the core. Cost only enters the picture with Terraform’s optional hosted platform, HCP Terraform. It bills by Resources Under Management (RUM) instead of a flat per-seat license.

TierPriceNotes
PowerShell DSC (v3 engine + PSDesiredStateConfiguration module)FreeNo license fee documented; standard Microsoft support terms apply
Ansible / ansible-coreFree (open source)Commercial Red Hat Ansible Automation Platform exists separately; pricing not published in official docs referenced here
Terraform CLIFree (BSL 1.1)Unlimited resources/users when self-hosted or run via your own CI
HCP Terraform, Essentials$0.10 per managed resource/monthEntry-tier RUM billing
HCP Terraform, Standard$0.47 per managed resource/monthAdds collaboration features
HCP Terraform, Premium$0.99 per managed resource/monthAdds advanced governance features
Official HashiCorp pricing page showing the Essentials, Standard, and Premium HCP Terraform tiers with resource-based pricing

For homelabs and most on-prem shops, the free CLI tiers cover everything you need. You start paying HashiCorp once you want managed remote state, single sign-on, or policy-as-code governance at scale.

Installing These Tools

Windows

DSC v3 installs cleanly through WinGet on Windows 10/11 or Windows Server 2022+:

winget search DesiredStateConfiguration --source msstore

Name Id Source
————————————————
DesiredStateConfiguration 9NVTPZWRC6KQ msstore

winget install --id 9NVTPZWRC6KQ --source msstore

Verify the install by listing available resources:

dsc resource list
Windows Terminal output of `winget search DesiredStateConfiguration --source msstore` showing the package name, ID, and source
Windows Terminal output of `dsc resource list` after installation, showing the list of available DSC resources
Windows Settings About page showing OS build and version, used to confirm WinGet and DSC v3 prerequisites are met

Ansible’s control node requirements, Python 3.12–3.14, POSIX-style tooling, aren’t natively supported on Windows. Most Windows admins just run the control node inside Windows Subsystem for Linux (WSL) and install Ansible there with pip. Fighting Windows path quirks isn’t worth the effort. Terraform, by contrast, ships as a single Windows binary. Download it from the official releases page, unzip it, and add the folder to your PATH environment variable. Done.

macOS

DSC v3 doesn’t ship an official Homebrew formula. Download the latest release archive from the PowerShell/DSC GitHub repository, expand it, and add the folder to your PATH:

dsc resource list

Type Kind Version
——————————————————
Microsoft.DSC/Group Group 3.0.0
Microsoft.DSC.Transitional/RunCommandOnSet Resource 3.0.0

Terminal output of `dsc resource list` on macOS after manually installing the DSC v3 binary, showing available resources
macOS System Settings General > About page showing macOS version, used to confirm compatibility for the manual DSC v3 binary install

Install Ansible directly with Python’s package manager. Make sure your Python version falls in the supported 3.11–3.13 range:

python3 -m pip install --user ansible

Terraform on macOS follows the same pattern as Windows: download the binary from HashiCorp’s releases page, unzip, and place it on your PATH.

Terminal output of `terraform init` followed by `terraform plan` in a sample working directory, showing provider initialization and a plan listing one resource to be created

Web (Documentation and Release Verification)

Before installing any of these tools, check the official release and documentation pages directly. Version numbers and support windows shift often. Trusting a bookmarked blog post, this one included eventually, is a bad idea.

Microsoft Learn DSC v3 Get Started guide showing the installation and quickstart steps

Check the Terraform end-of-life tracker, Ansible end-of-life tracker, and ansible-core end-of-life tracker before standardizing on a specific version for production use. Ansible in particular has a fast release cadence with strict Python version pairing per release.

Real-World Tool Combinations

Terraform + Ansible: The Common Pairing

The most common pattern in production is dead simple: Terraform provisions the VM, Ansible configures it. A typical pipeline looks like this:

  • terraform apply creates the VM, attaches it to a network, and outputs its IP address.
  • That IP address feeds into a dynamic Ansible inventory (or is written to a static inventory file as part of the pipeline).
  • ansible-playbook runs against the new host to install packages, apply security baselines, and deploy application code.

This works because the two tools have zero overlap in responsibility. Terraform never touches in-guest configuration. Ansible never tries to create the VM it’s connecting to. It also means you can swap either tool independently. Move from AWS to a local Proxmox cluster, and you only rewrite the Terraform provider blocks, not the Ansible playbooks.

DSC-Only: The Pure-Windows Shop

If your entire environment is Windows Server (and maybe a slice of Azure), adding Terraform and Ansible on top of DSC adds operational overhead without a clear payoff. A DSC-only approach, pull-mode configurations synced from Azure Automation DSC or an on-prem pull server, can handle the full configuration management job natively. It uses tools your Windows admins already understand: PowerShell, Group Policy-adjacent concepts, MOF-based desired state. If you’re spinning up new Azure VMs from scratch, you’ll still need something else for provisioning: Terraform, ARM, or Bicep. For configuration alone, though, DSC doesn’t need backup.

Mixed Fleet Without Cloud Provisioning

Some homelabs and on-prem shops never provision new VMs programmatically. They clone templates manually or restore Proxmox/VMware snapshots, but still want consistent configuration across a dozen mixed Linux and Windows boxes. Here, Terraform adds no value; there’s nothing to provision. The choice comes down to Ansible vs. DSC. Ansible wins because it manages both OS types from a single control node. DSC v3’s cross-platform support exists, but its resource ecosystem and community tooling are still overwhelmingly Windows-focused.

Decision Matrix

Environment TypeRecommended Tool(s)Why
Multi-cloud or hybrid cloud, provisioning new resources regularlyTerraform + AnsibleTerraform tracks state across providers; Ansible configures the resulting hosts
Pure-Windows Server or Azure-only shopPowerShell DSC (v3 or classic)Native Windows integration, no need for a separate orchestration layer
Mixed Windows/Linux fleet, no cloud provisioningAnsibleSingle control node manages both OS types via SSH/WinRM
Homelab with occasional VM creation (Proxmox, libvirt)Terraform (provisioning) + Ansible (configuration)Clean separation even at small scale; state file prevents orphaned VMs
Enterprise with existing Azure Automation DSC investmentPowerShell DSC, optionally + Terraform for Azure resource provisioningUses existing DSC pull infrastructure instead of replacing it
CI/CD pipeline building ephemeral test environmentsTerraform + AnsibleTerraform spins up/tears down disposable infra; Ansible configures it per test run

Use Case Recommendations

Choose Terraform if:

  • You regularly create, resize, or destroy cloud or virtualization resources
  • You need a reliable record of what infrastructure currently exists (state tracking)
  • You’re working across multiple cloud providers or a hybrid cloud/on-prem setup
  • You want to review infrastructure changes before they happen (terraform plan)

Choose Ansible if:

  • Your fleet includes both Windows and Linux hosts that need consistent configuration
  • You want to avoid installing permanent agents on managed nodes
  • Your team is more comfortable with YAML than a dedicated DSL
  • You need orchestration (multi-step, ordered tasks across many hosts) in addition to configuration

Choose PowerShell DSC if:

  • Your environment is pure Windows Server or heavily Azure-centric
  • You want native integration with Windows Features, registry keys, and local users without a translation layer
  • You already have (or want) Azure Automation DSC as a central pull server
  • You don’t have a provisioning need that would justify introducing Terraform

Terraform

Pros
  • State file gives an accurate, queryable record of what exists
  • Massive provider ecosystem (AWS, Azure, GCP, Proxmox, Kubernetes, and more) using consistent HCL syntax
  • terraform plan lets you review changes before they happen
  • Free CLI with no artificial resource limits when self-hosted
Cons
  • Not designed for in-guest OS configuration; provisioners exist but are explicitly discouraged for that purpose
  • State file management (locking, remote backends) adds operational complexity for teams
  • HCL has a learning curve distinct from general-purpose scripting languages

Ansible

Pros
  • Truly agentless: nothing persistent to install or patch on managed nodes
  • Handles Windows (WinRM) and Linux (SSH) from the same control node and playbook structure
  • YAML playbooks are approachable for admins without a programming background
  • Huge community module ecosystem for everything from package management to cloud APIs
Cons
  • No built-in state file; drift detection requires re-running playbooks, not diffing against a record
  • WinRM setup and certificate/auth configuration on Windows can be fiddly compared to SSH
  • Strict Python version requirements on the control node can complicate air-gapped or legacy environments

PowerShell DSC

Pros
  • Native Windows integration; no translation layer for registry, services, or Windows Features
  • Push/pull model supports both ad hoc changes and scaled, centrally-managed drift correction
  • DSC v3 is now genuinely cross-platform (Windows, Linux, macOS)
  • Free, with no separate license required
Cons
  • PSDesiredStateConfiguration module no longer ships with PowerShell 7.2+, adding an install step
  • Cross-platform resource ecosystem for DSC v3 is still much smaller than Ansible’s module library
  • Less community documentation and fewer third-party integrations outside the Microsoft ecosystem
  • No native provisioning capability; needs pairing with Terraform, ARM, or Bicep for cloud resource creation

Final Verdict

There’s no single winner here. These tools don’t compete for the same job. Force a ranking by versatility across real-world environments, though, and Terraform plus Ansible cover the most ground. Terraform handles anything created or destroyed. Ansible handles anything configured, regardless of OS. DSC still wins outright when your fleet is Windows-only or Azure-centric and you don’t want a second tool just to configure servers you already own.

Overall Winner: Terraform + Ansible (for mixed environments); PowerShell DSC (for pure-Windows/Azure shops)

Managing anything beyond a single-OS Windows fleet? Standardize on Terraform for provisioning and Ansible for configuration. It’s the combination most teams converge on. It cleanly separates “does this exist” from “is this configured correctly.” If your entire footprint is Windows Server and Azure, DSC alone is a lower-overhead choice. And a perfectly reasonable one.

Score Summary

CategoryTerraformAnsiblePowerShell DSC
Provisioning capability9/103/102/10
Configuration management3/109/108/10
Cross-platform reach8/109/106/10
Learning curve (ease)6/108/106/10
Windows-native integration4/106/1010/10
Ecosystem/community9/109/105/10
Overall (as a category leader)8/108/107/10

Frequently Asked Questions

What is the actual difference between infrastructure provisioning and configuration management?

Provisioning creates, resizes, or destroys the infrastructure resource itself: the VM, the virtual network, the DNS record, the storage bucket. Before provisioning, the resource doesn’t exist. After provisioning, it exists with the specs you declared: CPU, RAM, disk, network attachment. Provisioning is Terraform’s whole reason for existing. It keeps a state file that records exactly what it created. On every terraform plan, it compares your declared configuration against reality. Then it tells you exactly what would change.

Configuration management means changing the software and settings inside a resource that already exists. Installing packages, writing config files, creating local users, setting registry keys, enforcing a security baseline: none of that requires creating or destroying the VM. Ansible and PowerShell DSC both live here. Neither one tracks “did I create this VM.” That’s not their job. They assume the target host is already reachable. Their focus is reconciling its internal state.

Winner: neither. This is a category, not a competition. The mistake to avoid: making Terraform manage in-guest configuration, or making Ansible/DSC provision cloud resources from scratch. Terraform’s provisioner blocks technically let you do the former. HashiCorp’s own docs discourage it, for good reason. Neither Ansible nor DSC tracks state the way a dedicated provisioning tool does. Use them to spin up VMs from nothing, and you give up the one thing Terraform is actually built for.

Why do Terraform and Ansible get used together so often?

Because their responsibilities don’t overlap. Terraform’s state file has no concept of in-guest configuration. Ansible has no concept of “did I create this resource.” Combining them lets each tool do the job it was designed for. You can also swap out one without rewriting the other.

Is DSC still relevant now that it has a cross-platform v3 engine, or is Ansible always the better cross-platform choice?

DSC v3’s cross-platform engine is real, but its resource ecosystem outside Windows is still thin compared to Ansible’s module library. For genuinely mixed Windows/Linux fleets, Ansible remains the more practical cross-platform choice. DSC v3 makes more sense for teams already invested in DSC tooling. It lets you extend a few configurations to Linux nodes without adopting a whole new tool.

How do these tools differ in terms of agent requirements (agentless vs. push/pull vs. state file)?

Ansible is agentless. It connects over SSH or WinRM per run and leaves nothing behind. PowerShell DSC uses a push/pull model. Configurations are either pushed directly or pulled periodically by a lightweight Local Configuration Manager (LCM) client. Terraform uses neither an agent nor a client on the target. Instead, it keeps a state file that records what it created. It compares that record against reality on every run.

Wrapping Up

Once you separate provisioning from configuration management in your head, the choice mostly makes itself. Terraform builds the box. Ansible or DSC fills it in. For mixed Windows/Linux shops, Terraform + Ansible is the pairing you’ll see most often in the wild. It splits responsibility cleanly and lets you swap either tool without touching the other.

If you’re Windows-only or Azure-centric, don’t bolt on extra tooling just because it’s popular elsewhere. DSC alone, especially with the newer v3 engine, still gets the job done. It’s one less thing to patch and maintain.

StepActionApplies To
1Identify whether you need to create infrastructure or just configure existing hostsAll environments
2Use Terraform for provisioning, tracking state in a remote backend for team useMulti-cloud, hybrid, homelab with VM creation
3Use Ansible for cross-platform configuration via SSH/WinRMMixed Windows/Linux fleets
4Use PowerShell DSC (push or pull mode) for Windows-native configurationPure-Windows or Azure-only shops
5Avoid letting two tools manage the same property; partition responsibility clearlyAll environments

This comparison is based on documentation and release information as of August 2026. Versions, pricing, and support windows may have changed since publication. Check the official sources linked throughout this article before standardizing on a specific tool version.