diff --git a/terraform/modules/az-des/main.tf b/terraform/modules/az-des/main.tf
new file mode 100644
index 00000000..bce983b3
--- /dev/null
+++ b/terraform/modules/az-des/main.tf
@@ -0,0 +1,50 @@
+resource "azurerm_key_vault_key" "des" {
+  name         = var.name
+  key_vault_id = var.kv_id
+  key_type     = "RSA-HSM"
+  key_size     = var.key_size
+
+  key_opts = [
+    "decrypt",
+    "encrypt",
+    "sign",
+    "unwrapKey",
+    "verify",
+    "wrapKey",
+  ]
+
+  rotation_policy {
+    automatic {
+      time_before_expiry = var.auto_rotation_time_before_expiry
+    }
+
+    expire_after         = var.rotation_expire_after
+    notify_before_expiry = var.rotation_notify_before_expiry
+  }
+
+  expiration_date = var.expiration_date
+}
+
+resource "azurerm_role_assignment" "des" {
+  scope                = var.kv_id
+  role_definition_name = "Key Vault Crypto Service Encryption User"
+  principal_id         = var.principal_id
+}
+
+resource "azurerm_disk_encryption_set" "des" {
+
+  depends_on = [azurerm_role_assignment.des]
+
+  name                = var.name
+  resource_group_name = var.resource_group_name
+  location            = var.location
+  key_vault_key_id    = azurerm_key_vault_key.des.id
+
+  identity {
+    type         = "UserAssigned"
+    identity_ids = [var.identity_id]
+  }
+
+  tags = var.tags
+}
+
diff --git a/terraform/modules/az-des/ouputs.tf b/terraform/modules/az-des/ouputs.tf
new file mode 100644
index 00000000..c70ba1de
--- /dev/null
+++ b/terraform/modules/az-des/ouputs.tf
@@ -0,0 +1,3 @@
+output "id" {
+  value = azurerm_disk_encryption_set.des.id
+}
diff --git a/terraform/modules/az-des/variables.tf b/terraform/modules/az-des/variables.tf
new file mode 100644
index 00000000..0ca9f332
--- /dev/null
+++ b/terraform/modules/az-des/variables.tf
@@ -0,0 +1,64 @@
+variable "location" {
+  description = "The Azure Region in which all resources in this example should be created."
+  type        = string
+}
+
+variable "resource_group_name" {
+  description = "The name of the resource group in which all resources in this example should be created."
+  type        = string
+}
+
+variable "name" {
+  description = "The name of the disk encryption set."
+  type        = string
+}
+
+variable "kv_id" {
+  description = "The ID of the Key Vault to use for encryption."
+  type        = string
+}
+
+variable "key_size" {
+  description = "The size of the key to use for encryption."
+  type        = number
+  default     = 4096
+}
+
+variable "auto_rotation_time_before_expiry" {
+  description = "The time before expiry to automatically rotate the key."
+  type        = string
+  default     = "P7D"
+}
+
+variable "rotation_expire_after" {
+  description = "The time after which the key expires."
+  type        = string
+  default     = "P30D"
+}
+
+variable "rotation_notify_before_expiry" {
+  description = "The time before expiry to notify that the key is expiring."
+  type        = string
+  default     = "P7D"
+}
+
+variable "expiration_date" {
+  description = "The date after which the key expires."
+  type        = string
+  default     = null
+}
+
+variable "identity_id" {
+  description = "The ID of the identity to assign to the disk encryption set."
+  type        = string
+}
+
+variable "principal_id" {
+  description = "The ID of the principal to assign to the disk encryption set."
+  type        = string
+}
+
+variable "tags" {
+  description = "A mapping of tags to assign to the resource."
+  type        = map(string)
+}
diff --git a/terraform/modules/az-disk-encryption-set/main.tf b/terraform/modules/az-disk-encryption-set/main.tf
deleted file mode 100644
index 0402e0e5..00000000
--- a/terraform/modules/az-disk-encryption-set/main.tf
+++ /dev/null
@@ -1,13 +0,0 @@
-resource "azurerm_disk_encryption_set" "encryption_set" {
-  name                = var.name
-  resource_group_name = var.resource_group_name
-  location            = var.location
-  key_vault_key_id    = var.kv_key_id_cust_managed_key
-
-  identity {
-    type         = "UserAssigned"
-    identity_ids = var.identity_ids
-  }
-
-  tags = var.tags
-}
diff --git a/terraform/modules/az-disk-encryption-set/ouputs.tf b/terraform/modules/az-disk-encryption-set/ouputs.tf
deleted file mode 100644
index f5b84400..00000000
--- a/terraform/modules/az-disk-encryption-set/ouputs.tf
+++ /dev/null
@@ -1,3 +0,0 @@
-output "id" {
-  value = azurerm_disk_encryption_set.encryption_set.id
-}
diff --git a/terraform/modules/az-disk-encryption-set/variables.tf b/terraform/modules/az-disk-encryption-set/variables.tf
deleted file mode 100644
index 80a6c4df..00000000
--- a/terraform/modules/az-disk-encryption-set/variables.tf
+++ /dev/null
@@ -1,34 +0,0 @@
-variable "location" {
-  description = "The Azure Region in which all resources in this example should be created."
-  type        = string
-}
-
-variable "resource_group_name" {
-  description = "The name of the resource group in which all resources in this example should be created."
-  type        = string
-}
-
-variable "name" {
-  description = "The name of the disk encryption set."
-  type        = string
-}
-
-variable "kv_key_id_cust_managed_key" {
-  description = "The ID of the Key Vault Key to be used as the Customer Managed Key for the Storage Account."
-  type        = string
-}
-
-variable "identity_ids" {
-  description = "A list of User Assigned Identity IDs to assign to the Disk Encryption Set."
-  type        = list(string)
-}
-
-variable "expiration_date" {
-  description = "The expiration date of the Key Vault Key."
-  default     = null
-}
-
-variable "tags" {
-  description = "A mapping of tags to assign to the resource."
-  type        = map(string)
-}
diff --git a/terraform/modules/az-kv-cust-key/main.tf b/terraform/modules/az-kv-cust-key/main.tf
deleted file mode 100644
index c5ca2f51..00000000
--- a/terraform/modules/az-kv-cust-key/main.tf
+++ /dev/null
@@ -1,17 +0,0 @@
-resource "azurerm_key_vault_key" "cust_managed_key" {
-  name         = var.name
-  key_vault_id = var.kv_id
-  key_type     = "RSA-HSM"
-  key_size     = 2048
-
-  key_opts = [
-    "decrypt",
-    "encrypt",
-    "sign",
-    "unwrapKey",
-    "verify",
-    "wrapKey",
-  ]
-
-  expiration_date = var.expiration_date
-}
diff --git a/terraform/modules/az-kv-cust-key/outputs.tf b/terraform/modules/az-kv-cust-key/outputs.tf
deleted file mode 100644
index d6a476c6..00000000
--- a/terraform/modules/az-kv-cust-key/outputs.tf
+++ /dev/null
@@ -1,8 +0,0 @@
-output "id" {
-  description = "The ID of the Key Vault Key."
-  value       = azurerm_key_vault_key.cust_managed_key.id
-}
-
-output "name" {
-  value = var.name
-}
diff --git a/terraform/modules/az-kv-cust-key/variables.tf b/terraform/modules/az-kv-cust-key/variables.tf
deleted file mode 100644
index 6493943b..00000000
--- a/terraform/modules/az-kv-cust-key/variables.tf
+++ /dev/null
@@ -1,15 +0,0 @@
-variable "name" {
-  description = "The name of the kv customer managed key"
-  type        = string
-}
-
-variable "kv_id" {
-  description = "The ID of the Key Vault in which the Key Vault Key for the customer managed key be created."
-  type        = string
-}
-
-
-variable "expiration_date" {
-  description = "The expiration date of the Key Vault Key."
-  default     = null
-}
diff --git a/terraform/modules/az-kv-key/main.tf b/terraform/modules/az-kv-key/main.tf
new file mode 100644
index 00000000..0618e964
--- /dev/null
+++ b/terraform/modules/az-kv-key/main.tf
@@ -0,0 +1,41 @@
+resource "azurerm_key_vault_key" "key" {
+  name         = var.name
+  key_vault_id = var.kv_id
+  key_type     = "RSA-HSM"
+  key_size     = var.key_size
+
+  key_opts = [
+    "decrypt",
+    "encrypt",
+    "sign",
+    "unwrapKey",
+    "verify",
+    "wrapKey",
+  ]
+
+  dynamic "rotation_policy" {
+    for_each = var.rotation.expire_after == null ? [] : ["rotation_policy"]
+
+    content {
+      automatic {
+        time_before_expiry = var.rotation.auto_rotatation_time_before_expiry
+      }
+
+      expire_after         = var.rotation.expire_after
+      notify_before_expiry = var.rotation.notify_before_expiry
+    }
+  }
+
+  expiration_date = var.static_expiration_date
+
+  lifecycle {
+    precondition {
+      condition     = (var.rotation.expire_after != null && var.rotation.auto_rotatation_time_before_expiry != null) || var.rotation.expire_after == null
+      error_message = "If rotation.expire_after is set, rotation.auto_rotatation_time_before_expiry must be set"
+    }
+    precondition {
+      condition     = (var.static_expiration_date != null && var.rotation.expire_after == null) || (var.static_expiration_date == null && var.rotation.expire_after != null) || (var.static_expiration_date == null && var.rotation.expire_after == null)
+      error_message = "If static_expiration_date is set, rotation.expire_after must be null"
+    }
+  }
+}
diff --git a/terraform/modules/az-kv-key/outputs.tf b/terraform/modules/az-kv-key/outputs.tf
new file mode 100644
index 00000000..43b235b7
--- /dev/null
+++ b/terraform/modules/az-kv-key/outputs.tf
@@ -0,0 +1,20 @@
+output "id" {
+  description = "The ID of the Key Vault Key."
+  value       = azurerm_key_vault_key.key.id
+}
+
+output "name" {
+  value = var.name
+}
+
+output "public_key_pem" {
+  description = "The public key of the Key Vault Key in PEM format."
+  value       = azurerm_key_vault_key.key.public_key_pem
+  sensitive   = true
+}
+
+output "public_key_openssh" {
+  description = "The public key of the Key Vault Key in OpenSSH format."
+  value       = azurerm_key_vault_key.key.public_key_openssh
+  sensitive   = true
+}
diff --git a/terraform/modules/az-kv-key/variables.tf b/terraform/modules/az-kv-key/variables.tf
new file mode 100644
index 00000000..02ccc1b6
--- /dev/null
+++ b/terraform/modules/az-kv-key/variables.tf
@@ -0,0 +1,31 @@
+variable "name" {
+  description = "The name of the kv customer key"
+  type        = string
+}
+
+variable "kv_id" {
+  description = "The ID of the Key Vault in which the Key Vault Key for the ctomer managed key be created."
+  type        = string
+}
+
+variable "key_size" {
+  description = "The size of the key to create in the Key Vault."
+  type        = number
+  default     = 4096
+}
+
+variable "rotation" {
+  description = "The rotation policy of the managed key"
+  type = object({
+    auto_rotatation_time_before_expiry = optional(string, "P30D")
+    expire_after                       = optional(string, "P90D")
+    notify_before_expiry               = optional(string, "P29D")
+  })
+}
+
+variable "static_expiration_date" {
+  description = "The static expiration date of the managed key"
+  type        = string
+  default     = null
+}
+
diff --git a/terraform/modules/az-pep/main.tf b/terraform/modules/az-pep/main.tf
index 0a85e648..ff12d34d 100644
--- a/terraform/modules/az-pep/main.tf
+++ b/terraform/modules/az-pep/main.tf
@@ -6,9 +6,12 @@ resource "azurerm_private_endpoint" "pep" {
 
   subnet_id = var.subnet_id
 
-  private_dns_zone_group {
-    name                 = "${var.name}-dzg"
-    private_dns_zone_ids = var.private_dns_zone_ids
+  dynamic "private_dns_zone_group" {
+    for_each = length(var.private_dns_zone_ids) > 0 ? [1] : []
+    content {
+      name                 = "${var.name}-dzg"
+      private_dns_zone_ids = var.private_dns_zone_ids
+    }
   }
 
   private_service_connection {
diff --git a/terraform/modules/az-pep/variables.tf b/terraform/modules/az-pep/variables.tf
index c383f559..50fc0df5 100644
--- a/terraform/modules/az-pep/variables.tf
+++ b/terraform/modules/az-pep/variables.tf
@@ -17,6 +17,7 @@ variable "subnet_id" {
 variable "private_dns_zone_ids" {
   description = "Ids of the private dns zones to use for the pep"
   type        = list(string)
+  default     = []
 }
 
 variable "private_connection_resource_id" {
diff --git a/terraform/modules/az-vm-linux/main.tf b/terraform/modules/az-vm-linux/main.tf
new file mode 100644
index 00000000..ea266120
--- /dev/null
+++ b/terraform/modules/az-vm-linux/main.tf
@@ -0,0 +1,57 @@
+resource "azurerm_network_interface" "vm" {
+
+  name                = "${var.name}-nic"
+  location            = var.location
+  resource_group_name = var.resource_group_name
+
+  ip_configuration {
+    name                          = "${var.name}-nic-ipconfig"
+    subnet_id                     = var.subnet_id
+    private_ip_address_allocation = "Dynamic"
+  }
+
+  tags = var.tags
+}
+
+resource "azurerm_linux_virtual_machine" "vm" {
+  name                = "${var.name}-vm"
+  resource_group_name = var.resource_group_name
+  location            = var.location
+  size                = var.size
+  admin_username      = var.admin_username
+  network_interface_ids = [
+    azurerm_network_interface.vm.id,
+  ]
+
+  admin_ssh_key {
+    username   = var.admin_username
+    public_key = var.public_key_openssh
+  }
+
+  os_disk {
+    caching                = "ReadOnly"
+    storage_account_type   = var.storage_account_type
+    disk_encryption_set_id = var.disk_encryption_set_id
+
+    diff_disk_settings {
+      option    = "Local"
+      placement = "CacheDisk"
+    }
+  }
+
+  source_image_reference {
+    publisher = var.image.publisher
+    offer     = var.image.offer
+    sku       = var.image.sku
+    version   = var.image.version
+  }
+
+  identity {
+    type         = "UserAssigned"
+    identity_ids = var.identity_ids
+  }
+
+  allow_extension_operations = var.allow_extension_operations
+
+  tags = var.tags
+}
diff --git a/terraform/modules/az-vm-linux/outputs.tf b/terraform/modules/az-vm-linux/outputs.tf
new file mode 100644
index 00000000..eb269c6e
--- /dev/null
+++ b/terraform/modules/az-vm-linux/outputs.tf
@@ -0,0 +1,7 @@
+output "id" {
+  value = azurerm_linux_virtual_machine.vm.id
+}
+
+output "private_ip_address" {
+  value = azurerm_network_interface.vm.private_ip_address
+}
diff --git a/terraform/modules/az-vm-linux/variables.tf b/terraform/modules/az-vm-linux/variables.tf
new file mode 100644
index 00000000..b99e0692
--- /dev/null
+++ b/terraform/modules/az-vm-linux/variables.tf
@@ -0,0 +1,71 @@
+variable "resource_group_name" {
+  description = "The name of the resource group in which to create the linux vm."
+  type        = string
+}
+
+variable "location" {
+  description = "The location/region where the linux vm will be created."
+  type        = string
+}
+
+variable "name" {
+  description = "The name of the linux vm."
+  type        = string
+}
+
+variable "size" {
+  description = "The size of the linux vm."
+  type        = string
+}
+
+variable "admin_username" {
+  description = "The admin username of the linux vm."
+  type        = string
+}
+
+variable "public_key_openssh" {
+  description = "The public key of the linux vm in OpenSSH format."
+  type        = string
+}
+
+variable "subnet_id" {
+  description = "The subnet id of the linux vm."
+  type        = string
+}
+
+variable "storage_account_type" {
+  description = "The storage account type of the linux vm."
+  type        = string
+  default     = "Standard_LRS"
+}
+
+variable "disk_encryption_set_id" {
+  description = "The disk encryption set id of the linux vm."
+  type        = string
+}
+
+variable "image" {
+  description = "The image of the linux vm."
+  type = object({
+    publisher = string
+    offer     = string
+    sku       = string
+    version   = string
+  })
+}
+
+variable "identity_ids" {
+  description = "List of identity ids to assign to the vm"
+  type        = list(string)
+}
+
+variable "allow_extension_operations" {
+  description = "Allow extension operations on the linux vm."
+  type        = bool
+  default     = false
+}
+
+variable "tags" {
+  description = "The tags of the linux vm."
+  type        = map(string)
+}
diff --git a/terraform/modules/az-vm/checkov.yml b/terraform/modules/az-vm-win/checkov.yml
similarity index 100%
rename from terraform/modules/az-vm/checkov.yml
rename to terraform/modules/az-vm-win/checkov.yml
diff --git a/terraform/modules/az-vm/main.tf b/terraform/modules/az-vm-win/main.tf
similarity index 100%
rename from terraform/modules/az-vm/main.tf
rename to terraform/modules/az-vm-win/main.tf
diff --git a/terraform/modules/az-vm/outputs.tf b/terraform/modules/az-vm-win/outputs.tf
similarity index 100%
rename from terraform/modules/az-vm/outputs.tf
rename to terraform/modules/az-vm-win/outputs.tf
diff --git a/terraform/modules/az-vm/variables.tf b/terraform/modules/az-vm-win/variables.tf
similarity index 100%
rename from terraform/modules/az-vm/variables.tf
rename to terraform/modules/az-vm-win/variables.tf