diff --git a/files/opendsa/.ssh/id_rsa b/files/opendsa/.ssh/id_rsa
new file mode 100755
index 0000000000000000000000000000000000000000..7d3860f75730124c09d6a77e6c24133ede256ec5
--- /dev/null
+++ b/files/opendsa/.ssh/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA1GjnhseuHiZdIpo5vpn8nhX94ZG8PS5LFgyy03VZQigC6NSJ
+G0HeVV5PoiJNsisDh251d/50kALNpNCuiAZqBGw8SuKxIbev8M4hLyy3IMLK9frY
+Ap6cefolxq5d/ZEz46kkrEFCg7oJiqvADtrhPYwz1jcKiTzdL6936POnrKv6Pcq5
+yhCu+cJ9avWUhCuk41tIX0SI7hl0Lm2KtP8jfyBepWc6Lq8E0xVOmwqQtf0flddu
+g8RfuHFTscl8wnyMSGLWa4Ps+Ihu1zsis7bSfGZjdHbxQT382VxemfkzE6FNGTZM
+4CtafhvF3ZuxjIrx658pCA34JSiKe/4Zlm/c3wIDAQABAoIBAES3k3+FBg1299aD
+8n55LsKt9q6NCUr5uQzvGsNSSYgfjaFpcNnCm30ev8CCPISRadjcoWAqj+cvIPxb
+Druu54l6wp6vbAKufFr5NL8gRjZxDlw5xLxEN/c2OBZovruTyCe0xsp7altXSlL0
+cXPc19Wjj/mTYPM5H42XxME2Yl53xUPi8OKrnZzyEJjrcdLeq8a4yusjdJOidW6O
+wXkk/cDvtMeZZWEEu4nBL3vnU7hXvdF90crwNYqScd1lWIriqjfKhEpAvyrlASSW
+tqURkbw0WIYas+jSexLV/bqmgPydsOHJ15TXKtIYUejpPKB5WEdiMECc286fMLCx
+ogVGoZECgYEA/z/bl2/+El3smoEgNv2xpQIyXgKKp19figxm9xJIqbAcN66JAn1R
+EiOFiEy5o33q6Yz1sZ5/n4e3zoj8HiVZHNqkpBtuleZJJ7XBM/NcUH+OUrke+tlV
+q7arNlx9uN6liGp9SfJwKYtUSH13Z3O7sEAt7OX+mzWj527zDROZQHMCgYEA1QjM
+bFkZx83Nj4Qklo+GvgJhq0+Vr4hQd/hLZmgGo3dKZ3McXUmM70A0ETOxyp4zzMep
+Hi5wYa/qdtdVi25S38NExqxFO+JbuXrHoM1XyxzQ3DooV1goaKPybJjoB64zmycx
+D+Ni9MEwMsP1X8kxGQr4A1Nh37sm2hAhWoZCcuUCgYEA7oGG+SyGpjbpfT2nEntf
+4SX6VmndkaPGrEIGfFuzVgvfchA+qfrbJC3Y+pFm7WQde3phokTOUA0LLYxGuQyB
+BjsvmMChRqRWOyrUi2ydGAL4xEeCsTcfnEImHbezKmmxF5UZ2V0WfVtZuBq01hAI
+kxqFT1Vh4TnwG7NKnS9xBg0CgYBouXeMt8xlnXU03PgDj7DkTVVoGqpx7Ofp4gRm
+5jKFP0ozSrIh5dtDbeNqpWf8PAMo4unvLVMPoqP3IeoqreRNnbd8lwk95AvFRWdH
+VEqZTaQa7vgP4AWVUysEWbKOvAMgfYav0c8+lI22FwDTwprBPdQoBmBx1JXH0vAi
+iSe3RQKBgQCPutMegWA+vLupD12XWdde3M5hi4Cd/PxcvJwJrUSjjwlbkHr8EAkZ
+3MXK9o+1Ssj4Ipf1T7VUfdE5uaFJjGLLiPmMAoHbqqPo/0nnsk5JCqJuagmMup0O
+rOQmrvbbDLRXndhb3YeoCtehTiONTMzW+dsdbCrrdnx5AzV4Akwlqw==
+-----END RSA PRIVATE KEY-----
diff --git a/files/opendsa/.ssh/id_rsa.pub b/files/opendsa/.ssh/id_rsa.pub
new file mode 100755
index 0000000000000000000000000000000000000000..10096d594d4b37a8e9633dc021c89ce8b957869a
--- /dev/null
+++ b/files/opendsa/.ssh/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUaOeGx64eJl0imjm+mfyeFf3hkbw9LksWDLLTdVlCKALo1IkbQd5VXk+iIk2yKwOHbnV3/nSQAs2k0K6IBmoEbDxK4rEht6/wziEvLLcgwsr1+tgCnpx5+iXGrl39kTPjqSSsQUKDugmKq8AO2uE9jDPWNwqJPN0vr3fo86esq/o9yrnKEK75wn1q9ZSEK6TjW0hfRIjuGXQubYq0/yN/IF6lZzourwTTFU6bCpC1/R+V126DxF+4cVOxyXzCfIxIYtZrg+z4iG7XOyKzttJ8ZmN0dvFBPfzZXF6Z+TMToU0ZNkzgK1p+G8Xdm7GMivHrnykIDfglKIp7/hmWb9zf opendsa@aes-devel.edu.liu.se
diff --git a/files/opendsa/.ssh/known_hosts b/files/opendsa/.ssh/known_hosts
new file mode 100755
index 0000000000000000000000000000000000000000..8eb1a50ebef29e00ee9ba2f141ec6c849132d61a
--- /dev/null
+++ b/files/opendsa/.ssh/known_hosts
@@ -0,0 +1 @@
+gitlab.ida.liu.se,130.236.180.82 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAYihlTjGKAun+fT/4v1c7zW0+HmsniwuuxWMmHM+/Y1RK/31DyxV+oLpiACP+2cR/FJ5Ab2wGai4sgnrZqY+yDHHZFbTAThqXylJmIvm57U1J8yL1ayOJe7wQNwan13rmEfzBjrNCxn/aFcvwLutZx+sRsYYfFnGhLeULbaoIeysXm+qufL2TQib+GJzanL6uksiccJ9RiWVg7YewzsdP23DzBSZBJobggaX5bIGzVp2omwe0F4X0YgMZvUHBNWJRjbit56c92jirmLaHJNvl3J+xSIty1XaCp/0kg5Ws8jRV9iGDXRafPPcWn2T8p1S4vIYsAD6QH9Ec6hAKT9qn
diff --git a/files/opendsa/manage.sh b/files/opendsa/manage.sh
new file mode 100755
index 0000000000000000000000000000000000000000..32cf02c8ef7bbb8852fce950a47add3393a4ab0b
--- /dev/null
+++ b/files/opendsa/manage.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+if [ $# == 0 ]
+then
+    echo "This script is only intended for managing the server. Please provide one argument as an action!"
+    exit 1
+fi
+cd ~/OpenDSA/server/
+~/OpenDSA/server/main.py "$@"
diff --git a/files/opendsa/opendsa.service b/files/opendsa/opendsa.service
new file mode 100644
index 0000000000000000000000000000000000000000..255d9964184ca9986f802003d670c6c09c972686
--- /dev/null
+++ b/files/opendsa/opendsa.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=OpenDSA Exam server
+After=network.target
+
+[Service]
+Type=simple
+User=opendsa
+WorkingDirectory=/srv/opendsa/OpenDSA/server
+ExecStart=/usr/bin/python3 /srv/opendsa/OpenDSA/server/main.py production 0.0.0.0 12000
+
+[Install]
+WantedBy=multi-user.target
diff --git a/manifests/opendsa.pp b/manifests/opendsa.pp
new file mode 100644
index 0000000000000000000000000000000000000000..e7a80989fb93f06bbdd1dbc30eedfbc501900fbd
--- /dev/null
+++ b/manifests/opendsa.pp
@@ -0,0 +1,91 @@
+class aes::opendsa {
+
+  $opendsa_user = opendsa
+  $opendsa_group = "${opendsa_user}"
+  $opendsa_home = "/srv/${opendsa_user}"
+  $opendsa_service = "${opendsa_user}"
+
+  user { "${opendsa_user}" :
+    ensure => present,
+    home => "${opendsa_home}",
+    comment => 'OpenDSA server',
+    managehome => false,
+    membership => inclusive,
+    system => true,
+  # Do we need login shell
+    shell => '/sbin/nologin',
+  }
+
+  file { "${opendsa_home}":
+    ensure => directory,
+    owner  => "${opendsa_user}",
+    group  => "${opendsa_group}",
+    mode => '0755',
+  }
+
+  file { "${opendsa_home}/.ssh":
+    ensure => directory,
+    recurse => true,
+  # Is modes copied correctly by "recurse" option above? NO
+  # chmod 0700 .ssh/id_rsa
+  # chmod 0744 .ssh/id_rsa.pub .ssh/known_hosts
+    purge => true,
+    force => true,
+    owner  => "${opendsa_user}",
+    group  => "${opendsa_group}",
+    mode => '0700',
+    source => "puppet:///modules/${module_name}/opendsa/.ssh",
+  }
+
+  exec { '/usr/bin/git clone --single-branch --branch exam git@gitlab.ida.liu.se:filst04/OpenDSA.git' : 
+    cwd => "${opendsa_home}",
+    creates => "${opendsa_home}/OpenDSA",
+    user => "${opendsa_user}",
+    group => "${opendsa_group}",
+  }
+
+  exec { '/usr/bin/git pull' : 
+    cwd => "${opendsa_home}/OpenDSA",
+    onlyif => "/bin/test -d ${opendsa_home}/OpenDSA/.git",
+    user => "${opendsa_user}",
+    group => "${opendsa_group}",
+  }
+
+ # Install python packets. Can this be run several times safely? (Idempotent?)
+  exec { '/usr/bin/python3 -m pip install --user -r OpenDSA/server/requirements.txt' : 
+    cwd => "${opendsa_home}",
+    user => "${opendsa_user}",
+    group => "${opendsa_group}",
+  }
+
+  file { "${opendsa_home}/manage.sh":
+    ensure => present,
+    owner  => "${opendsa_user}",
+    group  => "${opendsa_group}",
+    mode => '0751',
+    source => "puppet:///modules/${module_name}/opendsa/manage.sh",
+  }
+
+  exec { "${opendsa_home}/OpenDSA/server/main.py init_db" : 
+    cwd => "${opendsa_home}",
+    creates => "${opendsa_home}/OpenDSA/server/openDSA.db",
+    user => "${opendsa_user}",
+    group => "${opendsa_group}",
+  }
+
+  file { "/etc/systemd/system/${opendsa_service}.service":
+    ensure => present,
+    owner  => root,
+    group  => root,
+    mode => '0644',
+    source => "puppet:///modules/${module_name}/opendsa/opendsa.service",
+  }
+
+ # Do we need port 12000 open?
+  service { "${opendsa_service}" : 
+    ensure => "running",
+  }
+
+  # Ändra TDDD86-raden i config/opendsa till "TDDD86[AB]_191219" ???
+
+}
diff --git a/validate.sh b/validate.sh
new file mode 100755
index 0000000000000000000000000000000000000000..09d5ee5376b1b7bfc189cd7225cbec71be4759b3
--- /dev/null
+++ b/validate.sh
@@ -0,0 +1 @@
+puppet parser validate --strict_variables --modulepath="$(pwd)/.." manifests/