diff --git a/proxmox/terraform/modules/node/main.tf b/proxmox/terraform/modules/node/main.tf
new file mode 100644
index 0000000000000000000000000000000000000000..e3e5474634c8342698892da8b0bb388a5903836b
--- /dev/null
+++ b/proxmox/terraform/modules/node/main.tf
@@ -0,0 +1,51 @@
+resource "proxmox_vm_qemu" "node" {
+    name              = "${var.hostname}"
+    desc              = "${var.description}"
+    # hypervisor onto which make the vm
+    target_node       = "${var.hypervisor}"
+    # See init-template.md to see the template vm bootstrap
+    clone             = "${var.template}"
+    # linux kernel 2.6
+    qemu_os           = "l26"
+    # generic setup
+    sockets           = "${var.sockets}"
+    cores             = "${var.cores}"
+    memory            = "${var.memory}"
+    # boot machine when hypervirsor starts
+    onboot            = true
+    # cloud-init setup
+    os_type           = "cloud-init"
+    # ciuser - User name to change ssh keys and password for instead of the
+    # image’s configured default user.
+    ciuser            = "${var.user_admin}"
+    ssh_user          = "${var.user_admin}"
+    # sshkeys - public ssh keys, one per line
+    sshkeys           = "${var.user_admin_ssh_public_key}"
+    # searchdomain - Sets DNS search domains for a container.
+    searchdomain      = "${var.domain}"
+    # nameserver - Sets DNS server IP address for a container.
+    nameserver        = "${var.dns}"
+    # ipconfig0 - [gw =] [,ip=<IPv4Format/CIDR>]
+    ipconfig0         = "ip=${var.network["ip"]}/24,gw=${var.gateway_ip}"
+    disk {
+        id           = 0
+        type         = "virtio"
+        storage      = "${var.storage["location"]}"
+        storage_type = "ssd"
+        size         = "${var.storage["size"]}"
+    }
+    network {
+        id      = 0
+        model   = "virtio"
+        bridge  = "vmbr0"
+        macaddr = "${lookup(var.network, "macaddr", "")}"
+    }
+
+    # Delegate to puppet at the end of the provisioning the software setup
+    provisioner "remote-exec" {
+        inline = [
+            "sed -i 's/127.0.1.1/${var.network["ip"]}/g' /etc/hosts",
+            "puppet agent --server ${var.puppet_master} --environment=${var.puppet_environment} --waitforcert 60 --test || echo 'Node provisionned!'",
+        ]
+    }
+}
diff --git a/proxmox/terraform/modules/node/outputs.tf b/proxmox/terraform/modules/node/outputs.tf
new file mode 100644
index 0000000000000000000000000000000000000000..c4804d7273cca26508f28d75aac2e048390e730b
--- /dev/null
+++ b/proxmox/terraform/modules/node/outputs.tf
@@ -0,0 +1,11 @@
+output name {
+    value = "${proxmox_vm_qemu.node.name}"
+}
+
+output ip {
+    value = "${proxmox_vm_qemu.node.network.*.ip}"
+}
+
+output macaddr {
+    value = "${proxmox_vm_qemu.node.network.*.macaddr}"
+}
diff --git a/proxmox/terraform/modules/node/variables.tf b/proxmox/terraform/modules/node/variables.tf
new file mode 100644
index 0000000000000000000000000000000000000000..1a110d1b0969017deb310a6c4965680a08d133a7
--- /dev/null
+++ b/proxmox/terraform/modules/node/variables.tf
@@ -0,0 +1,99 @@
+variable "hostname" {
+    description = "Node's hostname"
+    type        = "string"
+}
+
+variable "description" {
+    description = "Node's description"
+    type        = "string"
+}
+
+variable "hypervisor" {
+    description = "Hypervisor to install the vm to (choice: orsay, hypervisor3, beaubourg)"
+    type        = "string"
+    default     = "orsay"
+}
+
+variable "template" {
+    description = "Template to use (template-debian-9, template-debian-10)"
+    type        = "string"
+    default     = "template-debian-9"
+}
+
+variable "sockets" {
+    description = "Number of sockets"
+    type        = "string"
+    default     = "1"
+}
+
+variable "cores" {
+    description = "Number of cores"
+    type        = "string"
+    default     = "1"
+}
+
+variable "memory" {
+    description = "Memory in Mb"
+    type        = "string"
+    default     = "1024"
+}
+
+variable "network" {
+    description = "staging network's ip/macaddr"
+    type        = "map"
+}
+
+variable "storage" {
+    description = "Storage disk location and size in the hypervisor storage"
+    type = "map"
+     default = {
+         location = "orsay-ssd-2018"
+         size     = "32G"
+     }
+}
+
+#### Below, variables are duplicated (/me is sad, don't know how to avoid it
+#### for now)
+
+variable "domain" {
+    description = "DNS zone for the staging area"
+    type        = "string"
+    default     = "internal.staging.swh.network"
+}
+
+variable "puppet_environment" {
+    description = "Puppet environment to use (swh-site's git branch)"
+    type        = "string"
+    default     = "new_staging"
+}
+
+variable "puppet_master" {
+    description = "Puppet master FQDN"
+    type        = "string"
+    default     = "pergamon.internal.softwareheritage.org"
+}
+
+variable "dns" {
+    description = "DNS server ip"
+    type        = "string"
+    default     = "192.168.100.29"
+}
+
+variable "gateway_ip" {
+    description = "Staging network gateway ip"
+    type        = "string"
+    default     = "192.168.128.1"
+}
+
+variable "user_admin" {
+    description = "User admin to use for managing the node"
+    type        = "string"
+    default     = "root"
+}
+
+# define input variables for the modules
+# `pass search terraform-proxmox` in credential store
+variable "user_admin_ssh_public_key" {
+  type    = "string"
+  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVKCfpeIMg7GS3Pk03ZAcBWAeDZ+AvWk2k/pPY0z8MJ3YAbqZkRtSK7yaDgJV6Gro7nn/TxdJLo2jEzzWvlC8d8AEzhZPy5Z/qfVVjqBTBM4H5+e+TItAHFfaY5+0WvIahxcfsfaq70MWfpJhszAah3ThJ4mqzYaw+dkr42+a7Gx3Ygpb/m2dpnFnxvXdcuAJYStmHKU5AWGWWM+Fm50/fdMqUfNd8MbKhkJt5ihXQmZWMOt7ls4N8i5NZWnS9YSWow8X/ENOEqCRN9TyRkc+pPS0w9DNi0BCsWvSRJOkyvQ6caEnKWlNoywCmM1AlIQD3k4RUgRWe0vqg/UKPpH3Z root@terraform"
+}
diff --git a/proxmox/terraform/staging.tf b/proxmox/terraform/staging.tf
index a1085dbc45ff0e00008f2a23dc578381a85060b8..3e87dfa33969139f52e53a2a5313737e33232cfb 100644
--- a/proxmox/terraform/staging.tf
+++ b/proxmox/terraform/staging.tf
@@ -9,42 +9,7 @@ provider "proxmox" {
     # in a shell (see README): source ./setup.sh
 }
 
-# `pass search terraform-proxmox` in credential store
-variable "ssh_key_data" {
-  type    = "string"
-  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVKCfpeIMg7GS3Pk03ZAcBWAeDZ+AvWk2k/pPY0z8MJ3YAbqZkRtSK7yaDgJV6Gro7nn/TxdJLo2jEzzWvlC8d8AEzhZPy5Z/qfVVjqBTBM4H5+e+TItAHFfaY5+0WvIahxcfsfaq70MWfpJhszAah3ThJ4mqzYaw+dkr42+a7Gx3Ygpb/m2dpnFnxvXdcuAJYStmHKU5AWGWWM+Fm50/fdMqUfNd8MbKhkJt5ihXQmZWMOt7ls4N8i5NZWnS9YSWow8X/ENOEqCRN9TyRkc+pPS0w9DNi0BCsWvSRJOkyvQ6caEnKWlNoywCmM1AlIQD3k4RUgRWe0vqg/UKPpH3Z root@terraform"
-}
-
-variable "user_admin" {
-  type    = "string"
-  default = "root"
-}
-
-variable "domain" {
-  type    = "string"
-  default = "internal.staging.swh.network"
-}
-
-variable "puppet_environment" {
-  type    = "string"
-  default = "new_staging"
-}
-
-variable "puppet_master" {
-  type    = "string"
-  default = "pergamon.internal.softwareheritage.org"
-}
-
-variable "dns" {
-  type    = "string"
-  default = "192.168.100.29"
-}
-
-variable "gateway_ip" {
-  type    = "string"
-  default = "192.168.128.1"
-}
-
+# define the staging network gateway
 resource "proxmox_vm_qemu" "gateway" {
     name              = "gateway"
     desc              = "staging gateway node"
@@ -73,7 +38,7 @@ resource "proxmox_vm_qemu" "gateway" {
     # nameserver - Sets DNS server IP address for a container.
     nameserver        = "${var.dns}"
     # sshkeys - public ssh keys, one per line
-    sshkeys           = "${var.ssh_key_data}"
+    sshkeys           = "${var.user_admin_ssh_public_key}"
     # FIXME: When T1872 lands, this will need to be updated
     # ipconfig0 - [gw =] [,ip=<IPv4Format/CIDR>]
     # ip to communicate for now with the prod network through louvre
@@ -113,58 +78,18 @@ resource "proxmox_vm_qemu" "gateway" {
     }
 }
 
-resource "proxmox_vm_qemu" "storage" {
-    name              = "storage0"
-    desc              = "swh storage node"
-    # hypervisor onto which make the vm
-    target_node       = "orsay"
-    # See init-template.md to see the template vm bootstrap
-    clone             = "template-debian-9"
-    # linux kernel 2.6
-    qemu_os           = "l26"
-    # generic setup
-    sockets           = 1
-    cores             = 2
-    memory            = 8192
-    # boot machine when hypervirsor starts
-    onboot            = true
-    #### cloud-init setup
-    # to actually set some information per os_type (values: ubuntu, centos,
-    # cloud-init). Keep this as cloud-init
-    os_type           = "cloud-init"
-    # ciuser - User name to change ssh keys and password for instead of the
-    # image’s configured default user.
-    ciuser            = "${var.user_admin}"
-    # searchdomain - Sets DNS search domains for a container.
-    searchdomain      = "${var.domain}"
-    # nameserver - Sets DNS server IP address for a container.
-    nameserver        = "${var.dns}"
-    # sshkeys - public ssh keys, one per line
-    sshkeys           = "${var.ssh_key_data}"
-    # ipconfig0 - [gw =] [,ip=<IPv4Format/CIDR>]
-    ipconfig0         = "ip=192.168.128.2/24,gw=${var.gateway_ip}"
-    ssh_user          = "${var.user_admin}"
-    disk {
-        id           = 0
-        type         = "virtio"
-        storage      = "orsay-ssd-2018"
-        storage_type = "ssd"
-        size         = "32G"
-    }
-    network {
-        id      = 0
-        model   = "virtio"
-        bridge  = "vmbr0"
+module "storage0" {
+    source      = "./modules/node"
+
+    hostname    = "storage0"
+    description = "swh storage services"
+    cores       = "4"
+    memory      = "8192"
+    network = {
+        ip      = "192.168.128.2"
         macaddr = "CA:73:7F:ED:F9:01"
     }
+}
 
-    # Delegate to puppet at the end of the provisioning the software setup
-    provisioner "remote-exec" {
-        inline = [
-            "sed -i 's/127.0.1.1/192.168.128.2/g' /etc/hosts",
-            "puppet agent --server ${var.puppet_master} --environment=${var.puppet_environment} --waitforcert 60 --test || echo 'Node provisionned!'",
-        ]
     }
-    # forced to specify as there is no way to introspect the gateway's ip
-    depends_on = ["proxmox_vm_qemu.gateway"]
 }
diff --git a/proxmox/terraform/variables.tf b/proxmox/terraform/variables.tf
new file mode 100644
index 0000000000000000000000000000000000000000..49ed57d96f674f811f376d8d11e2af15f2fe5623
--- /dev/null
+++ b/proxmox/terraform/variables.tf
@@ -0,0 +1,42 @@
+variable "domain" {
+    description = "DNS zone for the staging area"
+    type        = "string"
+    default     = "internal.staging.swh.network"
+}
+
+variable "puppet_environment" {
+    description = "Puppet environment to use (swh-site's git branch)"
+    type        = "string"
+    default     = "new_staging"
+}
+
+variable "puppet_master" {
+    description = "Puppet master FQDN"
+    type        = "string"
+    default     = "pergamon.internal.softwareheritage.org"
+}
+
+variable "dns" {
+    description = "DNS server ip"
+    type        = "string"
+    default     = "192.168.100.29"
+}
+
+variable "gateway_ip" {
+    description = "Staging network gateway ip"
+    type        = "string"
+    default     = "192.168.128.1"
+}
+
+variable "user_admin" {
+    description = "User admin to use for managing the node"
+    type        = "string"
+    default     = "root"
+}
+
+# define input variables for the modules
+# `pass search terraform-proxmox` in credential store
+variable "user_admin_ssh_public_key" {
+  type    = "string"
+  default = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDVKCfpeIMg7GS3Pk03ZAcBWAeDZ+AvWk2k/pPY0z8MJ3YAbqZkRtSK7yaDgJV6Gro7nn/TxdJLo2jEzzWvlC8d8AEzhZPy5Z/qfVVjqBTBM4H5+e+TItAHFfaY5+0WvIahxcfsfaq70MWfpJhszAah3ThJ4mqzYaw+dkr42+a7Gx3Ygpb/m2dpnFnxvXdcuAJYStmHKU5AWGWWM+Fm50/fdMqUfNd8MbKhkJt5ihXQmZWMOt7ls4N8i5NZWnS9YSWow8X/ENOEqCRN9TyRkc+pPS0w9DNi0BCsWvSRJOkyvQ6caEnKWlNoywCmM1AlIQD3k4RUgRWe0vqg/UKPpH3Z root@terraform"
+}