diff --git a/apply.sh b/apply.sh
deleted file mode 100755
index f2812024ef7180341a41e2dcaea56f25af35fe7d..0000000000000000000000000000000000000000
--- a/apply.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-modulepath="$(pwd)/.."
-
-set -x
-
-puppet parser validate --strict_variables --modulepath="$modulepath" manifests/
-
-for file in $*
-do
-    class=$(basename $file .pp)
-    # Sudo is needed for modules that do something with yum for example.
-    sudo /opt/puppetlabs/bin/puppet apply --test --modulepath="$modulepath" -e "include aes::$class"
-done
diff --git a/files/auth/on_update.sh b/files/auth/on_update.sh
index 7a13654e474b977a29cc3f2e1af013a944d5bf46..b347f4d5d581ba2b442cec1e840649e9a6cd6963 100644
--- a/files/auth/on_update.sh
+++ b/files/auth/on_update.sh
@@ -1,6 +1,5 @@
 #!/bin/bash
 
-run_as_broker=$(cat <<'EOF'
 cd
 # To make sure we have a decent GCC in our path.
 source /opt/rh/devtoolset-7/enable
@@ -17,12 +16,3 @@ cd
 mkdir -p bin/
 rm -f bin/auth
 cp src/auth/auth bin/
-EOF
-)
-
-# Compile as the auth user
-sudo --user auth --group auth --set-home -- bash -c "$run_as_broker"
-
-# Then, we can restart the services.
-systemctl restart aes_auth.service
-
diff --git a/files/auth/on_update_keydb.sh b/files/auth/on_update_keydb.sh
deleted file mode 100644
index 3ed0354cff24082abb91955c6e78a5d67b01fa30..0000000000000000000000000000000000000000
--- a/files/auth/on_update_keydb.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-
-systemctl restart aes_auth_keydb.service
-systemctl restart aes_temp_userdb.service
diff --git a/files/broker/on_update.sh b/files/broker/on_update.sh
index 1e6a62c2563fc353ed58fd4c00cb75e0d90d9409..e36a698e2e7f4629bdb1f34174a411593ccc5372 100644
--- a/files/broker/on_update.sh
+++ b/files/broker/on_update.sh
@@ -1,13 +1,12 @@
 #!/bin/bash
 
-run_as_broker=$(cat <<'EOF'
 cd
 # To make sure we have a decent GCC in our path.
-source /opt/rh/devtoolset-7/enable
+source /opt/rh/gcc-toolset-12/enable
 
 # Use a newer Boost.
-export LIBRARY_PATH=/usr/lib64/boost169
-export CPATH=/usr/include/boost169
+export LIBRARY_PATH=/usr/lib64
+export CPATH=/usr/include/boost
 
 cd src/broker
 make clean
@@ -17,12 +16,3 @@ cd
 mkdir -p bin/
 rm -f bin/broker
 cp src/broker/broker bin/
-EOF
-)
-
-# Compile as "broker".
-sudo --user broker --group broker --set-home -- bash -c "$run_as_broker"
-
-# Then, we can restart the services.
-systemctl restart aes_broker.service
-
diff --git a/files/opendsa/on_update.sh b/files/opendsa/on_update.sh
index a2912aae220747d95ecc6e82aec52d83e3c0d9fa..601494a2a79e503072beeea0d40df215a6b6c879 100644
--- a/files/opendsa/on_update.sh
+++ b/files/opendsa/on_update.sh
@@ -3,9 +3,6 @@
 # This file is called whenever the OpenDSA repo was updated. This means we should re-check
 # the requirements.txt file and restart the service.
 
-# Note: This file is executed as root, so we drop back to the opendsa user before starting pip.
-
-update_pip_fn=$(cat <<'EOF'
 cd
 python3 -m pip install --user -r OpenDSA/server/requirements.txt
 
@@ -15,11 +12,3 @@ then
     cd OpenDSA/server/
     ./main.py init_db
 fi
-EOF
-)
-
-# Run PIP as OpenDSA.
-sudo --user opendsa --group opendsa --set-home -- bash -c "$update_pip_fn"
-
-# Then, we can restart the service.
-systemctl service restart opendsa.service
diff --git a/files/squid/squid.conf b/files/squid/squid.conf
index 137bf1dd2409319bce09e1aa2d5af71d1b19a0a6..2bb701ea1509a575606680fb6a3e40f249431bdd 100644
--- a/files/squid/squid.conf
+++ b/files/squid/squid.conf
@@ -131,8 +131,8 @@ always_direct allow all
 ssl_bump server-first all
 
 # Inititate with:
-# /usr/lib64/squid/ssl_crtd -c -s /var/lib/ssl_db
-sslcrtd_program /usr/lib64/squid/ssl_crtd -s /var/lib/squid/ssl_db -M 4MB
+# /usr/lib64/squid/security_file_certgen -c -s /var/lib/ssl_db
+sslcrtd_program /usr/lib64/squid/security_file_certgen -s /var/lib/squid/ssl_db -M 4MB
 sslcrtd_children 32 startup=5 idle=1
 
 # the following two options are unsafe and not always necessary:
diff --git a/files/update_repo.sh b/files/update_repo.sh
deleted file mode 100755
index 210d36dcfbd73a4a35233c223a9c3d77bc342767..0000000000000000000000000000000000000000
--- a/files/update_repo.sh
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/bin/bash
-
-# Keep a Git repository updated. Optionally execute a command whenever it has been updated in order
-# to trigger further processing of some kind (e.g. restarting services).
-
-# The command is called as follows:
-# update_repo.sh <destination path> <source repo> <source branch>
-
-# If REPO_ON_UPDATE is set, then that file will be executed whenever the repo is created and/or
-# updated to a new revision. This can be used to trigger further processing of some sort, such as
-# compiling the contained source and/or restarting system services as appropriate.
-
-# This script is designed to be executed as a privillegied user so that the update command could do
-# privillegied operations. Thus, the git commands will be isolated to a user as described by
-# REPO_USER and REPO_GROUP, if they are is set. It is, of course, also possible to just run this
-# script as a regular user without setting REPO_USER.
-
-
-# "function" that keeps the repo updated. Returns 0 if nothing was done, 100 if the repo was updated,
-# and something else on some kind of error. This function will be executed as the user indicated in
-# the environment variables.
-# I'm sorry for this thing... Turns out that sudo does not allow passing bash functions as environment
-# variables, so I simply pass the entire "function" to Bash as a string.
-update_repo_fn=$(cat <<'EOF'
-if [[ ! -d "$repo_path" ]]
-then
-    # Does not exist. We need to checkout the repository.
-    git clone --single-branch --branch "$repo_branch" "$repo_source" "$repo_path" || exit 1
-    exit 100
-else
-    # It does exist. Make sure it is updated.
-    cd "$repo_path"
-    old_sha=$(git rev-parse HEAD)
-    git fetch -f "$repo_source" "$repo_branch":remotes/origin/"$repo_branch" || exit 1
-    new_sha=$(git rev-parse remotes/origin/"$repo_branch")
-    if [[ "$old_sha" == "$new_sha" ]]
-    then
-	# They are the same, we don't need to do anything.
-	exit 0
-    else
-	# They differ. Check out the new revision.
-	git checkout -f "$new_sha" || exit 1
-	git branch -f "$repo_branch" "$new_sha" || exit 1
-	# This is not strictly necessary, but it makes it look like we have the correct branch
-	# checked out. Good if someone inspects the repo at a later time.
-	git checkout -f "$repo_branch" || exit 1
-	exit 100
-    fi
-fi
-EOF
-)
-
-# Check for enough parameters.
-if [[ "$#" < 3 ]]
-then
-    echo "Not enough parameters."
-    echo "Usage: update_repo.sh <path> <source> <branch>"
-    exit 2
-fi
-
-# Check if the REPO_ON_UPDATE script exists. If it doesn't (probably
-# because Puppet has not created it yet), then we don't want to run
-# yet. If we run without the script being present, then the script
-# will not be executed until the repository is updated, thus breaking
-# our promises.
-if [[ ! -z "$REPO_ON_UPDATE" ]]
-then
-    if [[ ! -f "$REPO_ON_UPDATE" ]]
-    then
-	echo "The script $REPO_ON_UPDATE does not exist."
-	exit 4
-    fi
-fi
-
-# Setup variables for calling "update_repo"...
-repo_path="$1"
-repo_source="$2"
-repo_branch="$3"
-
-export repo_path repo_source repo_branch
-
-if [[ -z "$REPO_USER" ]]
-then
-    # Just run it in a subshell
-    bash -c "$update_repo_fn"
-else
-    # Perhaps group was not supplied.
-    if [[ -z "$REPO_GROUP" ]]
-    then
-	REPO_GROUP="$REPO_USER"
-    fi
-    sudo --preserve-env=repo_path,repo_source,repo_branch,update_repo --set-home --user="$REPO_USER" --group="$REPO_GROUP" -- bash -c "$update_repo_fn"
-fi
-# Note: We cannot put any commands between the if-statement and here. We need the result code from
-# invoking bash, which is the last command in both the if- and else- branches.
-result="$?"
-if [[ $result == 0 ]]
-then
-    # All is well, nothing needed to be done.
-    exit 0
-elif [[ $result == 100 ]]
-then
-    # All is well, but we need to tell the environment that we updated the repo.
-    if [[ ! -z "$REPO_ON_UPDATE" ]]
-    then
-	# Run it if it is there.
-	eval "$REPO_ON_UPDATE"
-    fi
-    exit 0
-else
-    # Something went awry, forward the exit code.
-    exit $result
-fi
diff --git a/git-merge-push-production.sh b/git-merge-push-production.sh
deleted file mode 100755
index 8846ad7f87576fec344137749d9f3d7ccf756624..0000000000000000000000000000000000000000
--- a/git-merge-push-production.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-git_cmds=("checkout production" "merge devel" "push" "checkout devel")
-
-for cmd in "${git_cmds[@]}"
-do
-    if ! git $cmd
-    then
-	exit $?
-    fi
-done
diff --git a/manifests/aes_sw.pp b/manifests/aes_sw.pp
index 15cec34746e7cf86234f7d9f25806fa70026473d..129d44610874cf03c5096f9e35f25fc51869a61c 100644
--- a/manifests/aes_sw.pp
+++ b/manifests/aes_sw.pp
@@ -1,5 +1,5 @@
 # @summary
-#   Describe what this class do!
+#   Software for the AES system.
 #
 #   Detailed summary info if suitable
 #
@@ -8,9 +8,57 @@ class aes::aes_sw {
   $examadm_group = $examadm_user
   $examadm_home = "/home/${examadm_user}"
 
+  case fact('os.name') {
+    'RedHat': {
+      firewalld_custom_service { 'aes-server':
+        description => 'Authentic Examination System server',
+        ports       => [
+          { port => '23431',  protocol => 'tcp' },
+          { port => '23816',  protocol => 'tcp' },
+          { port => '23817',  protocol => 'tcp' },
+        ],
+      }
+
+      @firewalld_rich_rule {
+        default:
+          service => 'aes-server',
+          log     => false;
+
+        'Accept aes-server in LiU networks without logging IPv4':
+          zone   => 'liu',
+          family => 'ipv4',
+          action => 'accept';
+        'Accept aes-server in LiU networks without logging IPv6':
+          zone   => 'liu',
+          family => 'ipv6',
+          action => 'accept';
+      }
+    }
+    'CentOS': {
+      ::server_firewall::rules_file { '45-permit_aes_sw.rules':
+        # lint:ignore:strict_indent heredoc failing...
+        content => @(EOF),
+	service sclogin is tcp/23431
+        service aesmsi is tcp/23816
+        service aesmso is tcp/23817
+
+        policy chain INPUT is
+          accept service:sclogin from class:liu-nets
+          accept service:aesmsi from class:liu-nets
+          accept service:aesmso from class:liu-nets
+        end policy
+        |-EOF
+        # lint:endignore:strict_indent
+      }
+    }
+    default: {
+      fail("${module_name} - Not supported for family ${fact('os.name')}.")
+    }
+  }
+
   package {
     [
-      'a2ps',
+      'enscript', # present in pars_pwd_list.py, but pars_pwd_list.py old and unused?
       'cronie',
       'java-11-openjdk-devel',
     ]:
@@ -138,15 +186,13 @@ class aes::aes_sw {
     enable => true,
   }
 
-  exec { 'script-repo-updated':
-    command => "/opt/utils/update_repo.sh ${examadm_home}/scripts https://oauth2:iAyewr9Jq5E-tnsVrmbj@gitlab.liu.se/examadm/scripts.git master",
-    cwd     => $examadm_home,
-    user    => $examadm_user,
-    group   => $examadm_group,
-  }
-
-  schedule { 'everyday':
-    period => daily,
-    range  => '01:00 - 04:00',
+  # Test to replace exec for repo update //thojo16
+  vcsrepo { "${examadm_home}/scripts":
+    ensure   => latest,
+    provider => git,
+    source   => 'https://oauth2:iAyewr9Jq5E-tnsVrmbj@gitlab.liu.se/examadm/scripts.git',
+    revision => 'master',
+    owner    => $examadm_user,
+    group    => $examadm_group,
   }
 }
diff --git a/manifests/auth.pp b/manifests/auth.pp
index 248720ef119bf12f3133e0360633da868bc7b200..eacb162d31cce551ca10c7e012ba9513698610dc 100644
--- a/manifests/auth.pp
+++ b/manifests/auth.pp
@@ -1,24 +1,19 @@
 # @summary
-#   Describe what this class do!
+#   Authentication service for the communication module.
 #
-#   Detailed summary info if suitable
+#   Sets up the authentication service for the communication module in the
+#   new exam system. Connects to the broker to authenticate users.
 #
 #
 # @param keytab_production_base64
-#   Describe keytab_production_base64
+#   Keytab contents (in base64) for the Kerberos host key used to authenticate
+#   in the production environment (aes.edu.liu.se).
 #
 # @param keytab_devel_base64
-#   Describe keytab_devel_base64
+#   Keytab contents (in base64) for the Kerberos host key used to authenticate
+#   in the development environment (aes-devel.edu.liu.se).
 #
 class aes::auth (
-  # Comment out old keytab params but
-  # keep temporay for comparison
-  #
-  # # Existing keytabs
-  # Optional[String] $keytab_production = undef,
-  # Optional[String] $keytab_devel = undef
-  #
-  # new base64+pkcs7 keytabs
   Optional[String] $keytab_production_base64 = undef,
   Optional[String] $keytab_devel_base64 = undef
 ) {
@@ -34,16 +29,21 @@ class aes::auth (
   # Pick the right keytab for the current environment. We use the fqdn rather than 
   # $environment since the keys are tied to the domain name rather than what 
   # environment the machine is configured in.
-  if $facts[fqdn] == 'aes.edu.liu.se' {
+  if $facts[networking][fqdn] == 'aes.edu.liu.se' {
     # The AD service account for this key is: ida_sys002_srv
     $auth_keytab_data = $keytab_production
     $server_type = 'production'
-  } elsif $facts[fqdn] == 'aes-devel.edu.liu.se' {
+  } elsif $facts[networking][fqdn] == 'aes-devel.edu.liu.se' {
+    # The AD service account for this key is: ida_sys004_srv
+    $auth_keytab_data = $keytab_devel
+    $server_type = 'devel'
+  } elsif $facts[networking][fqdn] == 'aes-sbox.it.liu.se' {
     # The AD service account for this key is: ida_sys004_srv
     $auth_keytab_data = $keytab_devel
     $server_type = 'devel'
   } else {
     $auth_keytab_data = undef
+    $server_type = 'devel'
   }
 
   # Note: We rely on Boost being installed by the broker. It seems Puppet does not like
@@ -92,9 +92,9 @@ class aes::auth (
 
   file { "${auth_home}/on_update.sh" :
     ensure => file,
-    owner  => root,
-    group  => root,
-    mode   => '0700',
+    owner  => $auth_user,
+    group  => $auth_group,
+    mode   => '0755',
     source => "puppet:///modules/${module_name}/auth/on_update.sh",
   }
 
@@ -131,14 +131,26 @@ class aes::auth (
     }
   }
 
-  exec { 'update-auth-repo' :
-    command     => "/opt/utils/update_repo.sh ${auth_home}/src https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git ${server_type}",
-    environment => ["REPO_USER=${auth_user}", "REPO_GROUP=${auth_group}", "REPO_ON_UPDATE=${auth_home}/on_update.sh"],
-    # This command will need to run "on_update" as root in order to restart the service.
-    user        => root,
-    group       => root,
+  vcsrepo { "${auth_home}/src":
+    ensure   => latest,
+    provider => git,
+    source   => 'https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git',
+    revision => $server_type,
+    owner    => $auth_user,
+    group    => $auth_group,
+    notify   => Exec['compile-auth-repo'],
+  }
+
+  exec { 'compile-auth-repo':
+    user        => $auth_user,
+    group       => $auth_group,
     cwd         => $auth_home,
+    path        => '/bin:/usr/bin',
+    environment => ["HOME=${auth_home}"],
+    command     => "${auth_home}/on_update.sh",
     require     => File["${auth_home}/on_update.sh"],
+    creates     => "${auth_home}/bin/auth",
+    notify      => Service[$auth_service],
   }
 
   service { $auth_service :
diff --git a/manifests/auth_keydb.pp b/manifests/auth_keydb.pp
index 5bde484935cd53ffe2e218a3ee094acc8d8b34a4..f71e86bae746707e824d4792cf5de839bca693f3 100644
--- a/manifests/auth_keydb.pp
+++ b/manifests/auth_keydb.pp
@@ -1,7 +1,8 @@
 # @summary
-#   Describe what this class do!
+#   Key database for the communication module.
 #
-#   Detailed summary info if suitable
+#   Stores authentication keys (=SSH keys) for cases where Kerberos
+#   is not a suitable authentication method.
 #
 #
 class aes::auth_keydb {
@@ -11,9 +12,9 @@ class aes::auth_keydb {
   $keydb_service = 'aes_auth_keydb'
 
   # Figure out which certificate to use based on the hostname.
-  if $facts[fqdn] == 'aes.edu.liu.se' {
+  if $facts[networking][fqdn] == 'aes.edu.liu.se' {
     $server_type = 'production'
-  } elsif $facts[fqdn] == 'aes-devel.edu.liu.se' {
+  } elsif $facts[networking][fqdn] == 'aes-devel.edu.liu.se' {
     $server_type = 'devel'
   } else {
     $server_type = undef
@@ -45,22 +46,14 @@ class aes::auth_keydb {
     source => "puppet:///modules/${module_name}/auth/keydb.service",
   }
 
-  file { "${keydb_home}/on_update.sh" :
-    ensure => file,
-    owner  => root,
-    group  => root,
-    mode   => '0700',
-    source => "puppet:///modules/${module_name}/auth/on_update_keydb.sh",
-  }
-
-  exec { 'update-keydb-repo' :
-    command     => "/opt/utils/update_repo.sh ${keydb_home}/src https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git ${server_type}",
-    environment => ["REPO_USER=${keydb_user}", "REPO_GROUP=${keydb_group}", "REPO_ON_UPDATE=${keydb_home}/on_update.sh"],
-    # This command will need to run "on_update" as root in order to restart the service.
-    user        => root,
-    group       => root,
-    cwd         => $keydb_home,
-    require     => File["${keydb_home}/on_update.sh"],
+  vcsrepo { "${keydb_home}/src":
+    ensure   => latest,
+    provider => git,
+    source   => 'https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git',
+    revision => $server_type,
+    owner    => $keydb_user,
+    group    => $keydb_group,
+    notify   => [Service[$keydb_service], Service['aes_temp_userdb']],
   }
 
   service { $keydb_service :
diff --git a/manifests/broker.pp b/manifests/broker.pp
index 3a093332f110217a9ff26eb5f313e39903219e51..4dfa689593333a4b8be043b229ecb130a0c6e195 100644
--- a/manifests/broker.pp
+++ b/manifests/broker.pp
@@ -1,7 +1,8 @@
 # @summary
-#   Describe what this class do!
+#   Message broker for the communication module.
 #
-#   Detailed summary info if suitable
+#   Sets up the message broker for the communication module in the
+#   new exam system.
 #
 #
 class aes::broker {
@@ -10,20 +11,72 @@ class aes::broker {
   $broker_home = "/srv/${broker_user}"
   $broker_service = 'aes_broker'
 
-  # Sadly, it does not seem like we can not only install asio, so we need
-  # to install the Boost as a whole.
-  package {
-    [
-      'boost169',
-      'boost169-devel',
-    ]:
-      ensure => installed,
+  case fact('os.name') {
+    'RedHat': {
+      package {
+        [
+          'boost',
+          'boost-devel',
+        ]:
+          ensure => installed,
+      }
+
+      firewalld_custom_service { 'aes-broker':
+        description => 'Authentic Examination System communication broker',
+        ports       => [
+          { port => '31337',  protocol => 'tcp' },
+        ],
+      }
+
+      @firewalld_rich_rule {
+        default:
+          service => 'aes-broker',
+          log     => false;
+
+        'Accept aes-broker in LiU networks without logging IPv4':
+          zone   => 'liu',
+          family => 'ipv4',
+          action => 'accept';
+        'Accept aes-broker in LiU networks without logging IPv6':
+          zone   => 'liu',
+          family => 'ipv6',
+          action => 'accept';
+      }
+    }
+    'CentOS': {
+      # Sadly, it does not seem like we can not only install asio, so we need
+      # to install the Boost as a whole.
+      package {
+        [
+          'boost169',
+          'boost169-devel',
+        ]:
+          ensure => installed,
+      }
+
+      ::server_firewall::rules_file { '45-permit_aes_broker.rules':
+        # lint:ignore:strict_indent heredoc failing...
+        content => @(EOF),
+        service aesbroker is tcp/31337
+        
+        policy chain INPUT is
+          accept service:aesbroker from class:liu-nets
+        end policy
+        |-EOF
+        # lint:endignore:strict_indent
+      }
+    }
+    default: {
+      fail("${module_name} - Not supported for family ${fact('os.name')}.")
+    }
   }
 
   # Figure out which certificate to use based on the hostname.
-  if $facts[fqdn] == 'aes.edu.liu.se' {
+  if $facts[networking][fqdn] == 'aes.edu.liu.se' {
     $server_type = 'production'
-  } elsif $facts[fqdn] == 'aes-devel.edu.liu.se' {
+  } elsif $facts[networking][fqdn] == 'aes-devel.edu.liu.se' {
+    $server_type = 'devel'
+  } elsif $facts[networking][fqdn] == 'aes-sbox.it.liu.se' {
     $server_type = 'devel'
   } else {
     $server_type = undef
@@ -56,9 +109,9 @@ class aes::broker {
 
   file { "${broker_home}/on_update.sh" :
     ensure => file,
-    owner  => root,
-    group  => root,
-    mode   => '0700',
+    owner  => $broker_user,
+    group  => $broker_group,
+    mode   => '0755',
     source => "puppet:///modules/${module_name}/broker/on_update.sh",
   }
 
@@ -93,14 +146,26 @@ class aes::broker {
     source => "puppet:///modules/${module_name}/broker/cert/${server_type}_password",
   }
 
-  exec { 'update-broker-repo' :
-    command     => "/opt/utils/update_repo.sh ${broker_home}/src https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git ${server_type}",
-    environment => ["REPO_USER=${broker_user}", "REPO_GROUP=${broker_group}", "REPO_ON_UPDATE=${broker_home}/on_update.sh"],
-    # This command will need to run "on_update" as root in order to restart the service.
-    user        => root,
-    group       => root,
+  vcsrepo { "${broker_home}/src":
+    ensure   => latest,
+    provider => git,
+    source   => 'https://oauth2:F-agHaRXCdyFy38q4c-N@gitlab.liu.se/upp-aes/communication.git',
+    revision => $server_type,
+    owner    => $broker_user,
+    group    => $broker_group,
+    notify   => Exec['compile-broker-repo'],
+  }
+
+  exec { 'compile-broker-repo':
+    user        => $broker_user,
+    group       => $broker_group,
     cwd         => $broker_home,
+    path        => '/bin:/usr/bin',
+    environment => ["HOME=${broker_home}"],
+    command     => "${broker_home}/on_update.sh",
     require     => File["${broker_home}/on_update.sh"],
+    creates     => "${broker_home}/bin/broker",
+    notify      => Service[$broker_service],
   }
 
   service { $broker_service :
diff --git a/manifests/init.pp b/manifests/init.pp
index 6f3529e453441a5a39ee74c89c34fbc3897b7664..660f39131cbb6e080ee26b591988f1ee6a7335d7 100644
--- a/manifests/init.pp
+++ b/manifests/init.pp
@@ -14,18 +14,42 @@ class aes {
   include aes::broker
   include aes::auth
   include aes::auth_keydb
-  include liurepo::centos_sclo_rh
 
-  package {
-    [
-      'devtoolset-7',
-      'gcc',
-      'gcc-c++',
-      'libaio',
-      'python36',
-    ]:
-      ensure  => installed,
-      require => Class['liurepo::centos_sclo_rh'],
+  case fact('os.name') {
+    'RedHat': {
+      # TODO: liurepo::centos_sclo_rh ???
+      # TODO: Move this to the subclass that actually require it?
+      package {
+        [
+          'gcc-toolset-12',
+          'gcc',
+          'gcc-c++',
+          'libaio',
+          'python3',
+          'python3-pip',
+          'python3-ldap',
+        ]:
+          ensure  => installed,
+      }
+    }
+    'CentOS': {
+      include liurepo::centos_sclo_rh
+      # TODO: Move this to the subclass that actually require it?
+      package {
+        [
+          'devtoolset-7',
+          'gcc',
+          'gcc-c++',
+          'libaio',
+          'python36',
+        ]:
+          ensure  => installed,
+          require => Class['liurepo::centos_sclo_rh'],
+      }
+    }
+    default: {
+      fail("${module_name} - Not supported for family ${fact('os.name')}.")
+    }
   }
 
   file { '/etc/sudoers.d/aes':
@@ -44,37 +68,16 @@ class aes {
     content => file("${module_name}/anacrontab"),
   }
 
-  # File for updating repositories.
-  file { '/opt/utils':
-    ensure => directory,
-    mode   => '0755',
-    owner  => root,
-    group  => root,
-  }
-  file { '/opt/utils/update_repo.sh':
-    ensure  => file,
-    mode    => '0755',
-    owner   => root,
-    group   => root,
-    content => file("${module_name}/update_repo.sh"),
-  }
-
-  # File to easily see when Puppet was last executed.
-  # Ideally, we would like to know if it is devel or production as well.
-  exec { '/usr/bin/touch /var/last_puppet_run' :
-    cwd   => '/var',
-    user  => root,
-    group => root,
-  }
-
-  # File containing which environment is used.
-  file { '/var/puppet_environment' :
-    ensure  => file,
-    mode    => '0644',
-    owner   => root,
-    group   => root,
-    content => $environment,
-  }
+  # 2023-10-03: Is this used for anything? What breaks?
+  #
+  # # File containing which environment is used.
+  # file { '/var/puppet_environment' :
+  #   ensure  => file,
+  #   mode    => '0644',
+  #   owner   => root,
+  #   group   => root,
+  #   content => $environment,
+  # }
 
   ::users::liu_user { 'klaar36':
     commonname => 'Klas Arvidsson',
@@ -111,24 +114,4 @@ class aes {
     shell      => '/bin/bash',
     sshkey     => 'AAAAB3NzaC1yc2EAAAADAQABAAABAQCsUKr53aCwErzsdhD/5oEQ4gWW51NgXa70Ow20Fnv/pyKAepDsIMCOB6kf1aET8LOlnq8Wyu0/52GGB38mO6cUzi7MLeWj7bg1Npq7b5/Uoaquq/dginoVQDc5RuJfmoy7PwmjKep/J2OIkCs8kD4sKbqN3ArCW555hgBvlGCdHxER1x2c5wGc2iuMCcbsfonOfORIxzCoiF4igfmuA1wpFZgyjBLuHn/SOtp85pD3nR0JSiaJWcMLB7IkWzXxvbpUWhDf7/gE4mwCDkOajY8zdG+aLkAZI0J1TJUGq50zji4OouwxxPW2JhpVl1KbRPqec+pVtdQIZstgUg3YbJGl', # lint:ignore:140chars
   }
-
-  ::server_firewall::rules_file { '45-permit_squid.rules':
-    # lint:ignore:strict_indent heredoc failing...
-    content => @(EOF),
-    service squid is tcp/3128
-    service sclogin is tcp/23431
-    service aesmsi is tcp/23816
-    service aesmso is tcp/23817
-    service aesbroker is tcp/31337
-
-    policy chain INPUT is
-      accept service:squid from class:liu-nets
-      accept service:sclogin from class:liu-nets
-      accept service:aesmsi from class:liu-nets
-      accept service:aesmso from class:liu-nets
-      accept service:aesbroker from class:liu-nets
-    end policy
-    |-EOF
-    # lint:endignore:strict_indent
-  }
 }
diff --git a/manifests/latex.pp b/manifests/latex.pp
index 02092261dd54f71948c26bd09240488d7c97948f..46a6f27111431f91f8ce45d07d09a19d48628240 100644
--- a/manifests/latex.pp
+++ b/manifests/latex.pp
@@ -1,5 +1,5 @@
 # @summary
-#   Describe what this class do!
+#   Install required LaTeX packages for PDF generation.
 #
 #   Detailed summary info if suitable
 #
@@ -13,7 +13,7 @@ class aes::latex {
       'texlive-collection-xetex',
       'texlive-collection-latex',
       'texlive-collection-latexrecommended',
-      'texlive-xetex-def',
+#      'texlive-xetex-def', # Not in RHEL9, why is this required? What breaks?
       'texlive-tcolorbox',
       'texlive-booktabs',
       'latexmk',
diff --git a/manifests/opendsa.pp b/manifests/opendsa.pp
index 0dc64cfe1ec99c8183509de9541203bcd914f898..58f8c329cbffe5aae2bc9d553dbe353f3cd4d118 100644
--- a/manifests/opendsa.pp
+++ b/manifests/opendsa.pp
@@ -1,5 +1,5 @@
 # @summary
-#   Describe what this class do!
+#   OpenDSA server for exams.
 #
 #   Detailed summary info if suitable
 #
@@ -30,9 +30,9 @@ class aes::opendsa {
   # This file will be executed as root, which is why we don't let anyone but root examine it.
   file { "${opendsa_home}/on_update.sh":
     ensure => file,
-    owner  => root,
-    group  => root,
-    mode   => '0700',
+    owner  => $opendsa_user,
+    group  => $opendsa_group,
+    mode   => '0755',
     source => "puppet:///modules/${module_name}/opendsa/on_update.sh",
   }
 
@@ -44,14 +44,27 @@ class aes::opendsa {
     source => "puppet:///modules/${module_name}/opendsa/opendsa.service",
   }
 
-  exec { 'update-repo':
-    command     => "/opt/utils/update_repo.sh ${opendsa_home}/OpenDSA https://oauth2:taNPRZid9Hv6jJtdW_T8@gitlab.liu.se/opendsa/OpenDSA.git exam",
-    environment => ["REPO_USER=${opendsa_user}", "REPO_GROUP=${opendsa_group}", "REPO_ON_UPDATE=${opendsa_home}/on_update.sh"],
-    # This command will need to run "on_update" as root in order to restart the service.
-    user        => root,
-    group       => root,
+  vcsrepo { "${opendsa_home}/OpenDSA":
+    ensure     => latest,
+    provider   => git,
+    submodules => false,
+    source     => 'https://oauth2:taNPRZid9Hv6jJtdW_T8@gitlab.liu.se/opendsa/OpenDSA.git',
+    revision   => 'exam',
+    owner      => $opendsa_user,
+    group      => $opendsa_group,
+    notify     => Exec['update-opendsa-repo'],
+  }
+
+  exec { 'update-opendsa-repo':
+    user        => $opendsa_user,
+    group       => $opendsa_group,
     cwd         => $opendsa_home,
+    path        => '/bin:/usr/bin',
+    environment => ["HOME=${opendsa_home}"],
+    command     => "${opendsa_home}/on_update.sh",
     require     => File["${opendsa_home}/on_update.sh"],
+    notify      => Service[$opendsa_service],
+    refreshonly => true,
   }
 
   file { "${opendsa_home}/manage.sh":
diff --git a/manifests/squid_filter.pp b/manifests/squid_filter.pp
index 071d583849619ead9e299a3d70d55a6fa42cb71f..1ab820998b3dba741f99ea7ae6cfc1cab4ceaec7 100644
--- a/manifests/squid_filter.pp
+++ b/manifests/squid_filter.pp
@@ -2,9 +2,60 @@
 #   Describe what this class do!
 #
 #   Detailed summary info if suitable
+#   TODO: certificat generation and management
 #
 #
 class aes::squid_filter {
+  case fact('os.name') {
+    'RedHat': {
+      firewalld_custom_service { 'squid':
+        description => 'Squid proxy for filtered internet access',
+        ports       => [
+          { port => '3128',  protocol => 'tcp' },
+        ],
+      }
+
+      @firewalld_rich_rule {
+        default:
+          service => 'squid',
+          log     => false;
+
+        'Accept squid in LiU networks without logging IPv4':
+          zone   => 'liu',
+          family => 'ipv4',
+          action => 'accept';
+        'Accept squid in LiU networks without logging IPv6':
+          zone   => 'liu',
+          family => 'ipv6',
+          action => 'accept';
+      }
+    }
+    'CentOS': {
+      ::server_firewall::rules_file { '45-permit_squid.rules':
+        # lint:ignore:strict_indent heredoc failing...
+        content => @(EOF),
+        service squid is tcp/3128
+
+        policy chain INPUT is
+          accept service:squid from class:liu-nets
+        end policy
+        |-EOF
+        # lint:endignore:strict_indent
+      }
+
+      # ensure new name exist to match new config file
+      file { '/usr/lib64/squid/security_file_certgen':
+        ensure => link,
+        owner  => root,
+        group  => root,
+        target => '/usr/lib64/squid/ssl_crtd',
+      }
+    }
+    default: {
+      fail("${module_name} - Not supported for family ${fact('os.name')}.")
+    }
+  }
+
   package { 'squid' :
     ensure => 'present',
   }
@@ -59,7 +110,7 @@ class aes::squid_filter {
     group  => squid,
   }
 
-  exec { '/usr/lib64/squid/ssl_crtd -c -s /var/lib/squid/ssl_db' :
+  exec { '/usr/lib64/squid/security_file_certgen -c -s /var/lib/squid/ssl_db -M 4MB' :
     user    => 'squid',
     group   => 'squid',
     creates => '/var/lib/squid/ssl_db',
diff --git a/manifests/tal_cli.pp b/manifests/tal_cli.pp
index 530af22eeef3722a1caba428ef10523eb40ae0ac..0ee5b074c389a01d5cc75a904bd10d9bea7b16b8 100644
--- a/manifests/tal_cli.pp
+++ b/manifests/tal_cli.pp
@@ -1,11 +1,11 @@
 # @summary
-#   Describe what this class do!
+#   Command-line interface to TAL.
 #
-#   Detailed summary info if suitable
+#   Clones and compiles repo to provide TAL access.
 #
 #
 # @param credentials
-#   Describe credentials
+#   Credentials for TAL access.
 #
 class aes::tal_cli (
   Optional[String] $credentials = undef
@@ -17,6 +17,13 @@ class aes::tal_cli (
     mode   => '0700',
   }
 
+  file { '/home/examadm/bin' :
+    ensure => directory,
+    owner  => examadm,
+    group  => examadm,
+    mode   => '0755',
+  }
+
   file { '/home/examadm/bin/tal-cli' :
     ensure => file,
     owner  => examadm,
@@ -49,12 +56,26 @@ class aes::tal_cli (
     source => "puppet:///modules/${module_name}/tal/on_update.sh",
   }
 
-  exec { 'update-tal-repo' :
-    command     => '/opt/utils/update_repo.sh /home/examadm/tal-cli/source https://oauth2:glpat-bfpVssm_zFmt1YRW7cLz@gitlab.liu.se/upp-aes/tal-cli.git master',
-    environment => ['REPO_ON_UPDATE=/home/examadm/tal-cli/on_update.sh'],
+  vcsrepo { '/home/examadm/tal-cli/source':
+    ensure     => latest,
+    provider   => git,
+    submodules => false,
+    source     => 'https://oauth2:glpat-bfpVssm_zFmt1YRW7cLz@gitlab.liu.se/upp-aes/tal-cli.git',
+    revision   => master,
+    owner      => examadm,
+    group      => examadm,
+    notify     => Exec['compile-tal-repo'],
+  }
+
+  exec { 'compile-tal-repo':
     user        => examadm,
     group       => examadm,
     cwd         => '/home/examadm/tal-cli',
+    path        => '/bin:/usr/bin',
+    environment => ['HOME=/home/examadm/tal-cli'],
+    creates     => '/home/examadm/tal-cli/source/tal',
+    command     => '/home/examadm/tal-cli/on_update.sh',
+    require     => File['/home/examadm/tal-cli/on_update.sh'],
   }
 
   file { '/etc/cron.daily/tal-remind' :
diff --git a/metadata.json b/metadata.json
index 23148c9bfc8837015abd7dcbe0549304dca3b79b..ff778d798145c5897b7c8a4021dfffdbfc74a853 100644
--- a/metadata.json
+++ b/metadata.json
@@ -10,7 +10,6 @@
   "dependencies": [
 
   ],
-  "data_provider": "hiera",
   "operatingsystem_support": [
     {
       "operatingsystem": "CentOS",
diff --git a/puppet.sh b/puppet.sh
deleted file mode 100755
index 592eaaf03646d5e851e308989f8ed3a8b8c47900..0000000000000000000000000000000000000000
--- a/puppet.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-exec sudo /opt/puppetlabs/bin/puppet $*
diff --git a/scripts/eyaml_encrypt.sh b/scripts/eyaml_encrypt.sh
new file mode 100644
index 0000000000000000000000000000000000000000..69235037f4b7ac6bb611a8bea6c3a5392b8f4675
--- /dev/null
+++ b/scripts/eyaml_encrypt.sh
@@ -0,0 +1,3 @@
+#! /bin/sh -eu
+
+exec eyaml encrypt --pkcs7-public-key "puppet_public_key.pkcs7.pem" "$@"
diff --git a/scripts/puppet_public_key.pkcs7.pem b/scripts/puppet_public_key.pkcs7.pem
new file mode 100644
index 0000000000000000000000000000000000000000..48e5ee496c78f2b6eb2b56c999578d5129f42041
--- /dev/null
+++ b/scripts/puppet_public_key.pkcs7.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2TCCAcGgAwIBAgIBATANBgkqhkiG9w0BAQUFADAAMCAXDTE1MDYwMzA5MDUx
+MloYDzIwNjUwNTIxMDkwNTEyWjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAwh8REuonjpax1B3egJEHQ6FnrVPEhS8P1PftDVFlYrlpiualq2RX8ynz
+ZRigwRYqrFFpPJNooK1gXaFttpAMffUM7mFVPdXC3Tg4nihYncOOxGT3GqrbC7Oa
+GfXeUoEI7PIbRJhmcH/fJLLcsYrdIYyqferTgBGlVKbK2dSpqL9FGQCigcrmbalh
+3ZpIKKfmejZELNHY/7Mun1Gseoin5yuYMTGzI9xEmxBIEQzKpIJWrgvyfPs80ch3
+WTjufePl0PqlaVLKR8qk6H23LQMw0DcBjN+Dm1wG7kWIBK4CxHN7TSHPebiEwutH
+UG56w+2HKuf0J/loYRSQklcmlogNJQIDAQABo1wwWjAPBgNVHRMBAf8EBTADAQH/
+MB0GA1UdDgQWBBTvBDJEU5YW0kgqu2YI0HVwNchshDAoBgNVHSMEITAfgBTvBDJE
+U5YW0kgqu2YI0HVwNchshKEEpAIwAIIBATANBgkqhkiG9w0BAQUFAAOCAQEAdA8j
+RGF2rFXrGeOzurP6/1G1Yvi3adN9Adxnhe0ZKCYfsCzd+Ttuli11IGUWDeOsxTgf
+QkezGo6FPsyhv79yGMP5IOkToIXVyOeeGjQRDytRVAq2Q3dQa3/9xhabA88NfzdR
+S+VChWUWLgIKKtrrShiusGdvewpmo5lKvYNGTvmJchXPED9kXNJC8nmxBwcqk9fJ
+eUVmXyiMdvEcwHzzjZN8n0F9vRNAA9r0w2GeP5Bg5Ggxqldfnyt4TpBbcacdJLLt
+DWKsYYuI7wLPTTmXvMdAm/eC7zvrCLP9wMw1wN6Sh/SpG+CLiStJNTFigqu9vkmO
+4qJa9Cmm6QrZ6aGieQ==
+-----END CERTIFICATE-----
diff --git a/scripts/update_now.sh b/scripts/update_now.sh
new file mode 100644
index 0000000000000000000000000000000000000000..724a5b5940dd9beff382b7da66a20636818300a4
--- /dev/null
+++ b/scripts/update_now.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/bash
+
+sudo /opt/puppetlabs/bin/puppet agent --test
diff --git a/validate.sh b/validate.sh
deleted file mode 100755
index 63c0eeda4afa2894f74119fbe2efe621f539c189..0000000000000000000000000000000000000000
--- a/validate.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-modulepath="$(pwd)/.."
-
-set -x
-
-puppet parser validate --strict_variables --modulepath="$modulepath" manifests/
-
-for file in $*
-do
-    class=$(basename $file .pp)
-    # Sudo is needed for modules that do something with yum for example.
-    sudo /opt/puppetlabs/bin/puppet apply --noop --test --modulepath="$modulepath" -e "include aes::$class"
-done