Infrastructure as Code with Ansible and Terraform

Ansible, Terraform, and the IaC Decision for Hyper-V

Infrastructure as Code with Ansible and Terraform

Post 19 built an automation practice around PowerShell , modules, DSC v3, CI/CD pipelines. For many organizations, that’s enough. PowerShell is native, it’s free, it covers 100% of Hyper-V functionality, and your Windows team already knows it.

But some organizations have standardized on Ansible for configuration management across Linux and Windows. Others use Terraform for all infrastructure provisioning. And some want both , Terraform for creating resources, Ansible for configuring them. The question isn’t “which tool is best” , it’s “which tool fits your team, your existing investments, and your Hyper-V use case.”

This twentieth post in the Hyper-V Renaissance series provides an honest assessment of three IaC approaches for Hyper-V: Ansible alone, Terraform with PowerShell, and Terraform with Ansible. We’ll cover what each tool can and cannot do, how mature the Hyper-V integration is, how to build pipelines for each, and when to choose which.

Repository: The full Post 20 companion deliverables are in the Post 20 folder, including the README, the execution walkthrough, Ansible examples, Terraform examples, PowerShell helpers, and pipeline samples.

Companion Deliverables

If you want to use the code from this post directly, start with these files in the toolkit:


The Three Approaches , At a Glance

IaC Approaches for Hyper-V

Before diving into each tool, understand what they do differently:

ToolWhat It DoesHow It ThinksState Management
AnsibleConfiguration management , makes systems look a certain wayProcedural with idempotent modules , “ensure this is true”Stateless , checks current state on every run
TerraformInfrastructure provisioning , creates and destroys thingsDeclarative with state file , “these resources should exist”Stateful , tracks what it created in a state file
PowerShellBoth , native access to everythingImperative (scripts) or declarative (DSC v3)Depends on approach , scripts are stateless, DSC is stateful

The fundamental difference: Ansible asks “is the system configured correctly?” and fixes what’s wrong. Terraform asks “do these resources exist?” and creates or destroys to match. PowerShell does whatever you tell it to do , the structure depends on how you use it (Post 19 patterns).

In practice, Terraform excels at provisioning (creating VMs, switches, networks) and Ansible excels at configuring (installing roles, setting up services, applying security baselines). PowerShell does both natively but requires you to build the patterns yourself. Many organizations use Terraform + Ansible together, or Terraform + PowerShell/DSC , Terraform creates the infrastructure, and Ansible or DSC configures it.


Approach 1: Ansible for Hyper-V

Ansible is the most mature IaC tool for Windows/Hyper-V management after PowerShell itself. It connects to Windows hosts via WinRM (or SSH on WS2025), runs modules that check and apply configuration, and requires no agent installation on the target hosts.

How Ansible Connects to Windows

Ansible runs on a Linux or macOS control node (or WSL on Windows) and reaches Windows targets via one of three connection methods:

MethodTransportAuthenticationBest For
WinRM (winrm plugin)HTTPS on port 5986Kerberos (recommended), NTLM, Certificate, CredSSPDomain-joined hosts, most environments
PSRP (psrp plugin)PowerShell Remoting Protocol over HTTPSSame as WinRMBetter performance than WinRM for large payloads
SSH (ssh plugin)OpenSSH on port 22SSH keysWS2025 (OpenSSH built-in), non-domain hosts

For production Hyper-V environments, use WinRM with Kerberos authentication. Kerberos provides mutual authentication, encryption, and credential delegation (needed for operations that access network resources from the target host). This is the same reason Post 5 recommended Kerberos for live migration.

SSH support for Windows became officially supported in Ansible 2.18. It’s simpler to configure (no WinRM setup) but requires key-based authentication only , Kerberos TGT forwarding doesn’t work over SSH from Ansible. Use SSH for environments where WinRM setup is impractical.

The Ansible Collections for Hyper-V

Ansible organizes modules into collections. Three collections matter for Hyper-V:

ansible.windows (v3.5.0) , The core Windows collection with 71 modules. This is your foundation:

ModuleWhat It DoesHyper-V Use
win_featureInstall/remove Windows featuresHyper-V role, Failover Clustering, MPIO, DCB
win_serviceManage Windows servicesMSiSCSI, vmms, WinDefend
win_firewall_ruleManage firewall rulesHyper-V management rules, WinRM
win_powershellRun arbitrary PowerShellAnything not covered by a module
win_regeditManage registry keysCredential Guard, HVCI, MPIO settings
win_rebootReboot and wait for reconnectionPost-feature-install reboots
win_updatesInstall Windows UpdatesPatch management
win_dscInvoke DSC v2 resourcesLegacy DSC configurations
dsc3Invoke DSC v3 resources (new in v3.4.0)Modern DSC configurations under PS7

microsoft.hyperv , Red Hat Ansible Certified Content for Hyper-V management. This is the official path for VM lifecycle:

ModuleWhat It Does
hv_vmCreate, modify, delete VMs
hv_vm_stateStart, stop, save, pause VMs
hv_checkpointCreate and manage VM checkpoints
hv_processor / hv_memoryConfigure VM CPU and memory
hv_hard_disk / hv_dvd_driveManage VM storage devices
hv_network_adapterConfigure VM network adapters
hv_scsi_controllerManage SCSI controllers
hv_vhdCreate and manage virtual hard disks
hv_host / hv_host_infoConfigure and query Hyper-V host settings

Install with: ansible-galaxy collection install microsoft.hyperv

What’s not covered (yet): Virtual switch management, clustering operations (live migration, CSV, quorum), Hyper-V Replica, and SCVMM integration don’t have dedicated modules. For these, use win_powershell to run PowerShell commands , Ansible orchestrates the workflow, PowerShell handles the Hyper-V specifics.

community.windows , Extended Windows modules for scheduled tasks, disk management, event log, and more. In maintenance mode (bugfixes only, no new modules).

The dsc3 Module , Bridging Ansible and DSC v3

This is significant. Added in ansible.windows v3.4.0 (March 2025), the dsc3 module lets Ansible invoke DSC v3 resources directly. This means you can:

  1. Use Ansible for orchestration (ordering, inventory, credential management, rolling execution)
  2. Use DSC v3 for declarative configuration (roles, services, registry, firewall)
  3. Combine both in a single playbook

The dsc3 module runs under PowerShell 7, unlike the older win_dsc module which requires PowerShell 5.1. This aligns with the Post 19 approach of targeting PS7 for all automation.

Ansible Inventory for Hyper-V

Ansible inventory defines your target hosts. For Hyper-V clusters, structure by role:

# inventory/hosts.ini

[hyperv_hosts]
hv-host-prod-sea01-001 ansible_host=10.10.10.11
hv-host-prod-sea01-002 ansible_host=10.10.10.12

[hyperv_hosts:vars]
ansible_connection=winrm
ansible_winrm_transport=kerberos
ansible_winrm_server_cert_validation=ignore
ansible_port=5986

[hyperv_clusters]
cl-hvhost-prod-sea01 ansible_host=10.10.10.20

[all:vars]
ansible_winrm_kerberos_delegation=true

Group variables (in group_vars/hyperv_hosts.yml) define the desired state , VLANs, features, services , the same data that Post 19’s variables file contains, but in Ansible’s YAML format.

What an Ansible Playbook Looks Like

A playbook for configuring a Hyper-V host combines multiple modules into an ordered workflow. Here’s the conceptual structure (full playbooks are in the companion repository):

# playbooks/configure-hyperv-host.yml
---
- name: Configure Hyper-V cluster node
  hosts: hyperv_hosts
  gather_facts: true

  tasks:
    - name: Install required Windows features
      ansible.windows.win_feature:
        name: "{{ item }}"
        state: present
      loop:
        - Hyper-V
        - Failover-Clustering
        - Multipath-IO
        - Data-Center-Bridging
      register: features_result

    - name: Reboot if features require it
      ansible.windows.win_reboot:
      when: features_result.results | selectattr('reboot_required', 'equalto', true) | list | length > 0

    - name: Ensure iSCSI Initiator service is running
      ansible.windows.win_service:
        name: MSiSCSI
        start_mode: auto
        state: started

    - name: Apply security baseline via DSC v3
      ansible.windows.dsc3:
        resource_name: Microsoft.Windows/Service
        properties:
          serviceName: Browser
          startupType: Disabled
          state: Stopped

    - name: Configure SET virtual switch (PowerShell)
      ansible.windows.win_powershell:
        script: |
          if (-not (Get-VMSwitch -Name '{{ switch_name }}' -EA SilentlyContinue)) {
              New-VMSwitch -Name '{{ switch_name }}' -NetAdapterName {{ nic_names | join(',') }} `
                  -EnableEmbeddedTeaming $true -AllowManagementOS $true
          }

Notice the pattern: Ansible uses native modules where they exist (win_feature, win_service, dsc3) and drops to win_powershell for Hyper-V-specific operations that don’t have dedicated modules. This is pragmatic , use the right tool for each task.

When Ansible Is the Right Choice for Hyper-V

  • Your team already uses Ansible for Linux or other Windows management , extend it to Hyper-V rather than maintaining two automation platforms
  • Mixed-OS environments where you manage Linux, Windows, and network devices from a single tool
  • Configuration management focus , you need to enforce and remediate configuration drift across many hosts
  • No state file complexity , Ansible doesn’t track state between runs; it checks and fixes on every execution
  • Red Hat ecosystem , your organization has Ansible Automation Platform or AWX deployed

When Ansible Is Not the Best Fit

  • Pure Windows/Hyper-V shop , PowerShell alone (Post 19) provides native access without the overhead of maintaining an Ansible control node and learning YAML syntax
  • VM provisioning at scale , Ansible can create VMs but doesn’t track them in a state file. Terraform is better for declarative “these VMs should exist” workflows
  • Cluster-level operations , live migration, CSV management, and quorum configuration don’t have Ansible modules; you’ll be writing win_powershell tasks for most cluster work

Approach 2: Terraform + PowerShell

Terraform is a declarative infrastructure provisioning tool. You define what resources should exist (VMs, switches, disks), Terraform creates them, and it tracks what it created in a state file. If you remove a VM from the configuration and run terraform apply, Terraform destroys it. This create-track-destroy lifecycle is Terraform’s superpower , and its complexity.

The Hyper-V Provider , Honest Assessment

The community provider taliesins/hyperv (v1.2.1, February 2024) connects to Hyper-V hosts via WinRM and manages basic resources:

ResourceWhat It DoesMaturity
hyperv_machine_instanceCreate/modify/delete VMsFunctional , covers basic VM settings
hyperv_network_switchCreate/modify/delete virtual switchesFunctional
hyperv_vhdCreate/modify/delete VHDsFunctional
hyperv_iso_imageManage ISO imagesBasic

What’s missing: Cluster operations, CSV management, live migration, Hyper-V Replica, GPU-P, vTPM, advanced NUMA configuration, and SCVMM integration. The provider covers VM provisioning basics but not the full Hyper-V feature set.

The honest assessment: This is a community project with 259 GitHub stars and no corporate backing. Recent releases are dependency updates, not feature additions. It’s functional for basic VM lifecycle (create, modify, destroy) but it is not a production-grade, enterprise-supported provider. Test thoroughly in your environment before relying on it.

For organizations also running Azure Local (covered in Post 18), the azurerm provider offers an enterprise-grade Terraform experience via Arc. But this series focuses on standalone Hyper-V , and for that, the community provider plus PowerShell is the practical path.

The Pattern: Terraform Provisions, PowerShell/DSC Configures

Because the Hyper-V provider has gaps, the practical pattern is:

  1. Terraform creates the VM (CPU, memory, disk, network adapter) via the hyperv provider
  2. PowerShell/DSC v3 configures the guest OS (join domain, install roles, apply security baseline) via remote-exec provisioner
# Conceptual example ,  full modules in companion repository

resource "hyperv_machine_instance" "web_server" {
  name       = "vm-web-prod-sea01-001"
  generation = 2

  processor { count = 4 }
  memory {
    startup = 4096
    dynamic_memory { enabled = true; minimum = 2048; maximum = 8192 }
  }

  hard_disk { path = "C:\\ClusterStorage\\Volume1\\VHDs\\vm-web-prod-sea01-001.vhdx" }
  network_adapter { switch_name = "vsw-set-prod" }

  # After VM is created, configure it with DSC v3
  provisioner "remote-exec" {
    connection {
      type     = "winrm"
      host     = self.network_adapter[0].ip_addresses[0]
      user     = var.admin_user
      password = var.admin_password
      https    = true
      insecure = true
    }
    inline = [
      "dsc config set --file C:\\deploy\\web-server.dsc.yaml"
    ]
  }
}

Important caveats about provisioners: HashiCorp considers provisioners a “last resort.” They run once during creation and don’t re-run on subsequent terraform apply. For ongoing configuration management, use Ansible or DSC on a schedule , not Terraform provisioners.

State Management for On-Prem

Terraform tracks every resource it creates in a state file. This file is critical , lose it and Terraform can’t manage the resources anymore.

BackendWhere State LivesTeam UseOn-Prem Friendly
LocalFile on diskSingle operator onlyYes, but dangerous
Azure StorageAzure Blob with lease lockingTeams (concurrent access safe)Needs Azure subscription
S3-compatibleMinIO, Wasabi, etc.TeamsFully on-prem with MinIO
ConsulHashiCorp Consul clusterTeamsFully on-prem
HCP TerraformHashiCorp CloudTeamsSaaS , requires internet

For on-prem Hyper-V environments without Azure, MinIO (S3-compatible object storage) or Consul are the self-hosted options for team state management with locking.

When Terraform + PowerShell Is the Right Choice

  • Your team already uses Terraform for cloud or other infrastructure , extend it to on-prem rather than maintaining separate tooling
  • Declarative VM lifecycle , you want “these 50 VMs should exist” expressed in code, with Terraform handling creates, updates, and destroys
  • State tracking matters , you need to know exactly what Terraform created and destroy it cleanly when decommissioning
  • Combined with Arc-enabled SCVMM , Terraform can provision VMs via the Azure Arc control plane using ARM/Bicep patterns (Post 17)

When Terraform Is Not the Best Fit

  • Configuration management , Terraform provisions resources but doesn’t manage ongoing configuration. You need Ansible or DSC for drift remediation.
  • Advanced Hyper-V features , the community provider doesn’t cover clustering, CSVs, or live migration. PowerShell provides more complete coverage.
  • Cluster operations , the provider doesn’t support clustering, CSVs, or live migration. PowerShell is required.
  • Small environments , Terraform’s state management overhead isn’t justified for 2-5 hosts and a handful of VMs.

Approach 3: Terraform + Ansible

This is the “best of both worlds” approach , Terraform handles infrastructure provisioning (VMs, switches, disks) and Ansible handles configuration management (OS setup, roles, security, ongoing drift remediation).

How They Work Together

Terraform creates VM → outputs IP address and hostname →
Ansible dynamic inventory reads Terraform state →
Ansible configures the guest OS (domain join, roles, security, application)

The integration point: Terraform writes its state file. An Ansible dynamic inventory plugin reads that state to discover which hosts exist and how to reach them. No manual inventory updates when VMs are added or removed.

When This Combination Makes Sense

  • Large environments with dozens of VMs that are provisioned and decommissioned regularly
  • Teams with Terraform and Ansible expertise , DevOps teams that already use both
  • Multi-platform environments , Terraform provisions VMs on Hyper-V, Azure, and AWS; Ansible configures all of them consistently
  • Separation of concerns , infrastructure team owns Terraform, application team owns Ansible playbooks

When It’s Overkill

  • Small Hyper-V environments , two tools to maintain instead of one
  • Windows-only shops , PowerShell + DSC (Post 19) covers both provisioning and configuration natively
  • Teams without existing Terraform/Ansible experience , the learning curve for both tools simultaneously is steep

CI/CD Pipelines for IaC

Every IaC approach needs a pipeline. The patterns differ by tool.

Ansible Pipeline Flow

Developer changes playbook/inventory → Pipeline triggers →
  Stage 1: Lint (ansible-lint) → catches syntax and best-practice issues
  Stage 2: Dry run (--check --diff) → shows what would change without applying
  Stage 3: Apply (ansible-playbook) → executes the playbook (manual approval gate)
  Stage 4: Verify → re-run in check mode to confirm desired state

Key detail: Ansible’s --check mode runs every module in “check” (dry-run) mode. Modules report what they would change without actually changing it. The --diff flag shows the exact differences. This is your “plan” step , review it before approving the apply.

The self-hosted runner (same concept as Post 19) needs:

  • Python with pywinrm installed (for WinRM connections)
  • Kerberos client libraries (for Kerberos auth)
  • Network access to Hyper-V hosts on port 5986 (WinRM HTTPS) or 22 (SSH)
  • Ansible collections installed: ansible.windows, microsoft.hyperv

Terraform Pipeline Flow

Developer changes .tf files → Pipeline triggers →
  Stage 1: Validate (terraform validate) → syntax and configuration checks
  Stage 2: Plan (terraform plan) → shows what will be created/modified/destroyed
  Stage 3: Apply (terraform apply) → executes the plan (manual approval gate)
  Stage 4: Post-apply → trigger Ansible or DSC for configuration

Key detail: terraform plan is the most important step. It shows you exactly what will change , “1 to add, 0 to change, 2 to destroy.” Review this carefully. In a pipeline, save the plan output as an artifact and require approval before terraform apply.

State locking: When multiple people or pipelines can run Terraform concurrently, state locking prevents conflicts. The remote backend (Azure Storage, MinIO + DynamoDB, Consul) handles this. Without locking, two concurrent applies can corrupt the state file.

Pipeline Examples

The companion repository includes CI/CD pipeline definitions for both tools across all three platforms:

PipelinePlatformWhat It Runs
ansible-validate.ymlGitHub Actionsansible-lint → check mode → apply (manual)
terraform-plan-apply.ymlGitHub Actionsvalidate → plan (PR comment) → apply (manual)
.gitlab-ci.ymlGitLab CISame stages for both tools
azure-pipelines.ymlAzure DevOpsSame stages with environment approval gates

All pipelines use self-hosted runners with network access to on-prem Hyper-V hosts. Credentials are injected via pipeline secrets , never stored in the repository.


The Decision Framework , Which Approach for Your Environment

IaC Decision Framework

Your SituationRecommended ApproachWhy
Windows-only team, Hyper-V focusPowerShell + DSC v3 (Post 19)Native, free, 100% Hyper-V coverage, no external dependencies
Existing Ansible investmentAnsible with microsoft.hyperv + dsc3Extend existing tooling, single config management platform
Existing Terraform investmentTerraform + PowerShell/DSC v3Terraform provisions, PowerShell configures, familiar workflow
DevOps team, multi-platformTerraform + AnsibleTerraform for provisioning, Ansible for configuration, both mature
Small environment (< 10 hosts)PowerShell aloneSimplest option, no overhead, direct access to everything
Large environment (50+ hosts)Ansible or Terraform + AnsibleScale requires automation platforms with inventory management and RBAC

The Honest Bottom Line

PowerShell + DSC v3 (Post 19) is the most complete option for Hyper-V. It covers 100% of functionality, runs natively, costs nothing, and your Windows team already has the skills. Every other approach is an overlay that adds capability (multi-platform management, declarative provisioning, team collaboration) at the cost of complexity and additional tooling.

Ansible is the strongest third-party option. Mature Windows support, the new microsoft.hyperv collection, the dsc3 module bridging to DSC v3, and production-ready WinRM connectivity make it a solid choice , especially if you already use Ansible.

Terraform is functional but limited for Hyper-V. The community provider handles basic VM lifecycle but lacks cluster operations and advanced features. It’s most valuable when combined with PowerShell/DSC for the configuration layer that the provider doesn’t cover.

Don’t add tools for the sake of adding tools. If PowerShell covers your needs, use PowerShell. Add Ansible when you need cross-platform configuration management. Add Terraform when you need declarative provisioning with state tracking. Each tool should solve a problem you actually have, not a problem you might have someday.


The Hyper-V Renaissance , One Final Step

This is the twentieth post in the series, not the end of the argument. Over twenty posts, we’ve demonstrated that Hyper-V with Windows Server 2025 is a mature, enterprise-ready virtualization platform that deserves serious consideration , whether you’re migrating from VMware, evaluating alternatives to Azure Local, or building new infrastructure.

The series covered:

  • The Case for Change (Posts 1-4) , market context, TCO, capabilities, hardware reuse
  • Foundation Building (Posts 5-8) , hands-on deployment, storage, migration, POC
  • Production Architecture (Posts 9-16) , monitoring, security, management, storage, backup, DR, live migration, scale
  • Strategy & Automation (Posts 17-20) , hybrid Azure, platform comparison, PowerShell automation, IaC

The tools are mature. The costs are lower. The automation is real. PowerShell returned to its throne.

One more post wraps the whole series together and answers the strategic question that sat underneath every build guide, cost model, and comparison table: why Hyper-V so often ends up being the better fit when the VMware bill no longer works and Azure Local still asks you to buy into more platform than you need.

Read that final wrap-up next, then go build something.


Resources

Ansible Documentation

Terraform Documentation


Series Navigation ← Previous: Post 19 , PowerShell Automation Patterns → Next: Post 21 , What Was Under Your Nose All Along