| `string` | `""` | no |
+| [aap\_org\_name](#input\_aap\_org\_name) | Name of the Ansible Automation Platform (AAP) organization. | `string` | `"Default"` | no |
+| [boot\_image\_url](#input\_boot\_image\_url) | URL for the base QCOW2 image used as the boot disk. | `string` | `"https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2"` | no |
+| [bridge\_name](#input\_bridge\_name) | Name of the network bridge for the second network interface. | `string` | `"nm-bridge"` | no |
+| [cloudinit\_meta\_data\_template](#input\_cloudinit\_meta\_data\_template) | The template content for cloud-init meta-data configuration. | `string` | n/a | yes |
+| [cloudinit\_meta\_data\_vars](#input\_cloudinit\_meta\_data\_vars) | Variable map for the cloud-init meta-data template. | `map(string)` | n/a | yes |
+| [cloudinit\_network\_config\_template](#input\_cloudinit\_network\_config\_template) | The template content for cloud-init network configuration. | `string` | n/a | yes |
+| [cloudinit\_network\_config\_vars](#input\_cloudinit\_network\_config\_vars) | Variable map for the cloud-init network configuration template. | `map(string)` | n/a | yes |
+| [cloudinit\_user\_data\_template](#input\_cloudinit\_user\_data\_template) | The template content for cloud-init user-data configuration. | `string` | n/a | yes |
+| [cloudinit\_user\_data\_vars](#input\_cloudinit\_user\_data\_vars) | Variable map for the cloud-init user-data template. Set to {} if not used. | `map(string)` | n/a | yes |
+| [description](#input\_description) | Description for the libvirt domain (virtual machine). | `string` | `""` | no |
+| [enable\_aap](#input\_enable\_aap) | Whether to provision Ansible Automation Platform (AAP) resources for this domain. | `bool` | `false` | no |
+| [extra\_volumes](#input\_extra\_volumes) | List of additional volumes to attach to the domain. Each object should contain:
- name: Name of the volume.
- size: Size of the volume in bytes.
Example:
[
{
name = "runner-var-lib-docker.qcow2"
size = 107374182400
}
] | list(object({
name = string
size = number
})) | `[]` | no |
+| [memory](#input\_memory) | Amount of memory (in MB) to assign to the domain. | `number` | `2048` | no |
+| [name](#input\_name) | The name of the libvirt domain (virtual machine) and related resources. | `string` | n/a | yes |
+| [private\_ip\_addr](#input\_private\_ip\_addr) | Private IP address to assign to the VM (used for network config and inventory). | `string` | n/a | yes |
+| [proxyhost](#input\_proxyhost) | Proxy host for SSH connection, used in ansible\_ssh\_common\_args. | `string` | n/a | yes |
+| [storage\_pool](#input\_storage\_pool) | Name of the libvirt storage pool where volumes will be created. | `string` | `"default"` | no |
+| [vcpu](#input\_vcpu) | Number of virtual CPUs to assign to the domain. | `number` | `1` | no |
+
+## Outputs
+
+| Name | Description |
+|------|-------------|
+| [boot\_volume\_id](#output\_boot\_volume\_id) | The ID of the boot volume |
+| [cloudinit\_disk\_id](#output\_cloudinit\_disk\_id) | The ID of the cloud-init disk |
+| [domain\_id](#output\_domain\_id) | The ID of the libvirt domain |
+| [domain\_name](#output\_domain\_name) | The name of the libvirt domain |
+
diff --git a/main.tf b/main.tf
new file mode 100644
index 0000000..e9675fb
--- /dev/null
+++ b/main.tf
@@ -0,0 +1,189 @@
+resource "libvirt_volume" "boot" {
+ name = "${var.name}.qcow2"
+ pool = var.storage_pool
+
+ target = {
+ format = {
+ type = "qcow2"
+ }
+ }
+
+ create = {
+ content = {
+ url = var.boot_image_url
+ }
+ }
+}
+
+resource "libvirt_volume" "extra" {
+ count = length(var.extra_volumes)
+ name = var.extra_volumes[count.index].name
+ pool = var.storage_pool
+ capacity = var.extra_volumes[count.index].size
+}
+
+resource "libvirt_cloudinit_disk" "commoninit" {
+ name = "${var.name}_commoninit"
+ meta_data = templatefile(var.cloudinit_meta_data_template, var.cloudinit_meta_data_vars)
+ user_data = templatefile(var.cloudinit_user_data_template, var.cloudinit_user_data_vars)
+ network_config = templatefile(var.cloudinit_network_config_template, var.cloudinit_network_config_vars)
+}
+
+resource "libvirt_volume" "cloudinit" {
+ name = "${var.name}_cloudinit.iso"
+ pool = var.storage_pool
+
+ create = {
+ content = {
+ url = libvirt_cloudinit_disk.commoninit.path
+ }
+ }
+}
+
+resource "libvirt_domain" "vm" {
+ name = var.name
+ type = "kvm"
+ description = var.description
+ vcpu = var.vcpu
+ memory = var.memory
+ memory_unit = "MiB"
+ running = true
+
+ cpu = {
+ mode = "host-passthrough"
+ }
+
+ os = {
+ type = "hvm"
+ type_arch = "x86_64"
+ boot_devices = [{ dev = "hd" }]
+ }
+
+ devices = {
+ disks = concat(
+ [
+ {
+ source = {
+ volume = {
+ pool = var.storage_pool
+ volume = libvirt_volume.boot.name
+ }
+ }
+ target = {
+ dev = "vda"
+ bus = "virtio"
+ }
+ },
+ {
+ source = {
+ volume = {
+ pool = var.storage_pool
+ volume = libvirt_volume.cloudinit.name
+ }
+ }
+ target = {
+ dev = "vdb"
+ bus = "virtio"
+ }
+ device = "cdrom"
+ }
+ ],
+ [
+ for idx, vol in libvirt_volume.extra : {
+ source = {
+ volume = {
+ pool = var.storage_pool
+ volume = vol.name
+ }
+ }
+ target = {
+ dev = "vd${substr("cdefghij", idx, 1)}"
+ bus = "virtio"
+ }
+ }
+ ]
+ )
+
+ interfaces = [
+ {
+ model = {
+ type = "virtio"
+ }
+ source = {
+ network = {
+ network = "default"
+ }
+ }
+ },
+ {
+ model = {
+ type = "virtio"
+ }
+ source = {
+ bridge = {
+ bridge = var.bridge_name
+ }
+ }
+ }
+ ]
+
+ graphics = [
+ {
+ vnc = {
+ auto_port = true
+ listen = "0.0.0.0"
+ listeners = [
+ {
+ address = {
+ address = "0.0.0.0"
+ }
+ }
+ ]
+ }
+ }
+ ]
+ }
+
+ lifecycle {
+ ignore_changes = [devices]
+ }
+}
+
+data "aap_organization" "org" {
+ count = var.enable_aap ? 1 : 0
+ name = var.aap_org_name
+ depends_on = [libvirt_domain.vm]
+}
+
+data "aap_inventory" "inventory" {
+ count = var.enable_aap ? 1 : 0
+ name = var.aap_inventory_name
+ organization_name = data.aap_organization.org[0].name
+ depends_on = [data.aap_organization.org]
+}
+
+resource "aap_host" "host" {
+ count = var.enable_aap ? 1 : 0
+ name = var.name
+ description = var.description
+ inventory_id = data.aap_inventory.inventory[0].id
+ enabled = true
+ variables = jsonencode({
+ ansible_host = var.private_ip_addr
+ ansible_ssh_common_args = "-o ProxyCommand=\"ssh -o StrictHostKeyChecking=no -W %h:%p ${var.proxyhost}\""
+ })
+ depends_on = [data.aap_inventory.inventory]
+}
+
+data "aap_job_template" "job_template" {
+ count = var.enable_aap ? 1 : 0
+ name = var.aap_job_template_name != "" ? var.aap_job_template_name : "configure_${var.name}"
+ organization_name = data.aap_organization.org[0].name
+ depends_on = [data.aap_organization.org]
+}
+
+resource "aap_job" "job" {
+ count = var.enable_aap ? 1 : 0
+ job_template_id = data.aap_job_template.job_template[0].id
+ depends_on = [aap_host.host, data.aap_job_template.job_template]
+}
diff --git a/outputs.tf b/outputs.tf
new file mode 100644
index 0000000..a3ef12e
--- /dev/null
+++ b/outputs.tf
@@ -0,0 +1,19 @@
+output "domain_id" {
+ description = "The ID of the libvirt domain"
+ value = libvirt_domain.vm.id
+}
+
+output "domain_name" {
+ description = "The name of the libvirt domain"
+ value = libvirt_domain.vm.name
+}
+
+output "boot_volume_id" {
+ description = "The ID of the boot volume"
+ value = libvirt_volume.boot.id
+}
+
+output "cloudinit_disk_id" {
+ description = "The ID of the cloud-init disk"
+ value = libvirt_cloudinit_disk.commoninit.id
+}
diff --git a/providers.tf b/providers.tf
new file mode 100644
index 0000000..64ae94e
--- /dev/null
+++ b/providers.tf
@@ -0,0 +1,13 @@
+terraform {
+ required_version = ">= 1.3"
+
+ required_providers {
+ libvirt = {
+ source = "dmacvicar/libvirt"
+ version = ">= 0.9.0"
+ }
+ aap = {
+ source = "registry.terraform.io/ansible/aap"
+ }
+ }
+}
diff --git a/vars.tf b/vars.tf
new file mode 100644
index 0000000..44e6bd7
--- /dev/null
+++ b/vars.tf
@@ -0,0 +1,124 @@
+variable "name" {
+ description = "The name of the libvirt domain (virtual machine) and related resources."
+ type = string
+}
+
+variable "description" {
+ description = "Description for the libvirt domain (virtual machine)."
+ type = string
+ default = ""
+}
+
+variable "vcpu" {
+ description = "Number of virtual CPUs to assign to the domain."
+ type = number
+ default = 1
+}
+
+variable "memory" {
+ description = "Amount of memory (in MB) to assign to the domain."
+ type = number
+ default = 2048
+}
+
+variable "storage_pool" {
+ description = "Name of the libvirt storage pool where volumes will be created."
+ type = string
+ default = "default"
+}
+
+variable "boot_image_url" {
+ description = "URL for the base QCOW2 image used as the boot disk."
+ type = string
+ default = "https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2"
+}
+
+variable "extra_volumes" {
+ description = <