diff --git a/.gitignore b/.gitignore
index b09ca9d3b101034c7e34430177c1d64738df5fbb..c75c9981ddaa87919b92808d74ab822b139be5c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -99,3 +99,7 @@ target/
 # virtualenv
 venv/
 ENV/
+
+# molecule
+roles/**/molecule/**/__pycache__/
+roles/**/molecule/**/*.conf
diff --git a/roles/adduser/molecule/default/converge.yml b/roles/adduser/molecule/default/converge.yml
new file mode 100644
index 0000000000000000000000000000000000000000..47ff6c7e0ff0603e71254afc8e7926debb0f5acd
--- /dev/null
+++ b/roles/adduser/molecule/default/converge.yml
@@ -0,0 +1,10 @@
+---
+- name: Converge
+  hosts: all
+  become: true
+  gather_facts: false
+  roles:
+    - role: adduser
+  vars:
+    user:
+      name: foo
diff --git a/roles/adduser/molecule/default/molecule.yml b/roles/adduser/molecule/default/molecule.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4bb5dce3084b0911cf678ebe4ad5219db06aa131
--- /dev/null
+++ b/roles/adduser/molecule/default/molecule.yml
@@ -0,0 +1,23 @@
+---
+dependency:
+  name: galaxy
+lint: |
+  set -e
+  yamllint -c ../../.yamllint .
+driver:
+  name: vagrant
+  provider:
+    name: libvirt
+platforms:
+  - name: adduser-01
+    box: generic/ubuntu2004
+    cpus: 1
+    memory: 512
+provisioner:
+  name: ansible
+  lint:
+    name: ansible-lint
+verifier:
+  name: testinfra
+  lint:
+    name: flake8
diff --git a/roles/adduser/molecule/default/tests/test_default.py b/roles/adduser/molecule/default/tests/test_default.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c81047e2acd8a38f6ddc5e17ecd6bf2c67a97a7
--- /dev/null
+++ b/roles/adduser/molecule/default/tests/test_default.py
@@ -0,0 +1,37 @@
+import os
+import yaml
+import glob
+import testinfra.utils.ansible_runner
+from ansible.playbook import Playbook
+from ansible.cli.playbook import PlaybookCLI
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+def read_playbook(playbook):
+    cli_args = [os.path.realpath(playbook), testinfra_hosts]
+    cli = PlaybookCLI(cli_args)
+    cli.parse()
+    loader, inventory, variable_manager = cli._play_prereqs()
+
+    pb = Playbook.load(cli.args[0], variable_manager, loader)
+
+    for play in pb.get_plays():
+        yield variable_manager.get_vars(play)
+
+def get_playbook():
+    with open(os.path.realpath(' '.join(map(str,glob.glob('molecule.*')))), 'r') as yamlfile:
+        data = yaml.load(yamlfile, Loader=yaml.FullLoader)
+        if 'playbooks' in data['provisioner'].keys():
+            if 'converge' in data['provisioner']['playbooks'].keys():
+                return data['provisioner']['playbooks']['converge']
+        else:
+            return ' '.join(map(str,glob.glob('converge.*')))
+
+def test_user(host):
+    for vars in read_playbook(get_playbook()):
+        assert host.user(vars['user']['name']).exists
+        if 'group' in vars['user'].keys():
+            assert host.group(vars['user']['group']).exists
+        else:
+            assert host.group(vars['user']['name']).exists
diff --git a/roles/bastion-ssh-config/defaults/main.yml b/roles/bastion-ssh-config/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d322814e9ce490dee04585a6bac1b7f5ea03f377
--- /dev/null
+++ b/roles/bastion-ssh-config/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+ssh_bastion_confing__name: ssh-bastion.conf
\ No newline at end of file
diff --git a/roles/bastion-ssh-config/molecule/default/converge.yml b/roles/bastion-ssh-config/molecule/default/converge.yml
new file mode 100644
index 0000000000000000000000000000000000000000..54a624705b89ee2e9a4e1203d4f171ac19f12d19
--- /dev/null
+++ b/roles/bastion-ssh-config/molecule/default/converge.yml
@@ -0,0 +1,15 @@
+---
+- name: Converge
+  hosts: all
+  become: true
+  gather_facts: false
+  roles:
+    - role: bastion-ssh-config
+  tasks:
+    - name: Copy config to remote host
+      copy:
+        src: "{{ playbook_dir }}/{{ ssh_bastion_confing__name }}"
+        dest: "{{ ssh_bastion_confing__name }}"
+        owner: "{{ ansible_user }}"
+        group: "{{ ansible_user }}"
+        mode: 0644
diff --git a/roles/bastion-ssh-config/molecule/default/molecule.yml b/roles/bastion-ssh-config/molecule/default/molecule.yml
new file mode 100644
index 0000000000000000000000000000000000000000..1d84db76c136b951c5cefeb094881404e4f0e83d
--- /dev/null
+++ b/roles/bastion-ssh-config/molecule/default/molecule.yml
@@ -0,0 +1,31 @@
+---
+dependency:
+  name: galaxy
+lint: |
+  set -e
+  yamllint -c ../../.yamllint .
+driver:
+  name: vagrant
+  provider:
+    name: libvirt
+platforms:
+  - name: bastion-01
+    box: generic/ubuntu2004
+    cpus: 1
+    memory: 512
+provisioner:
+  name: ansible
+  lint:
+    name: ansible-lint
+  inventory:
+    hosts:
+      all:
+        hosts:
+        children:
+          bastion:
+            hosts:
+              bastion-01:
+verifier:
+  name: testinfra
+  lint:
+    name: flake8
diff --git a/roles/bastion-ssh-config/molecule/default/tests/test_default.py b/roles/bastion-ssh-config/molecule/default/tests/test_default.py
new file mode 100644
index 0000000000000000000000000000000000000000..f98faa4092566765c42014e5fcfe2eb82822c3d2
--- /dev/null
+++ b/roles/bastion-ssh-config/molecule/default/tests/test_default.py
@@ -0,0 +1,34 @@
+import os
+import yaml
+import glob
+import testinfra.utils.ansible_runner
+from ansible.playbook import Playbook
+from ansible.cli.playbook import PlaybookCLI
+
+testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
+    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')
+
+def read_playbook(playbook):
+    cli_args = [os.path.realpath(playbook), testinfra_hosts]
+    cli = PlaybookCLI(cli_args)
+    cli.parse()
+    loader, inventory, variable_manager = cli._play_prereqs()
+
+    pb = Playbook.load(cli.args[0], variable_manager, loader)
+
+    for play in pb.get_plays():
+        yield variable_manager.get_vars(play)
+
+def get_playbook():
+    with open(os.path.realpath(' '.join(map(str,glob.glob('molecule.*')))), 'r') as yamlfile:
+        data = yaml.load(yamlfile, Loader=yaml.FullLoader)
+        if 'playbooks' in data['provisioner'].keys():
+            if 'converge' in data['provisioner']['playbooks'].keys():
+                return data['provisioner']['playbooks']['converge']
+        else:
+            return ' '.join(map(str,glob.glob('converge.*')))
+
+def test_ssh_config(host):
+    for vars in read_playbook(get_playbook()):
+        assert host.file(vars['ssh_bastion_confing__name']).exists
+        assert host.file(vars['ssh_bastion_confing__name']).is_file
diff --git a/roles/bastion-ssh-config/tasks/main.yml b/roles/bastion-ssh-config/tasks/main.yml
index d638e539e4f603919afd4600d2698d2357f85171..a18291b3bf121c7bf689eca39fb89460aecc3160 100644
--- a/roles/bastion-ssh-config/tasks/main.yml
+++ b/roles/bastion-ssh-config/tasks/main.yml
@@ -17,6 +17,6 @@
   delegate_to: localhost
   connection: local
   template:
-    src: ssh-bastion.conf
-    dest: "{{ playbook_dir }}/ssh-bastion.conf"
+    src: "{{ ssh_bastion_confing__name }}.j2"
+    dest: "{{ playbook_dir }}/{{ ssh_bastion_confing__name }}"
     mode: 0640
diff --git a/roles/bastion-ssh-config/templates/ssh-bastion.conf b/roles/bastion-ssh-config/templates/ssh-bastion.conf.j2
similarity index 100%
rename from roles/bastion-ssh-config/templates/ssh-bastion.conf
rename to roles/bastion-ssh-config/templates/ssh-bastion.conf.j2