diff --git a/modules/ansible_generator.py b/modules/ansible_generator.py
index 74fd818b6b48ee206fa3e6eea2af59b749ed8c8f..928a943aa9d493b47a1c72d471c60365804a2460 100644
--- a/modules/ansible_generator.py
+++ b/modules/ansible_generator.py
@@ -4,6 +4,15 @@ from modules.file_manager import generate_file, copy_template_file,\
     copy_user_provisioning_dir
 from modules.ansible_vars_generator import generate_ansible_vars
 from conf.border_router import BORDER_ROUTER_NAME
+from modules.controller import CONTROLLER_NAME
+
+def _find_windows_boxes(definitions):
+    """Find and return list of host names with windows boxes."""
+    windows_hosts = []
+    for host in definitions["hosts"]:
+        if "windows" in host["base_box"]["image"].lower():
+            windows_hosts.append(host["name"])
+    return windows_hosts
 
 
 def _create_config_playbooks(input_definitions, flags):
@@ -18,14 +27,23 @@ def _create_config_playbooks(input_definitions, flags):
         copy_template_file("routers",
                            "base_provisioning/roles/routers/tasks/main.yml")
 
+    windows_hosts = _find_windows_boxes(input_definitions)
+
     for device in input_definitions["hosts"] + input_definitions["routers"]:
         if "border_router" in flags and flags["border_router"] and\
            device["name"] == BORDER_ROUTER_NAME:
             copy_template_file("br", "base_provisioning/roles/" +
                                device["name"] + "/tasks/main.yml")
         else:
-            copy_template_file("separate_devices", "base_provisioning/roles/" +
-                               device["name"] + "/tasks/main.yml")
+            if device["name"] == CONTROLLER_NAME:
+                copy_template_file("controller", "base_provisioning/roles/" +
+                                   device["name"] + "/tasks/main.yml")
+            elif device["name"] in windows_hosts:
+                copy_template_file("windows_devices", "base_provisioning/roles/" +
+                                   device["name"] + "/tasks/main.yml")
+            else:
+                copy_template_file("separate_devices", "base_provisioning/roles/" +
+                                   device["name"] + "/tasks/main.yml")
 
 
 def _generate_user_playbooks(input_definitions):
diff --git a/modules/controller.py b/modules/controller.py
new file mode 100644
index 0000000000000000000000000000000000000000..2876bea160d3e936598e3ed524bf5a96367860ea
--- /dev/null
+++ b/modules/controller.py
@@ -0,0 +1,66 @@
+"""Contains functions for controller creation."""
+
+
+from netaddr import *
+from itertools import chain
+
+CONTROLLER_NAME = "controller"
+CONTROLLER_BOX_NAME = "debian/contrib-stretch64"
+CONTROLLER_MEMORY = 512
+
+
+def _are_controller_parameters_free(definitions):
+    """Check if controller parameters are not already taken."""
+    for host in definitions["hosts"]:
+        if host["name"] == CONTROLLER_NAME:
+            return False
+        # TODO add other parameters
+
+    return True
+
+
+def _is_ip_available(ip, definitions):
+    """Check if the given ip is used somewhere"""
+    for mapping in chain(definitions["router_mappings"],
+                         definitions["net_mappings"]):
+        if mapping["ip"] == ip:
+            return False
+    return True
+
+
+def _find_available_ip(definitions):
+    """Return an available ip for the controller"""
+    network = IPNetwork(definitions["networks"][0]["cidr"])
+    for ip in network:
+        if _is_ip_available(ip, definitions):
+            return str(ip)
+
+
+def _add_controller(definitions):
+    """Add controller to definitions"""
+    controller_ip = _find_available_ip(definitions)
+    controller = dict()
+    controller["name"] = CONTROLLER_NAME
+    controller["base_box"] = dict()
+    controller["base_box"]["image"] = CONTROLLER_BOX_NAME
+    controller["memory"] = CONTROLLER_MEMORY
+    definitions["hosts"].append(controller)
+    mapping = dict()
+    mapping["host"] = CONTROLLER_NAME
+    mapping["network"] = definitions["networks"][0]["name"]
+    mapping["ip"] = controller_ip
+    definitions["net_mappings"].append(mapping)
+
+
+def create_controller(definitions):
+    """Add the definition of controller to definitions.
+
+    :param definitions: device definition structure
+    """
+
+    # TODO this should be later moved to input check
+    if not _are_controller_parameters_free(definitions):
+        raise ValueError("A device with the same name as border router "
+                         "already exists.")
+
+    _add_controller(definitions)
diff --git a/modules/preprocessing.py b/modules/preprocessing.py
index 06deecd2389573bedd8905e587191255dff68ad5..b331775d219b38ee60437f919d808a6734aa6b3f 100644
--- a/modules/preprocessing.py
+++ b/modules/preprocessing.py
@@ -7,6 +7,7 @@ creation.
 import itertools
 
 from modules.border_router import create_border_router
+from modules.controller import create_controller
 from modules.file_manager import open_yaml, cleanup_and_exit
 
 FLAVORS = open_yaml("conf/flavors.yml")
@@ -44,7 +45,7 @@ def _add_extra_arguments(definitions, flags):
     if "border_router" in flags and flags["ansible_local"]:
         for device in itertools.chain(definitions["hosts"],
                                       definitions["routers"]):
-            if "synced_folder" not in device:
+            if "synced_folder" not in device and "communicator" not in device:
                 device["synced_folder"] = "\".\", \"/vagrant\", type: \"rsync"\
                                           "\", rsync__exclude: \".git/\""
 
@@ -71,6 +72,22 @@ def _delete_dummy_router(definitions):
             return
 
 
+def _find_windows_boxes(definitions):
+    """Find and return list of host names with windows boxes."""
+    windows_hosts = []
+    for host in definitions["hosts"]:
+        if "windows" in host["base_box"]["image"].lower():
+            windows_hosts.append(host["name"])
+    return windows_hosts
+
+
+def _add_windows_attributes(host):
+    """Add vagrant attributes to windows hosts"""
+    host["communicator"] = "winrm"
+    host["winrm_username"] = "windows"
+    host["winrm_password"] = "vagrant"
+
+
 def preprocess(definitions, flags):
     """Run preprocessing.
 
@@ -97,6 +114,22 @@ def preprocess(definitions, flags):
         cleanup_and_exit("Preprocessing not successful: "
                          "Could not create border router (" + error + ")")
 
+    windows_hosts = _find_windows_boxes(definitions)
+    try:
+        if windows_hosts:
+            create_controller(definitions)
+    except (ValueError, IndexError) as error:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not create controller (" + error + ")")
+
+    try:
+        for host in definitions["hosts"]:
+            if host["name"] in windows_hosts:
+                _add_windows_attributes(host)
+    except (ValueError, IndexError) as error:
+        cleanup_and_exit("Preprocessing not successful: "
+                         "Could not create controller (" + error + ")")
+
     try:
         _configure_routers(definitions)
     except Exception:
diff --git a/modules/vagrant_generator.py b/modules/vagrant_generator.py
index f6211bbfd49b13306c9c2be60bfa4af6d0952f62..83f57574611a1393d4097538dd9f6c14564c4a41 100644
--- a/modules/vagrant_generator.py
+++ b/modules/vagrant_generator.py
@@ -1,6 +1,9 @@
 """Contains functions for generating a Vagrantfile from input definitions."""
 
+import netaddr
+
 from modules.file_manager import generate_file, open_yaml
+from modules.controller import CONTROLLER_NAME
 
 VAGRANT_MAPPING = open_yaml("conf/vagrant_mapping.yml")
 VIRTUALBOX_MAPPING = open_yaml("conf/virtualbox_mapping.yml")
@@ -93,9 +96,10 @@ def _create_commands(device_attributes):
     return commands
 
 
-def _create_ansible_commands(playbook_location, input_definitions, flags):
+def _create_ansible_commands(device_name, playbook_location, input_definitions, flags):
     """Create commands for running a playbook from the Vagrantfile."""
     commands = []
+    windows_hosts = _find_windows_boxes(input_definitions);
 
     playbook = dict()
     playbook["type"] = "string"
@@ -103,6 +107,12 @@ def _create_ansible_commands(playbook_location, input_definitions, flags):
     playbook["value"] = playbook_location
     commands.append(playbook)
 
+    limit = dict()
+    limit["type"] = "string"
+    limit["command"] = "limit"
+    limit["value"] = device_name
+    commands.append(limit)
+
     if "verbose_ansible" in flags and flags["verbose_ansible"]:
         verbosity = dict()
         verbosity["type"] = "string"
@@ -121,6 +131,16 @@ def _create_ansible_commands(playbook_location, input_definitions, flags):
     for router in input_definitions["routers"]:
         router_names.append(router["name"])
     groups["groups"]["routers"] = router_names
+    windows_hosts = _find_windows_boxes(input_definitions)
+    groups["groups"]["windows"] = windows_hosts
+    if device_name in windows_hosts:
+        windows_vars = dict()
+        windows_vars["ansible_connection"] = "\"winrm\""
+        windows_vars["ansible_user"] = "\"windows\""
+        windows_vars["ansible_password"] = "\"vagrant\""
+        windows_vars["ansible_winrm_transport"] = "\"basic\""
+        windows_vars["ansible_winrm_server_cert_validation"] = "\"ignore\""
+        groups["groups"]["windows:vars"] = windows_vars
     if "groups" in input_definitions:
         for group in input_definitions["groups"]:
             groups["groups"][group["name"]] = group["nodes"]
@@ -148,8 +168,8 @@ def _create_ansible_commands(playbook_location, input_definitions, flags):
     extravars["type"] = "dictionary"
     extravars["command"] = "extra_vars"
     extravars["dictionary"] = dict()
-    extravars["dictionary"]["ansible_python_interpreter"] = \
-        "\"/usr/bin/python3\""
+    if device_name not in windows_hosts:
+        extravars["dictionary"]["ansible_python_interpreter"] = "\"/usr/bin/python3\""
     commands.append(extravars)
 
     return commands
@@ -173,8 +193,10 @@ def _add_networks_to_device(definition, mappings, input_definitions):
             network["network_type"] = "private_network"
             network["name"] = mapping["network"]
             network["ip"] = mapping["ip"]
-            network["netmask"] = _find_netmask(mapping["network"],
-                                               input_definitions["networks"])
+            netmask = netaddr.IPNetwork("0.0.0.0/" +
+                                        _find_netmask(mapping["network"],
+                                                      input_definitions["networks"])).netmask
+            network["netmask"] = str(netmask)
             definition["commands"].append(network)
 
 
@@ -191,10 +213,23 @@ def _add_all_networks(vagrant_definitions, input_definitions, flags):
                                     input_definitions)
 
 
-def _call_provisioner(input_definitions, flags):
+def _call_provisioner(device_name, input_definitions, flags):
     """Create entry to vagrant definitions for calling the provisioner."""
     provisioner_calls = []
 
+    if device_name == CONTROLLER_NAME:
+        shell_provisioning = dict()
+        shell_provisioning["type"] = "provision"
+        shell_provisioning["provisioner"] = "shell"
+        shell_provisioning["note"] = "install pip3 and ansible"
+
+        install_ansible = dict()
+        install_ansible["type"] = "string"
+        install_ansible["command"] = "inline"
+        install_ansible["value"] = "sudo apt-get install -y python3-pip && pip3 install --upgrade ansible"
+        shell_provisioning["commands"] = [install_ansible]
+        provisioner_calls.append(shell_provisioning)
+
     config_playbook = dict()
     config_playbook["type"] = "provision"
     if "ansible_local" in flags and flags["ansible_local"]:
@@ -202,7 +237,8 @@ def _call_provisioner(input_definitions, flags):
     else:
         config_playbook["provisioner"] = "ansible"
     config_playbook["note"] = "basic configuration of devices and networks"
-    config_playbook["commands"] = _create_ansible_commands(BASE_PLAYBOOK,
+    config_playbook["commands"] = _create_ansible_commands(device_name,
+                                                           BASE_PLAYBOOK,
                                                            input_definitions,
                                                            flags)
 
@@ -215,7 +251,8 @@ def _call_provisioner(input_definitions, flags):
     else:
         user_playbook["provisioner"] = "ansible"
     user_playbook["note"] = "user configuration of devices"
-    user_playbook["commands"] = _create_ansible_commands(USER_PLAYBOOK,
+    user_playbook["commands"] = _create_ansible_commands(device_name,
+                                                         USER_PLAYBOOK,
                                                          input_definitions,
                                                          flags)
 
@@ -224,6 +261,15 @@ def _call_provisioner(input_definitions, flags):
     return provisioner_calls
 
 
+def _find_windows_boxes(definitions):
+    """Find and return list of host names with windows boxes."""
+    windows_hosts = []
+    for host in definitions["hosts"]:
+        if "windows" in host["base_box"]["image"].lower():
+            windows_hosts.append(host["name"])
+    return windows_hosts
+
+
 def _build_vagrant_definitions(input_definitions, flags):
     """Create a definition structure for vagrant.
 
@@ -236,19 +282,26 @@ def _build_vagrant_definitions(input_definitions, flags):
         device["type"] = "router"
         device["name"] = router["name"]
         device["commands"] = _create_commands(router)
+        device["commands"].extend(_call_provisioner(device["name"], input_definitions, flags))
         vagrant_definitions.append(device)
 
+    windows_hosts = _find_windows_boxes(input_definitions)
+    windows_provisioning_commands = []
     for host in input_definitions["hosts"]:
         device = dict()
         device["type"] = "host"
         device["name"] = host["name"]
         device["commands"] = _create_commands(host)
+        if host["name"] in windows_hosts:
+            windows_provisioning_commands.extend(_call_provisioner(device["name"], input_definitions, flags))
+        else:
+            device["commands"].extend(_call_provisioner(device["name"], input_definitions, flags))
+        if host["name"] == CONTROLLER_NAME:
+            device["commands"].extend(windows_provisioning_commands)
         vagrant_definitions.append(device)
 
     _add_all_networks(vagrant_definitions, input_definitions, flags)
 
-    vagrant_definitions.extend(_call_provisioner(input_definitions, flags))
-
     return vagrant_definitions
 
 
diff --git a/requirements.txt b/requirements.txt
index 72a3c107abb60bc793986db432e8f7c783d82869..fdada436324c91dded5553993f73e454b218319b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
 Jinja2>=2.10.1
 PyYAML>=5.3.1
+netaddr>=0.7
\ No newline at end of file
diff --git a/templates/controller b/templates/controller
new file mode 100644
index 0000000000000000000000000000000000000000..8351a03292d43cd27bb35abdb98960876166ce61
--- /dev/null
+++ b/templates/controller
@@ -0,0 +1,27 @@
+---
+- name: Adding aliases
+  loop: "{{ aliases | dict2items }}"
+  lineinfile:
+    path: /etc/hosts
+    line: "{{ item.value }} {{ item.key }}"
+
+- name: Configuring routes
+  include_role:
+    name: interface
+  vars:
+    interface_configuration_type: static
+    interface_identification: "{{ interface.interface_ip }}"
+    interface_static_ip: "{{ interface.interface_ip }}"
+    interface_static_netmask: "{{ interface.interface_netmask }}"
+    interface_default_gateway: "{{ interface.interface_default_gateway | default('') }}"
+    interface_routes: "{{ interface.interface_routes | default([]) }}"
+  loop: "{{ routes }}"
+  loop_control:
+    loop_var: interface
+
+
+- name: Install pywinrm
+  pip:
+    name: pywinrm
+    executable: "/usr/bin/pip3"
+...
diff --git a/templates/device_configuration b/templates/device_configuration
index 056e19806fae62a00131a4ae13a3c87ec4a7cae8..39582f43fcbd55f9b3bb3ea88fda7bebcbdd2612 100644
--- a/templates/device_configuration
+++ b/templates/device_configuration
@@ -10,12 +10,6 @@
       file: config.yml
       name: config
 
-- name: Configuring hosts
-  hosts: hosts
-  become: yes
-  roles:
-    - hosts
-
 - name: Configuring routers
   hosts: routers
   become: yes
@@ -25,9 +19,21 @@
       name: routers
     when: config.routers
 
-- name: Configuring devices separately
-  hosts: all
+- name: Configuring Linux devices separately
+  hosts: all:!windows
+  become: yes
+  tasks:
+  - name: include role
+    include_role:
+      name: "{{ inventory_hostname }}"
+
+- name: Configuring Windows devices separately
+  hosts: windows
   become: yes
+  become_method: runas
+  become_user: windows
+  vars:
+    ansible_become_pass: vagrant
   tasks:
   - name: include role
     include_role:
diff --git a/templates/separate_devices b/templates/separate_devices
index 0c8df57912921e8d52cb7064b4265e2233550ac4..88b2ca7c7f2fdc18e2a14ab4bc11414bc9c54df5 100644
--- a/templates/separate_devices
+++ b/templates/separate_devices
@@ -1,4 +1,8 @@
 ---
+- name: Install net-tools
+  apt:
+    name: "net-tools"
+
 - name: Adding aliases
   loop: "{{ aliases | dict2items }}"
   lineinfile:
@@ -18,4 +22,4 @@
   loop: "{{ routes }}"
   loop_control:
     loop_var: interface
-...
\ No newline at end of file
+...
diff --git a/templates/vagrantfile b/templates/vagrantfile
index c0f4cc928a998ef0f594fbe66049d8d34ed16d6a..b5eff4a5e6f8302d7d955bbe862f7f1a6a449ba8 100644
--- a/templates/vagrantfile
+++ b/templates/vagrantfile
@@ -27,8 +27,8 @@
 {# Macro for provision items #}
 {% macro provision(item, namespace) %}
   # {{ item.note }}
-  {{ namespace }}.vm.provision :{{ item.provisioner }} do |provisioner|
-{{ layer2(item.commands, "provisioner") }}
+    {{ namespace }}.vm.provision :{{ item.provisioner }} do |provisioner|
+{{ layer3(item.commands, "provisioner") }}
 {% endmacro -%}
 
 {# Macro for string items #}
@@ -64,25 +64,40 @@
   {{ namespace }}.{{ item.command }} = {
   {% for key, value in item.dictionary.items() %}
     {% if loop.last %}
-      {{ key }}: {{ value }}
+        {{ key }}: {{ value }}
     {% else %}
-      {{ key }}: {{ value }},
+        {{ key }}: {{ value }},
     {% endif %}
   {% endfor %}
-    }
+      }
 {% endmacro -%}
 
+{# Macro for group dictionaries #}
+{% macro group_dict(item, namespace) %}{
+  {% for key, value in item.items() %}
+      {% if loop.last %}
+          "{{ key }}" => {{ value }}
+      {% else %}
+          "{{ key }}" => {{ value }},
+    {% endif %}
+  {% endfor %}
+        }{% endmacro -%}
+
 {# Macro for dictionaries #}
 {% macro groups(item, namespace) %}
   {{ namespace }}.groups = {
   {% for key, value in item.groups.items() %}
-    {% if loop.last %}
-      "{{ key }}" => {{ value }}
+    {% if value is mapping %}
+        "{{ key }}" => {{ group_dict(value, namespace) }}
     {% else %}
-      "{{ key }}" => {{ value }},
+      {% if loop.last %}
+        "{{ key }}" => {{ value }}
+      {% else %}
+        "{{ key }}" => {{ value }},
+      {% endif %}
     {% endif %}
   {% endfor %}
-    }
+      }
 {% endmacro -%}
 
 {# Macro for network items #}
@@ -103,8 +118,6 @@
 {{ router(item, namespace) }}
 {% elif item.type == "host" %}
 {{ host(item, namespace) }}
-{% elif item.type == "provision" %}
-{{ provision(item, namespace) }}
 {% endif %}
 {% endfor %}
 end
@@ -129,6 +142,8 @@ end
   {{ dictionary(item, namespace) -}}
 {% elif item.type == "groups" %}
   {{ groups(item, namespace) -}}
+{% elif item.type == "provision" %}
+  {{ provision(item, namespace) }}
 {% endif %}
 {% endfor %}
   end
@@ -143,6 +158,10 @@ end
     {{ boolean(item, namespace) -}}
 {% elif item.type == "integer" %}
     {{ integer(item, namespace) -}}
+{% elif item.type == "dictionary" %}
+    {{ dictionary(item, namespace) -}}
+{% elif item.type == "groups" %}
+    {{ groups(item, namespace) -}}
 {% endif %}
 {% endfor %}
     end
diff --git a/templates/windows_devices b/templates/windows_devices
new file mode 100644
index 0000000000000000000000000000000000000000..bf9673bbaad95198a2275c17c42258ef7dd50980
--- /dev/null
+++ b/templates/windows_devices
@@ -0,0 +1,6 @@
+---
+
+- name: print hello world
+  debug:
+    msg: "Hello World"
+...