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
| Tool | Description | Best For | Price |
|---|---|---|---|
| Terraform | Declarative provisioning tool that creates, updates, and destroys infrastructure resources using a tracked state file | Multi-cloud/hybrid VM, network, and storage provisioning | Free CLI (BSL 1.1); HCP Terraform from $0.10/resource/month |
| Ansible | Agentless configuration management and orchestration tool using SSH (Linux) and WinRM (Windows) | Configuring existing mixed Windows/Linux fleets at scale | Free (open source, ansible-core) |
| PowerShell DSC | Microsoft’s native declarative configuration engine using MOF files and a push/pull model; now cross-platform via DSC v3 | Pure-Windows or Azure-centric configuration management | Free (Microsoft tool, no license fee) |
Feature Comparison
At a Glance
| Feature | Terraform | Ansible | PowerShell DSC |
|---|---|---|---|
| Primary job | Provisioning | Configuration management | Configuration management |
| Agent required | No (state file instead) | No (agentless) | No (push) / lightweight pull client (pull mode) |
| Connection method | Provider APIs (cloud/HTTP) | SSH (Linux), WinRM (Windows) | Local execution or pull server sync |
| Config language | HCL (declarative) | YAML playbooks | PowerShell-based DSL compiled to MOF |
| State tracking | Yes terraform.tfstate | No built-in state file | Partial; current state queried live per resource |
| Cross-platform | Yes (via providers) | Yes (control node: Linux/macOS/WSL; managed nodes: Linux + Windows) | Yes as of DSC v3 (Windows, Linux, macOS) |
| Idempotency | Yes | Yes (per-module) | Yes |
| Native Windows integration | Via provider only | Via WinRM | Native |
| Learning curve | Moderate (HCL + state concepts) | Low (YAML) | Moderate (PowerShell + MOF concepts) |
| Best paired with | Ansible or DSC | Terraform | Standalone 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) ordsc 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.


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=5986And 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: presentRun it against your inventory:
ansible-playbook -i inventory.ini site.ymlPLAY [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.

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 initInitializing the backend…
Initializing provider plugins…
– Finding dmacvicar/libvirt versions matching “~> 0.8″…
– Installing dmacvicar/libvirt v0.8.1…
Terraform has been successfully initialized!
terraform planTerraform 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.

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.
| Aspect | Terraform | Ansible | PowerShell DSC |
|---|---|---|---|
| Agent on target | None (talks to cloud/hypervisor API) | None (SSH/WinRM per run) | None in push mode; lightweight LCM/pull client in pull mode |
| Required open ports | Provider API endpoints (usually HTTPS/443) | TCP 22 (SSH) / TCP 5985|5986 (WinRM) | Local execution, or pull server endpoint (custom port) |
| Central control point | Terraform state (local or remote backend) | Ansible control node + inventory | Pull server (optional) or ad hoc push |
| Drift detection | terraform plan diff | Re-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.
| Tier | Price | Notes |
|---|---|---|
| PowerShell DSC (v3 engine + PSDesiredStateConfiguration module) | Free | No license fee documented; standard Microsoft support terms apply |
| Ansible / ansible-core | Free (open source) | Commercial Red Hat Ansible Automation Platform exists separately; pricing not published in official docs referenced here |
| Terraform CLI | Free (BSL 1.1) | Unlimited resources/users when self-hosted or run via your own CI |
| HCP Terraform, Essentials | $0.10 per managed resource/month | Entry-tier RUM billing |
| HCP Terraform, Standard | $0.47 per managed resource/month | Adds collaboration features |
| HCP Terraform, Premium | $0.99 per managed resource/month | Adds advanced governance features |

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 msstoreName Id Source
————————————————
DesiredStateConfiguration 9NVTPZWRC6KQ msstore
winget install --id 9NVTPZWRC6KQ --source msstoreVerify the install by listing available resources:
dsc resource list


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 listType Kind Version
——————————————————
Microsoft.DSC/Group Group 3.0.0
Microsoft.DSC.Transitional/RunCommandOnSet Resource 3.0.0


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 ansibleTerraform on macOS follows the same pattern as Windows: download the binary from HashiCorp’s releases page, unzip, and place it on your PATH.

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.

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 applycreates 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-playbookruns 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 Type | Recommended Tool(s) | Why |
|---|---|---|
| Multi-cloud or hybrid cloud, provisioning new resources regularly | Terraform + Ansible | Terraform tracks state across providers; Ansible configures the resulting hosts |
| Pure-Windows Server or Azure-only shop | PowerShell DSC (v3 or classic) | Native Windows integration, no need for a separate orchestration layer |
| Mixed Windows/Linux fleet, no cloud provisioning | Ansible | Single 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 investment | PowerShell DSC, optionally + Terraform for Azure resource provisioning | Uses existing DSC pull infrastructure instead of replacing it |
| CI/CD pipeline building ephemeral test environments | Terraform + Ansible | Terraform 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
- 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 planlets you review changes before they happen- Free CLI with no artificial resource limits when self-hosted
- 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
- 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
- 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
- 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
- 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
| Category | Terraform | Ansible | PowerShell DSC |
|---|---|---|---|
| Provisioning capability | 9/10 | 3/10 | 2/10 |
| Configuration management | 3/10 | 9/10 | 8/10 |
| Cross-platform reach | 8/10 | 9/10 | 6/10 |
| Learning curve (ease) | 6/10 | 8/10 | 6/10 |
| Windows-native integration | 4/10 | 6/10 | 10/10 |
| Ecosystem/community | 9/10 | 9/10 | 5/10 |
| Overall (as a category leader) | 8/10 | 8/10 | 7/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.
| Step | Action | Applies To |
|---|---|---|
| 1 | Identify whether you need to create infrastructure or just configure existing hosts | All environments |
| 2 | Use Terraform for provisioning, tracking state in a remote backend for team use | Multi-cloud, hybrid, homelab with VM creation |
| 3 | Use Ansible for cross-platform configuration via SSH/WinRM | Mixed Windows/Linux fleets |
| 4 | Use PowerShell DSC (push or pull mode) for Windows-native configuration | Pure-Windows or Azure-only shops |
| 5 | Avoid letting two tools manage the same property; partition responsibility clearly | All 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.