From 6dcc3a66a145a95322482ded87a6d726b1b18555 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Sachse?= <joerg.sachse@slub-dresden.de>
Date: Tue, 13 Sep 2022 16:49:02 +0200
Subject: [PATCH] feat: implements Gitlab-CI

---
 .ansible-lint                                 |  45 +++---
 .config/molecule/config.yml                   |   2 +
 .gitignore                                    |  14 ++
 .gitlab-ci.yml                                |  35 ++++
 .gitlab/issue_templates/Default.md            |  16 ++
 .gitlab/merge_request_templates/Default.md    |   7 +
 .yamllint                                     |  32 ++--
 CODE-OF-CONDUCT.md                            |   3 +
 CONTRIBUTING.md                               |   0
 LICENSE                                       | 153 ++++++++++--------
 README.md                                     |  96 ++++++++---
 ansible.cfg                                   |   2 +-
 handlers/main.yml                             |  17 +-
 kudos.txt                                     |  48 ++++++
 meta/main.yml                                 |  50 +++---
 molecule/README.md                            | 139 +++++++++-------
 molecule/default                              |   1 +
 molecule/default/converge.yml                 |   5 -
 molecule/default/molecule.yml                 |  38 -----
 molecule/default/prepare.yml                  |  40 -----
 molecule/default/tests/conftest.py            |  20 ---
 molecule/default/tests/test_default.py        |   6 -
 .../playbooks}/INSTALL.rst                    |   6 +-
 molecule/resources/playbooks/README.md        |   3 +
 molecule/resources/playbooks/converge.yml     |  17 ++
 molecule/resources/playbooks/prepare.yml      |  56 +++++++
 molecule/resources/playbooks/verify.yml       |  10 ++
 molecule/virtualbox/molecule.yml              |  41 +++++
 requirements.yml                              |  10 --
 site.yml                                      |   2 -
 tasks/compile_callas_binaries.yml             |  53 ++++--
 tasks/configure_apache.yml                    |  27 +++-
 tasks/configure_crontab.yml                   |   4 +-
 tasks/configure_iptables.yml                  |   4 +-
 tasks/configure_local_mounts.yml              |   4 +-
 tasks/configure_nfs_mounts.yml                |   5 +-
 tasks/create_users_groups.yml                 |   8 +-
 tasks/install_callas_pdf_engine.yml           |  74 +++++----
 tasks/install_converters.yml                  |   2 +-
 tasks/install_fonts.yml                       |   6 +-
 tasks/install_packages.yml                    |   7 +-
 tasks/install_validators.yml                  |   2 +-
 tasks/main.yml                                |  30 ++--
 43 files changed, 713 insertions(+), 427 deletions(-)
 create mode 100644 .config/molecule/config.yml
 create mode 100644 .gitlab-ci.yml
 create mode 100644 .gitlab/issue_templates/Default.md
 create mode 100644 .gitlab/merge_request_templates/Default.md
 create mode 100644 CODE-OF-CONDUCT.md
 create mode 100644 CONTRIBUTING.md
 create mode 100644 kudos.txt
 create mode 120000 molecule/default
 delete mode 100644 molecule/default/converge.yml
 delete mode 100644 molecule/default/molecule.yml
 delete mode 100644 molecule/default/prepare.yml
 delete mode 100644 molecule/default/tests/conftest.py
 delete mode 100644 molecule/default/tests/test_default.py
 rename molecule/{default => resources/playbooks}/INSTALL.rst (84%)
 create mode 100644 molecule/resources/playbooks/README.md
 create mode 100644 molecule/resources/playbooks/converge.yml
 create mode 100644 molecule/resources/playbooks/prepare.yml
 create mode 100644 molecule/resources/playbooks/verify.yml
 create mode 100644 molecule/virtualbox/molecule.yml
 delete mode 100644 requirements.yml

diff --git a/.ansible-lint b/.ansible-lint
index 9c9323e..f18a647 100644
--- a/.ansible-lint
+++ b/.ansible-lint
@@ -6,7 +6,7 @@
 # and not relative to the CWD of execution. CLI arguments passed to the --exclude
 # option will be parsed relative to the CWD of execution.
 exclude_paths:
-  - .cache/ # implicit unless exclude_paths is defined in config
+  - .cache/    # implicit unless exclude_paths is defined in config
   - .git/
   - .githooks/
   - backups/
@@ -15,24 +15,19 @@ exclude_paths:
 # verbosity: 1
 
 # Mock modules or roles in order to pass ansible-playbook --syntax-check
-#mock_modules:
-#  - zuul_return
-#  # note the foo.bar is invalid as being neither a module or a collection
-#  - fake_namespace.fake_collection.fake_module
-#  - fake_namespace.fake_collection.fake_module.fake_submodule
-#mock_roles:
-#  - mocked_role
-#  - author.role_name # old standalone galaxy role
-#  - fake_namespace.fake_collection.fake_role # role within a collection
+# mock_modules:
+#   - zuul_return
+#   # note the foo.bar is invalid as being neither a module or a collection
+#   - fake_namespace.fake_collection.fake_module
+#   - fake_namespace.fake_collection.fake_module.fake_submodule
+# mock_roles:
+#   - mocked_role
+#   - author.role_name # old standalone galaxy role
+#   - fake_namespace.fake_collection.fake_role # role within a collection
 
 # Enable checking of loop variable prefixes in roles
 loop_var_prefix: "{role}_"
 
-# Enforce variable names to follow pattern below, in addition to Ansible own
-# requirements, like avoiding python identifiers. To disable add `var-naming`
-# to skip_list.
-var_naming_pattern: "^[a-z_][a-z0-9_]*$"
-
 use_default_rules: true
 # Load custom rules from this specific folder
 # rulesdir:
@@ -46,9 +41,9 @@ skip_list:
 # Any rule that has the 'opt-in' tag will not be loaded unless its 'id' is
 # mentioned in the enable_list:
 enable_list:
-  - empty-string-compare # opt-in
-  - no-log-password # opt-in
-  - no-same-owner # opt-in
+  - empty-string-compare    # opt-in
+  - no-log-password         # opt-in
+  - no-same-owner           # opt-in
   # add yaml here if you want to avoid ignoring yaml checks when yamllint
   # library is missing. Normally its absence just skips using that rule.
   - yaml
@@ -60,19 +55,19 @@ enable_list:
 warn_list:
   - skip_this_tag
   - git-latest
-  - experimental # experimental is included in the implicit list
+  - experimental    # experimental is included in the implicit list
   # - role-name
 
 # Offline mode disables installation of requirements.yml
 offline: false
 
 # Define required Ansible's variables to satisfy syntax check
-#extra_vars:
-#  foo: bar
-#  multiline_string_variable: |
-#    line1
-#    line2
-#  complex_variable: ":{;\t$()"
+# extra_vars:
+#   foo: bar
+#   multiline_string_variable: |
+#     line1
+#     line2
+#   complex_variable: ":{;\t$()"
 
 # Uncomment to enforce action validation with tasks, usually is not
 # needed as Ansible syntax check also covers it.
diff --git a/.config/molecule/config.yml b/.config/molecule/config.yml
new file mode 100644
index 0000000..ece7ff6
--- /dev/null
+++ b/.config/molecule/config.yml
@@ -0,0 +1,2 @@
+---
+prerun: false
diff --git a/.gitignore b/.gitignore
index 2e89c4b..ed78332 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,8 @@ Thumbs.db
 
 *.retry
 *.vault
+inventory.*
+inv.*
 
 # Vim #
 #######
@@ -72,3 +74,15 @@ tags
 
 .vagrant/
 *.box
+
+# Temporary/Build/Backup #
+##########################
+
+backups/
+build/
+
+# CONFIDENTIAL #
+################
+
+ssh_host_*
+
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..c763e79
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,35 @@
+---
+# A pipeline is composed of independent jobs that run scripts, grouped into stages.
+# Stages run in sequential order, but jobs within stages run in parallel.
+#
+# For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages
+
+stages:          # List of stages for jobs, and their order of execution
+  - test
+
+default:
+  before_script:
+    - source /opt/molecule/bin/activate
+    - ansible --version
+    - molecule --version
+  after_script:
+    - source /opt/molecule/bin/activate
+    - molecule destroy --scenario-name default
+
+test-job:
+  stage: test
+  tags:
+    - "shell"
+  script:
+    # make sure that Ansible Vaults are present and can be decrypted
+    - echo "${VAULT_VALIDATORS}" > ../lza_validators.pass
+    - export ANSIBLE_VAULT_IDENTITY_LIST="../lza_validators.pass"
+    - rm -rf ../ansible_vaults/
+    - git clone https://gitlab+deploy-token-25:${VAULT_ACCESS_TOKEN}@git.slub-dresden.de/slub-referat-2-3/ansible_vaults.git ../ansible_vaults/; \
+    # run Molecule tests
+    - molecule syntax --scenario-name default
+    - molecule lint --scenario-name default
+    - molecule create --scenario-name default
+    - molecule converge --scenario-name default
+    - molecule idempotence --scenario-name default
+    # - molecule verify --scenario-name default
diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md
new file mode 100644
index 0000000..acc60bf
--- /dev/null
+++ b/.gitlab/issue_templates/Default.md
@@ -0,0 +1,16 @@
+## Expected Behavior
+
+
+## Actual Behavior
+
+
+## Steps to Reproduce the Problem
+
+  1.
+  1.
+  1.
+
+## Specifications
+
+  - Version/Commit:
+  - Platform:
diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md
new file mode 100644
index 0000000..4030f6f
--- /dev/null
+++ b/.gitlab/merge_request_templates/Default.md
@@ -0,0 +1,7 @@
+Fixes #
+
+## Proposed Changes
+
+  -
+  -
+  -
diff --git a/.yamllint b/.yamllint
index 7c0f15c..8827676 100644
--- a/.yamllint
+++ b/.yamllint
@@ -1,7 +1,5 @@
 ---
-# based on documentation available at
-# https://yamllint.readthedocs.io/en/stable/rules.html
-
+# Based on ansible-lint config
 extends: default
 
 rules:
@@ -11,13 +9,25 @@ rules:
   brackets:
     max-spaces-inside: 1
     level: error
-  comments:
-    min-spaces-from-content: 4
+  colons:
+    max-spaces-after: -1
+    level: error
+  commas:
+    max-spaces-after: -1
+    level: error
+  comments: disable
   comments-indentation: disable
-  document-end: disable
-  document-start:
-    level: warning
-  octal-values:
-    forbid-explicit-octal: false
+  document-start: disable
+  empty-lines:
+    max: 3
+    level: error
+  hyphens:
+    level: error
+  indentation: disable
+  key-duplicates: enable
   line-length: disable
-  truthy: enable
+  new-line-at-end-of-file: disable
+  new-lines:
+    type: unix
+  trailing-spaces: disable
+  truthy: disable
diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md
new file mode 100644
index 0000000..19d2d20
--- /dev/null
+++ b/CODE-OF-CONDUCT.md
@@ -0,0 +1,3 @@
+# Code of Conduct
+
+Just be excellent to each other. That's it. EoC.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e69de29
diff --git a/LICENSE b/LICENSE
index fe0a078..4ba7851 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
-                    GNU AFFERO GENERAL PUBLIC LICENSE
-                       Version 3, 19 November 2007
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
  Everyone is permitted to copy and distribute verbatim copies
@@ -7,15 +7,17 @@
 
                             Preamble
 
-  The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
 
   The licenses for most software and other practical works are designed
 to take away your freedom to share and change the works.  By contrast,
-our General Public Licenses are intended to guarantee your freedom to
+the GNU General Public License is intended to guarantee your freedom to
 share and change all versions of a program--to make sure it remains free
-software for all its users.
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
 
   When we speak of free software, we are referring to freedom, not
 price.  Our General Public Licenses are designed to make sure that you
@@ -24,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
 want it, that you can change the software or use pieces of it in new
 free programs, and that you know you can do these things.
 
-  Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
-  A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate.  Many developers of free software are heartened and
-encouraged by the resulting cooperation.  However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
-  The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community.  It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server.  Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
-  An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals.  This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
 
   The precise terms and conditions for copying, distribution and
 modification follow.
@@ -60,7 +72,7 @@ modification follow.
 
   0. Definitions.
 
-  "This License" refers to version 3 of the GNU Affero General Public License.
+  "This License" refers to version 3 of the GNU General Public License.
 
   "Copyright" also means copyright-like laws that apply to other kinds of
 works, such as semiconductor masks.
@@ -537,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
 the Program, the only way you could satisfy both those terms and this
 License would be to refrain entirely from conveying the Program.
 
-  13. Remote Network Interaction; Use with the GNU General Public License.
-
-  Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software.  This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
+  13. Use with the GNU Affero General Public License.
 
   Notwithstanding any other provision of this License, you have
 permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
+under version 3 of the GNU Affero General Public License into a single
 combined work, and to convey the resulting work.  The terms of this
 License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
 
   14. Revised Versions of this License.
 
   The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time.  Such new versions
-will be similar in spirit to the present version, but may differ in detail to
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
 address new problems or concerns.
 
   Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU Affero General
+Program specifies that a certain numbered version of the GNU General
 Public License "or any later version" applies to it, you have the
 option of following the terms and conditions either of that numbered
 version or of any later version published by the Free Software
 Foundation.  If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
+GNU General Public License, you may choose any version ever published
 by the Free Software Foundation.
 
   If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
+versions of the GNU General Public License can be used, that proxy's
 public statement of acceptance of a version permanently authorizes you
 to choose that version for the Program.
 
@@ -629,33 +631,44 @@ to attach them to the start of each source file to most effectively
 state the exclusion of warranty; and each file should have at least
 the "copyright" line and a pointer to where the full notice is found.
 
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) 2021  Jörg Sachse
+    ansible_lza_validators
+    Copyright (C) 2022  Digital Preservation
 
     This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU Affero General Public License as published
-    by the Free Software Foundation, either version 3 of the License, or
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
     (at your option) any later version.
 
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU Affero General Public License for more details.
+    GNU General Public License for more details.
 
-    You should have received a copy of the GNU Affero General Public License
+    You should have received a copy of the GNU General Public License
     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 Also add information on how to contact you by electronic and paper mail.
 
-  If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source.  For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code.  There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) 2022  Digital Preservation
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
 
   You should also get your employer (if you work as a programmer) or school,
 if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
+For more information on this, and how to apply and follow the GNU GPL, see
 <https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/README.md b/README.md
index 78a4d5a..ed81366 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
-# Ansible-Role "ansible_lza_validators"
+# Ansible-Role "<ROLE_NAME>"
 
-## Description
+## What does it do?
 
-This role provides all necessary tasks to setup a file validation server for SLUBArchiv.
+This role provides all necessary tasks to <DO THING>.
 
-## Prerequisites
+## What do I need?
+
+### Prerequisites
 
 To use this role, the following software must be installed on your workstation:
 * ansible
@@ -12,30 +14,34 @@ To use this role, the following software must be installed on your workstation:
 To deploy this role to a managed host, the following software must be installed on the target:
 * Python3
 * SSHd
+* <INSERT MORE HERE IF NEEDED>
+
+### Dependencies
 
 Other roles required by this role can be easily installed using `ansible-galaxy` if necessary. As the roles reside in SLUB's local Git server instance, you might need a password for certain repositories despite most of them being publicly available. This command will install the required roles in the correct location so they are found in the search path:
 ```
 	ansible-galaxy install -r requirements.yml -p ../
 ```
 
-It is recommended to use Debian VMs as deployed by SLUB's UDA tool with this role.
+### Infrastructure
+
+It is recommended to use Debian VMs as deployed by SLUB's GUBS tool with this role. Otherwise you will not have access to the software packages that are located in SLUB's private Debian package repository.
 
-## Quick Start
+## Can i have a Quick Start?
 
-Most options already have sensible defaults in `ansible.cfg`. However, you can override these defaults using CLI options/flags if you want to.
+Most options already have sensible defaults in `ansible.cfg`. However, you can override these defaults using CLI options/flags if you want to. The flags can be combined if necessary, and most of them have long versions as well. Get more information using `ansible-playbook --help`.
 
 To simply run the playbook, just call the `site.yml` playbook like this:
+```bash
+ansible-playbook site.yml -u <username>
 ```
-	ansible-playbook site.yml -u <username>
-```
-
 If you want to limit the execution to a subset of all hosts that are listed in the inventory, use the `-l` or `--limit` option like this:
-```
-	ansible-playbook site.yml -l <hostna*>
-	ansible-playbook site.yml -l <hostname>
-	ansible-playbook site.yml -l <hostname1>:<hostname2>:...
-	ansible-playbook site.yml -l <inventory_group>
-	ansible-playbook site.yml --limit=<hostna*>
+```bash
+ansible-playbook site.yml -l <hostna*>
+ansible-playbook site.yml -l <hostname>
+ansible-playbook site.yml -l <hostname1>:<hostname2>:...
+ansible-playbook site.yml -l <inventory_group>
+ansible-playbook site.yml --limit=<hostna*>
 ```
 
 If you do not have Vault password files in the directory above the role direcory, you have to give the Vault password before execution:
@@ -43,6 +49,12 @@ If you do not have Vault password files in the directory above the role direcory
 	ansible-playbook site.yml --ask-vault-pass
 ```
 
+You can use your own inventory file by adding the `-i` or `--inventory=INVENTORY` option:
+```
+	ansible-playbook site.yml -i inventory.yml
+	ansible-playbook site.yml --inventory=inventory.yml
+```
+
 Tasks in this role have been tagged to enable users to only run subsets of tasks. This can be leveraged to decrease run times or run only certain tasks after small changes.
 To list all available tags, use:
 ```
@@ -54,20 +66,62 @@ You can then run only certain tagged tasks by using the `--tags` option:
 	ansible-playbook site.yml --tags=tag1,tag2,...,tagN
 ```
 
-## Testing the role
+## How can I run tests?
 
 Tests have been implemented using the Molecule framework. The details on using the test suite are described below `molecule/`.
 
-## Variables
+To run some quick tests, you can do:
+```bash
+# pure syntax check
+molecule syntax
+# run yamllint and ansible-lint
+molecule lint
+# list available test scenarios, e.g. based on different OS images or platforms
+molecule list
+# create the test environment for a specific scenario (can be left out for "default")
+molecule create [-s scenario]
+# run your tasks against the test env
+molecule converge [-s scenario]      # multiple times if needed
+# run idempotence checks to se if any of the tasks keeps changing (subsequent runs shouldn't trigger changes)
+molecule idempotence [-s scenario]
+# cleanup test env (remove VM/container)
+molecule destroy [-s scenario]
+```
+
+We recommend running those tests before pushing any code to the Git server.
+
+On every `git push`, the GitLab-CI pipeline will run a similar set of tests to ensure that all changes are working. Find the details in the `.gitlab-ci.yml` file located at the project root directory.
+
+## What can be configured?
+
+### Ansible Role
 
 Many variables have been "hidden" in encrypted Ansible Vaults. For security reasons, these Vaults are maintained in a separate private internal repository of SLUB's Git. However, in order to better understand the data within the vaults, you can find `\*.vault.example` files below the `vars/` directory.
 
 If you work outside of SLUBArchive and have no access to the vault repository, make sure to put the necessary vaults in the expected paths at `../ansible_vaults/<ROLENAME>/`.
 
-## git configuration
+Variable defaults have been set in `defaults/main.yml`. You can overwrite them with your own values by setting them in `vars/main.yml`.
+
+### Git configuration
 
 Just run the `setup_gitconfig.sh` script that comes with the repo to correctly setup all necessary local Git configurations.
 
-## Author Information
+## What changes have been made lately?
+
+All changes can be found in the `CHANGELOG` file located at the project root directory. Alternatively, you can have a look at the commit log to get a detailed view.
+
+## Who is maintaining this project?
+
+All authors/maintainers are listed in the `kudos.txt` file located at the project root directory.
+
+## How can I contribute?
+
+If you have any comments or find bugs, please contact langzeitarchiv@slub-dresden.de, create an issue or send us a pull request.
+
+Details on how to contribute to this project can be found at the `CONTRIBUTING.md` file located at the project root directory.
+
+If you have commited to the project yourself, you can leave a note in the `kudos.txt` file located at the project root directory. Be assured of our eternal gratitude.
+
+## Is there a Code of Conduct?
 
-If you have any comments or find bugs, please contact langzeitarchiv@slub-dresden.de or issue a pull request.
+Yes there is. You can find it in the `CODE-OF-CONDUCT.md` file located at the project root directory. It's kept very brief by design.
diff --git a/ansible.cfg b/ansible.cfg
index 15a0f1d..5b1e462 100644
--- a/ansible.cfg
+++ b/ansible.cfg
@@ -1,7 +1,7 @@
 [defaults]
 # If set, configures the path to the Vault password file as an alternative to
 # specifying --vault-password-file on the command line.
-vault_identity_list = ../lza_ingest.pass, ../slub_osquery.pass
+vault_identity_list = ../lza_validators.pass
 
 # Path to default inventory file
 # Administrators can override this by using the "-i <inventoryfile>" CLI
diff --git a/handlers/main.yml b/handlers/main.yml
index 8239c47..291417a 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -1,30 +1,33 @@
 ---
 - name: Ordner für iptables-Config erstellen
-  file:
+  ansible.builtin.file:
     path: "/etc/iptables"
     state: directory
     owner: "root"
     group: "root"
-    mode: 0755
+    mode: "0755"
   listen: "save iptables rules"
 
 - name: install netfilter-persistent to be able to save iptables rules
-  apt:
+  ansible.builtin.apt:
     name: "netfilter-persistent"
     state: present
   listen: "save iptables rules"
 
 - name: save iptables rules
-  command: "netfilter-persistent save"
+  ansible.builtin.command: "netfilter-persistent save"
   listen: "save iptables rules"
+  changed_when: false
 
 - name: restart apache
-  systemd:
+  ansible.builtin.systemd:
     name: "apache2"
     state: restarted
 
 - name: run ldconfig to configure dynamic linker run-time bindings
-  command: "ldconfig /usr/lib/cgi-bin/lib"
+  ansible.builtin.command: "ldconfig /usr/lib/cgi-bin/lib"
+  changed_when: false
 
 - name: run fc-cache to rebuild font information cache files and add applefonts
-  command: "fc-cache --force --verbose --system-only"
+  ansible.builtin.command: "fc-cache --force --verbose --system-only"
+  changed_when: false
diff --git a/kudos.txt b/kudos.txt
new file mode 100644
index 0000000..968b76f
--- /dev/null
+++ b/kudos.txt
@@ -0,0 +1,48 @@
+# kudos.txt - Express gratitude to your contributors.
+# https://github.com/kudos-txt
+
+project:
+  - name: ansible_lza_validators
+    site: https://git.slub-dresden.de/digital-preservation/ansible_lza_validators
+#    blog: <blog url>
+#    help: <support url>
+#    news: <news url>, <news feed url>
+#    mail: <mail@domain.tld>
+#    chat: <service:nick>, <service:channel>, <url>
+#    note: <free text notes>
+
+contributor:
+#  - name: <name or nick of the contributor>
+#    role: <role in the project>
+#    site: <website url>
+#    blog: <blog url>
+#    mail: <mail@domain.tld>
+#    chat: <service:nick>, <service:channel>, <url>
+#    home: <country>, <region>, <zip>, <city>, <address>
+#    work: <company>, <job title>, <job description>
+#    note: <free text notes>
+  - name: Jörg Sachse
+    role: Maintainer
+    mail: Joerg.Sachse@slub-dresden.de
+    home: Germany, Saxony, 01059, Dresden, Zellescher Weg 18
+    work: SLUB Dresden, Digital Preservationist & IT Administrator & Installation Wizard
+
+#partner:
+#  - name: <name of the partner>
+#    site: <website url>
+#    blog: <blog url>
+#    help: <support url>
+#    news: <news url>, <news feed url>
+#    mail: <mail@domain.tld>
+#    chat: <service:nick>, <service:channel>, <url>
+#    note: <free text notes>
+
+#software:
+#  - name: <name of the software>
+#    site: <website url>
+#    blog: <blog url>
+#    help: <support url>
+#    news: <news url>, <news feed url>
+#    mail: <mail@domain.tld>
+#    chat: <service:nick>, <service:channel>, <url>
+#    note: <free text notes>
diff --git a/meta/main.yml b/meta/main.yml
index 37d455e..ffcfaca 100644
--- a/meta/main.yml
+++ b/meta/main.yml
@@ -1,18 +1,30 @@
 ---
 galaxy_info:
-  author: Jörg Sachse
-  description: role to install validation server for the SLUBarchiv digital preservation repository
+  author: Jörg Sachse (<Joerg.Sachse@slub-dresden.de>)
   company: SLUB Dresden
-  role_name: ansible_lza_validators  # if absent directory name hosting role is used instead
-  namespace: SLUBarchiv_digital      # if absent, author is used instead
-  # If the issue tracker for your role is not on github, uncomment the next line and provide a value issue_tracker_url: http://example.com/issue/tracker Some suggested licenses: - BSD
-  # (default) - MIT - GPLv2 - GPLv3 - Apache - CC-BY
-  license: public domain
-  min_ansible_version: 2.8
-  # If this a Container Enabled role, provide the minimum Ansible Container version. min_ansible_container_version: Optionally specify the branch Galaxy will use when accessing the GitHub repo
-  # for this role. During role install, if no tags are available, Galaxy will use this branch. During import Galaxy will access files on this branch. If Travis integration is configured, only
-  # notifications for this branch will be accepted. Otherwise, in all cases, the repo's default branch (usually master) will be used. github_branch:
-  #
+  description: role to install validation server for the SLUBarchiv digital preservation repository
+  galaxy_tags: []
+    # List tags for your role here, one per line. A tag is a keyword that describes and categorizes the role. Users find roles by searching for tags. Be sure to remove the '[]' above, if you
+    # add tags to this list.
+    #
+    # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
+    #       Maximum 20 tags per role.
+  # issue_tracker_url: "https://example.com/"
+    # If the issue tracker for your role is not on github, uncomment the next line and provide a value issue_tracker_url: http://example.com/issue/tracker
+
+    # Choose a valid license ID from https://spdx.org - some suggested licenses:
+    # - BSD-3-Clause (default)
+    # - MIT
+    # - GPL-2.0-or-later
+    # - GPL-3.0-only
+    # - Apache-2.0
+    # - CC-BY-4.0
+  license: GPL-3.0-or-later
+
+  min_ansible_version: "2.5"
+    # If this a Container Enabled role, provide the minimum Ansible Container version. min_ansible_container_version: Optionally specify the branch Galaxy will use when accessing the GitHub repo
+    # for this role. During role install, if no tags are available, Galaxy will use this branch. During import Galaxy will access files on this branch. If Travis integration is configured, only
+    # notifications for this branch will be accepted. Otherwise, in all cases, the repo's default branch (usually master) will be used. github_branch:
   # Provide a list of supported platforms, and for each platform a list of versions. If you don't wish to enumerate all versions for a particular platform, use 'all'. To view available
   # platforms and versions (or releases), visit: https://galaxy.ansible.com/api/v1/platforms/
   #
@@ -21,14 +33,6 @@ galaxy_info:
   platforms:
     - name: Debian
       versions:
-        - 9
-        - 10
-  galaxy_tags: []
-  # List tags for your role here, one per line. A tag is a keyword that describes and categorizes the role. Users find roles by searching for tags. Be sure to remove the '[]' above, if you
-  # add tags to this list.
-  #
-  # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
-  #       Maximum 20 tags per role.
-dependencies:
-# - ansible_lza_install_common
-# - ansible_lza_server_hardening
+        - "buster"
+        - "bullseye"
+dependencies: []
diff --git a/molecule/README.md b/molecule/README.md
index 33a7eb5..fdb41fe 100644
--- a/molecule/README.md
+++ b/molecule/README.md
@@ -4,39 +4,53 @@
 
 In order to be able to use the tests, you need to have some software packages installed. You may need sudo privileges for some of these operations.
 
-        ### install VirtualBox
-        # do NOT use distribution packages
-        # process documented at https://www.virtualbox.org/wiki/Linux_Downloads
-        #
-        # add repository URL
-        sudo echo "deb [arch=amd64] https://download.virtualbox.org/virtualbox/debian stretch contrib" > /etc/apt/sources.d/virtualbox.list
-        # add GPG key
-        wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -
-        # update sources
-        sudo apt update
-        # install VirtualBox
-        sudo apt-get install virtualbox-6.1
-
-        ### install Vagrant
-        # do NOT use distribution packages
-        # 
-        # download Debian package from Hashicorp
-        wget https://releases.hashicorp.com/vagrant/2.2.9/vagrant_2.2.9_x86_64.deb
-        # install package
-        sudo dpkg -i vagrant_2.2.9_x86_64.deb
-
-        ### install Molecule et. al.
-        # prepare directories
-        mkdir ~/python-envs/ && cd ~/python-env/
-        # create Python Virtual Environment with Python3 interpreter (Python2 is deprecated!)
-        virtualenv -p python3 molecule-env
-        # enter the Virtual Environment in your current shell (other shells will be unaffected)
-        source molecule-env/bin/activate
-        # install packages
-        pip3 install molecule ansible testinfra ansible-lint molecule-vagrant molecule-docker
-
-        # leave the Virtual Environment only when you're done
-        deactivate
+There are two ways to accomplish this.
+
+### Option 1: use `molecule-skel` (SLUB staff only)
+
+1. Clone the project at [molecule-skel](https://git.slub-dresden.de/slub-referat-2-3/molecule-skel): `git clone https://git.slub-dresden.de/slub-referat-2-3/molecule-skel.git`
+2. Switch to the `molecule-skel` repository directory and run `./install_testenv.sh`.
+3. PROFIT!
+
+ Check the `README.md` over at [molecule-skel](https://git.slub-dresden.de/slub-referat-2-3/molecule-skel) for details on how to use that. It's easy, I promise.
+
+### Option 2: manual installation
+
+Run these steps on your terminal.
+
+```bash
+### install VirtualBox
+# do NOT use distribution packages, as they may be too old!
+# process documented at https://www.virtualbox.org/wiki/Linux_Downloads
+#
+# add GPG key
+wget -q https://www.virtualbox.org/download/oracle_vbox.asc -O- | sudo apt-key add -
+# add repository URL
+sudo echo "deb [arch=amd64] https://download.virtualbox.org/virtualbox/debian stretch contrib" > /etc/apt/sources.d/virtualbox.list
+# update sources and install VirtualBox
+sudo apt update; sudo apt-get install virtualbox-6.1
+
+### install Vagrant
+# do NOT use distribution packages, as they may be too old!
+# 
+# download Debian package from Hashicorp
+wget https://releases.hashicorp.com/vagrant/2.2.9/vagrant_2.2.9_x86_64.deb
+# install package
+sudo dpkg -i vagrant_2.2.9_x86_64.deb
+
+### install Molecule et. al.
+# prepare directories
+mkdir ~/python-envs/ && cd ~/python-env/
+# create Python Virtual Environment with Python3 interpreter (Python2 is deprecated!)
+virtualenv -p python3 molecule-env
+# enter the Virtual Environment in your current shell (other shells will be unaffected)
+source molecule-env/bin/activate
+# install packages
+pip3 install molecule ansible testinfra ansible-lint molecule-vagrant molecule-docker
+
+# leave the Virtual Environment only when you're done
+deactivate
+```
 
 You can find suitable documentation at the respective vendors' websites.
 * [Vagrant Installation Guide](https://www.vagrantup.com/docs/installation/)
@@ -46,10 +60,14 @@ You can find suitable documentation at the respective vendors' websites.
 ## Initialising a new Molecule scenario
 
 If you have already created a role without the Molecule test framework or you want to add test scenarios, you can use:
-	molecule init scenario --scenario-name <my_scenario> --driver [azure|delegated|docker|ec2|gce|linode|lxc|lxd|openstack|vagrant] --verifier-name [ansible|testinfra]
+```bash
+molecule init scenario --scenario-name <my_scenario> --driver [azure|delegated|docker|ec2|gce|linode|lxc|lxd|openstack|vagrant] --verifier-name [ansible|testinfra]
+```
 
 If you need any help with the options, please use:
-	molecule init role --help
+```bash
+molecule init role --help
+```
 
 ## Running Tests
 
@@ -60,26 +78,31 @@ Various test environments are separated into so-called "scenarios" that can be b
 In the simplest configuration, the `molecule/` directory only contains one `default/` directory that contains the default scenario. This scenario is run if no other scenario is chosen using the `-s` CLI option.
 
 This is the basic usage of Molecule:
-	# create test infrastructure
-	cd <role_directory>
-	molecule create
-	# run playbooks against test infrastructure
-	molecule converge
-	# run Testinfra tests
-	molecule verify
-	# remove test infrastructure
-	molecule destroy
-
-	# run all steps at once:
-	molecule test
-
-It has proven helpful to use Vagrant to create a snapshot of the VM after the creation phase has completed.
-	# First, get UUID of the VM
-	vagrant global-status
-	# Then, create the snapshot
-	vagrant snapshot save <uuid> <snapshot_name>
-	# To restore the snapshot, use
-	vagrant snapshot restore <uuid> <snapshot_name>
-	# And to remove the snapshot, use
-	vagrant snapshot delete <uuid> <snapshot_name>
-
+```bash
+# create test infrastructure
+cd <role_directory>
+molecule create
+# run playbooks against test infrastructure
+molecule converge
+# run idempotence tests
+molecule idempotence
+# run tests, if they exist
+molecule verify
+# remove test infrastructure
+molecule destroy
+
+# run all steps at once:
+molecule test
+```
+
+It has proven helpful to use Vagrant to create a snapshot of the VM after the creation phase has completed. Just like this:
+```bash
+# First, get UUID of the VM
+vagrant global-status
+# Then, create the snapshot
+vagrant snapshot save <uuid> <snapshot_name>
+# To restore the snapshot, use
+vagrant snapshot restore <uuid> <snapshot_name>
+# And to remove the snapshot, use
+vagrant snapshot delete <uuid> <snapshot_name>
+```
diff --git a/molecule/default b/molecule/default
new file mode 120000
index 0000000..3841ab1
--- /dev/null
+++ b/molecule/default
@@ -0,0 +1 @@
+./virtualbox
\ No newline at end of file
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
deleted file mode 100644
index 8db7e45..0000000
--- a/molecule/default/converge.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-- name: Converge
-  hosts: all
-  roles:
-    - {role: "ansible_lza_validators", become: true}
diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml
deleted file mode 100644
index c916ee2..0000000
--- a/molecule/default/molecule.yml
+++ /dev/null
@@ -1,38 +0,0 @@
----
-dependency:
-  name: galaxy
-driver:
-  name: vagrant
-  provider:
-    name: virtualbox
-lint: |
-  set -e
-  yamllint .
-  ansible-lint -x formatting
-  flake8 --ignore=E501
-platforms:
-  - name: molecule-test-validators
-    box: debian/buster64
-    memory: 2048
-    cpus: 1
-    #    provider_override_args:
-    #      - disksize.size = '20GB'
-provisioner:
-  name: ansible
-  log: true
-  config_options:
-    defaults:
-      vault_identity_list: "@$HOME/.ansible/roles/molecule_prepare.pass, @$HOME/.ansible/roles/lza_install_common.pass, @$HOME/.ansible/roles/lza_server_hardening.pass, @$HOME/.ansible/roles/lza_validators.pass"
-  lint: |
-    set -e
-    ansible-lint
-  vvv: false
-verifier:
-  name: testinfra
-  env:
-    PYTHONWARNINGS: "ignore:.*U.*mode is deprecated:DeprecationWarning"
-  lint: |
-    set- e
-    flake8
-  options:
-    v: 1
diff --git a/molecule/default/prepare.yml b/molecule/default/prepare.yml
deleted file mode 100644
index d903f56..0000000
--- a/molecule/default/prepare.yml
+++ /dev/null
@@ -1,40 +0,0 @@
----
-- name: Prepare
-  hosts: all
-  gather_facts: true
-  pre_tasks:
-    - name: include vars
-      include_vars: "../../../ansible_vaults/molecule_prepare/{{ item }}"
-      loop:
-        - "prepare.vault"
-    - name: Install python for Ansible
-      raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal)
-      become: true
-      changed_when: false
-    - name: create users (as deployed in production)
-      user:
-        name: "{{ item.name }}"
-        uid: "{{ item.uid }}"
-        create_home: "yes"
-        shell: "/bin/bash"
-      loop: "{{ vault_molecule_users | flatten(levels=1) }}"
-      become: true
-    - name: add nonfree repos
-      apt_repository:
-        repo: "deb http://ftp2.de.debian.org/debian/ buster main non-free contrib"
-        state: present
-        update-cache: "yes"
-      become: true
-    - name: Install required packages
-      apt:
-        name: [
-          'aptitude',
-          'gpg',
-          'less',
-          'libuser'
-        ]
-        state: present
-      become: true
-  roles:
-    - {role: ansible_lza_install_common, become: true}
-    - {role: ansible_lza_server_hardening, become: true}
diff --git a/molecule/default/tests/conftest.py b/molecule/default/tests/conftest.py
deleted file mode 100644
index ba0f1e8..0000000
--- a/molecule/default/tests/conftest.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""PyTest Fixtures."""
-from __future__ import absolute_import
-import os
-import pytest
-
-
-def pytest_runtest_setup(item):
-    """Run tests only when under molecule with testinfra installed."""
-    try:
-        import testinfra
-    except ImportError:
-        pytest.skip("Test requires testinfra", allow_module_level=True)
-    if "MOLECULE_INVENTORY_FILE" in os.environ:
-        pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
-            os.environ["MOLECULE_INVENTORY_FILE"]
-        ).get_hosts("all")
-    else:
-        pytest.skip(
-            "Test should run only from inside molecule.", allow_module_level=True
-        )
diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py
deleted file mode 100644
index 39da80a..0000000
--- a/molecule/default/tests/test_default.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def test_hosts_file(host):
-    f = host.file('/etc/hosts')
-
-    assert f.exists
-    assert f.user == 'root'
-    assert f.group == 'root'
diff --git a/molecule/default/INSTALL.rst b/molecule/resources/playbooks/INSTALL.rst
similarity index 84%
rename from molecule/default/INSTALL.rst
rename to molecule/resources/playbooks/INSTALL.rst
index 4f44b67..0c4bf5c 100644
--- a/molecule/default/INSTALL.rst
+++ b/molecule/resources/playbooks/INSTALL.rst
@@ -1,6 +1,6 @@
-*******
+*********************************
 Vagrant driver installation guide
-*******
+*********************************
 
 Requirements
 ============
@@ -20,4 +20,4 @@ widely recommended `'--user' flag`_ when invoking ``pip``.
 
 .. code-block:: bash
 
-    $ pip install 'molecule[vagrant]'
+    $ pip install 'molecule_vagrant'
diff --git a/molecule/resources/playbooks/README.md b/molecule/resources/playbooks/README.md
new file mode 100644
index 0000000..0c91883
--- /dev/null
+++ b/molecule/resources/playbooks/README.md
@@ -0,0 +1,3 @@
+This drectory contains shared playbooks and a shared Dockerfile.
+
+Visit https://molecule.readthedocs.io/en/latest/examples.html#sharing-across-scenarios for details on sharing playbooks, tests etc. across multiple scenarios.
diff --git a/molecule/resources/playbooks/converge.yml b/molecule/resources/playbooks/converge.yml
new file mode 100644
index 0000000..6bc06ca
--- /dev/null
+++ b/molecule/resources/playbooks/converge.yml
@@ -0,0 +1,17 @@
+---
+- name: Converge
+  hosts: all
+  pre_tasks:
+    - name: update apt cache
+      ansible.builtin.apt:
+        update_cache: true
+        upgrade: dist
+      become: true
+      when: ansible_os_family == "Debian"
+    - name: update yum cache
+      ansible.builtin.yum:
+        update_cache: true
+      become: true
+      when: ansible_os_family == "RedHat"
+  roles:
+    - {role: "ansible_lza_validators", become: true}
diff --git a/molecule/resources/playbooks/prepare.yml b/molecule/resources/playbooks/prepare.yml
new file mode 100644
index 0000000..b8915be
--- /dev/null
+++ b/molecule/resources/playbooks/prepare.yml
@@ -0,0 +1,56 @@
+---
+- name: Prepare
+  hosts: "*"
+  gather_facts: true
+  pre_tasks:
+    - name: configure additional package repositories for Debian
+      block: 
+      - name: add 'contrib' repos to default Debian repos
+        ansible.builtin.replace:
+          path: "/etc/apt/sources.list"
+          regexp: " main$"
+          replace: " main contrib"
+        become: true
+      - name: install GPG
+        ansible.builtin.apt:
+          name: "gnupg"
+          state: latest
+          update_cache: true
+        become: true
+      - name: add GPG key for SLUB Debian repository
+        ansible.builtin.apt_key:
+          url: "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
+          state: present
+        become: true
+      - name: add repo URL to sources.list
+        ansible.builtin.apt_repository:
+          repo: "deb https://sdvdebianrepo.slub-dresden.de/deb-repository bullseye main"
+          state: present
+          update_cache: true
+          mode: "0644"
+        become: true
+      when: ansible_os_family == "Debian"
+
+    - name: configure additional package repositories for RedHat
+      block:
+      - name: add custom repositories
+        ansible.builtin.yum_repository:
+          name: "{{ item.name }}"
+          description: "{{ item.description }}"
+          baseurl: "{{ item.baseurl }}"
+          gpgcheck: "{{ item.gpgcheck | default('true') }}"
+          gpgkey: "{{ item.gpgkey | default(omit) }}"
+        loop:
+          - name: "epel"
+            description: EPEL YUM repo
+            baseurl: "https://download.fedoraproject.org/pub/epel/{{ ansible_distribution_major_version }}/x86_64/"
+            gpgkey: "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}"
+          - name: "slub"
+            description: SLUB YUM repo
+            baseurl: "http://sdvrhelrepo.slub-dresden.de/"
+            gpgcheck: "false"
+      - name: remove legacy repo configuration to avoid double configuration for SLUB repo
+        ansible.builtin.file:
+          path: "/etc/yum.repos.d/SLUB.repo"
+          state: absent
+      when: ansible_os_family == "RedHat"
diff --git a/molecule/resources/playbooks/verify.yml b/molecule/resources/playbooks/verify.yml
new file mode 100644
index 0000000..e707420
--- /dev/null
+++ b/molecule/resources/playbooks/verify.yml
@@ -0,0 +1,10 @@
+---
+# This is an example playbook to execute Ansible tests.
+
+- name: Verify
+  hosts: all
+  gather_facts: false
+  tasks:
+  - name: Example assertion
+    ansible.builtin.assert:
+      that: true
diff --git a/molecule/virtualbox/molecule.yml b/molecule/virtualbox/molecule.yml
new file mode 100644
index 0000000..5b11561
--- /dev/null
+++ b/molecule/virtualbox/molecule.yml
@@ -0,0 +1,41 @@
+---
+dependency:
+  name: galaxy
+  enabled: false
+driver:
+  name: vagrant
+lint: |
+  set -e
+  yamllint .
+  ansible-lint -x no-loop-var-prefix,command-instead-of-module,package-latest
+platforms:
+  # Check out the documentation at
+  # https://github.com/ansible-community/molecule-vagrant#documentation
+  # for more platform parameters.
+  - name: mol-validators
+    box: debian/bullseye64
+    memory: 1024
+    # List of raw Vagrant `config` options.
+    # provider_raw_config_args:
+    #   - "customize [ 'modifyvm', :id, '--natdnshostresolver1', 'on' ]"
+    # Dictionary of `config` options.
+    config_options:
+      ssh.keep_alive: yes
+      ssh.remote_user: "'lza'"
+provisioner:
+  name: ansible
+  log: true
+  config_options:
+    defaults:
+      # https://stackoverflow.com/questions/57435811/ansible-molecule-pass-multiple-vault-ids
+      # vault_identity_list: "@$HOME/.ansible/roles/lza_install_common.pass, @$HOME/.ansible/roles/passfile_1.pass"
+      vault_identity_list: "../lza_validators.pass"
+  vvv: false
+  playbooks:
+    # create: ../resources/playbooks/create.yml
+    # destroy: ../resources/playbooks/destroy.yml
+    converge: ../resources/playbooks/converge.yml
+    prepare: ../resources/playbooks/prepare.yml
+    verify: ../resources/playbooks/verify.yml
+verifier:
+  name: ansible
diff --git a/requirements.yml b/requirements.yml
deleted file mode 100644
index 1c25f02..0000000
--- a/requirements.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-# https://docs.ansible.com/ansible/latest/galaxy/user_guide.html#installing-multiple-roles-from-a-file
-- src: git+https://git.slub-dresden.de/slub-referat-2-3/ansible_vaults.git
-  scm: git
-- src: git+https://git.slub-dresden.de/digital-preservation/ansible_lza_install_common.git
-  scm: git
-- src: git+https://git.slub-dresden.de/digital-preservation/ansible_lza_server_hardening.git
-  scm: git
-#- src: git+
-#  scm: git
diff --git a/site.yml b/site.yml
index 8f228ea..7f71f79 100644
--- a/site.yml
+++ b/site.yml
@@ -24,6 +24,4 @@
   strategy: linear
 
   roles:
-    - { role: ansible_lza_install_common, become: true }
-    - { role: ansible_lza_server_hardening, become: true }
     - { role: ansible_lza_validators, become: true }
diff --git a/tasks/compile_callas_binaries.yml b/tasks/compile_callas_binaries.yml
index f62e680..2b61c41 100644
--- a/tasks/compile_callas_binaries.yml
+++ b/tasks/compile_callas_binaries.yml
@@ -1,6 +1,6 @@
 ---
 - name: install prerequisite packages for compilation
-  apt:
+  ansible.builtin.apt:
     name: [
       'g++',
       'git',
@@ -9,32 +9,51 @@
     ]
     state: latest
 
-- name: set symlinks for Callas PDF Engine
-  file:
-    state: link
-    src: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/{{ item }}"
-    path: "/opt/pdfa_webservice/{{ item }}"
-  loop:
-    - "etc"
-    - "include"
-    - "lang"
-    - "lib"
-    - "var"
+- name: setup deploy key
+  ansible.builtin.copy:
+    src: "{{ role_path }}/../ansible_vaults/{{ role_name }}/id_ed25519_deploykey"
+    dest: "/tmp/id_ed25519_deploykey"
+    mode: "0600"
+  delegate_to: 127.0.0.1
 
 # ATTENTION:
 #   - This is run on the controller PC, not on the target! Needs Git binary.
 #   - This task requires Git credentials to be entered in interactive mode.
 - name: checkout PDF/A-Webservice Git repo
-  git:
-    repo: "https://git.slub-dresden.de/slub-digitalpreservation/pdfa_webservice.git"
+  ansible.builtin.git:
+    repo: "git@git.slub-dresden.de:slub-digitalpreservation/pdfa_webservice.git"
     dest: "/tmp/pdfa_webservice/"
     depth: 1
+    key_file: "/tmp/id_ed25519_deploykey"
   delegate_to: 127.0.0.1
 
 - name: copy PDF/A-Webservice Git repo to managed host
-  copy:
+  ansible.builtin.copy:
     src: "/tmp/pdfa_webservice/"
     dest: "/opt/pdfa_webservice/"
+    directory_mode: "0755"
+    mode: "0644"
+  register: cp_git
+  changed_when: false    # I have no idea why this isn't idempotent.
+
+- name: set symlinks for Callas PDF Engine
+  ansible.builtin.file:
+    state: link
+    src: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/{{ item }}"
+    path: "/opt/pdfa_webservice/{{ item }}"
+  loop:
+    - "etc"
+    - "include"
+    - "lang"
+    - "lib"
+    - "var"
+  changed_when: false    # This always changes, because the dest path is fresh.
 
-- name: compile PDF/A-Webservice binary
-  shell: 'CXXFLAGS="-std=c++11" make --directory /opt/pdfa_webservice/src/'
+# We need to use the ansible.builtin.shell module here, because the
+# ansible.builtin.command module does not accept setting environment variables
+# inline. Molecule errors are turned off for this.
+- name: compile PDF/A-Webservice binary    # noqa command-instead-of-shell
+  ansible.builtin.shell:
+    cmd: 'CXXFLAGS="-std=c++11" make --directory /opt/pdfa_webservice/src/'
+  when: cp_git.failed is false
+  changed_when: false    # This always changes, because the dest path is fresh.
diff --git a/tasks/configure_apache.yml b/tasks/configure_apache.yml
index 70e193a..d5f20ab 100644
--- a/tasks/configure_apache.yml
+++ b/tasks/configure_apache.yml
@@ -1,10 +1,11 @@
 ---
 ### APACHE KONFIGURIEREN ###
 - name: Apache-Konfigurationsdateien einspielen (copied)
-  copy:
+  ansible.builtin.copy:
     src: "{{ item.src }}"
     dest: "{{ item.dest }}"
-  with_items:
+    mode: "0644"
+  loop:
     - src: "../files/etc/apache2/security.conf"
       dest: "/etc/apache2/conf-available/security.conf"
     - src: "../files/etc/logrotate.d/apache2"
@@ -13,9 +14,10 @@
     - restart apache
 
 - name: Apache-Konfigurationsdateien einspielen (templated)
-  template:
+  ansible.builtin.template:
     src: "{{ item.src }}.j2"
     dest: "{{ item.dest }}"
+    mode: "0644"
   loop:
     - src: "etc/apache2/default.conf"
       dest: "/etc/apache2/sites-available/000-default.conf"
@@ -29,21 +31,29 @@
   notify:
     - restart apache
 
+- name: check if config needs to be enabled
+  ansible.builtin.command: "/usr/sbin/a2query -c serve-cgi-bin"
+  register: a2query
+  changed_when: false
+
 # symlink /etc/apache2/conf-available/serve-cgi-bin.conf to /etc/apache2/conf-enabled/
 - name: CGI-Extension-Config aktivieren
-  command: "/usr/sbin/a2enconf serve-cgi-bin"        # noqa 301
-  changed_when: "not db_create_result.stdout|search('already enabled')"
+  ansible.builtin.command: "/usr/sbin/a2enconf serve-cgi-bin"        # noqa 301
+  when: not "(enabled by site administrator)" in a2query.stdout
+  register: a2enconf
+  changed_when: not "already enabled" in a2enconf.stdout
   notify:
     - restart apache
 
 ### STARTSEITE INSTALLIEREN ###
 - name: alte Startseite löschen
-  file:
+  ansible.builtin.file:
     path: "/var/www/html/index.html"
     state: absent
+  changed_when: false
 
 - name: Startseite neu installieren
-  blockinfile:
+  ansible.builtin.blockinfile:
     dest: "/var/www/html/index.html"
     backup: "no"
     create: "yes"
@@ -67,9 +77,10 @@
         </body>
       </html>
     insertafter: EOF
+  changed_when: false
 
 - name: set HTTP timeout
-  lineinfile:
+  ansible.builtin.lineinfile:
     path: "/etc/apache2/apache2.conf"
     regexp: "^Timeout [0-9]+$"
     line: "Timeout 3000"
diff --git a/tasks/configure_crontab.yml b/tasks/configure_crontab.yml
index 7de424f..e96eb71 100644
--- a/tasks/configure_crontab.yml
+++ b/tasks/configure_crontab.yml
@@ -1,10 +1,10 @@
 ---
 ### CRONTAB EINRICHTEN ###
 - name: DEBUG
-  debug:
+  ansible.builtin.debug:
     var: vault_users
 - name: Cronjob zum Löschen alter tmpfiles aus /tmp erstellen
-  cron:
+  ansible.builtin.cron:
     name: "delete old tmpfiles from /tmp"
     hour: "0"
     minute: "0"
diff --git a/tasks/configure_iptables.yml b/tasks/configure_iptables.yml
index ec216d9..57a6333 100644
--- a/tasks/configure_iptables.yml
+++ b/tasks/configure_iptables.yml
@@ -1,6 +1,6 @@
 ---
 - name: iptables-Regeln setzen
-  iptables:
+  ansible.builtin.iptables:
     action: "insert"
     chain: "INPUT"
     comment: "{{ item.comment }}"
@@ -15,6 +15,6 @@
     source_port: "{{ item.src_port | default(omit) }}"
     state: "{{ item.state | default('present') }}"
     table: "filter"
-  loop: "{{ vault_iptables|flatten(levels=1) }}"
+  loop: "{{ vault_iptables | flatten(levels=1) }}"
   notify:
     - save iptables rules
diff --git a/tasks/configure_local_mounts.yml b/tasks/configure_local_mounts.yml
index 3426abd..0ea27b9 100644
--- a/tasks/configure_local_mounts.yml
+++ b/tasks/configure_local_mounts.yml
@@ -1,7 +1,7 @@
 ---
 - name: Mount tmpfs for non-persistent and fast /tmp directory
-  mount:
-    boot: "yes"
+  ansible.posix.mount:
+    boot: true
     fstype: "tmpfs"
     opts: "defaults,size=5G"
     path: "/tmp"
diff --git a/tasks/configure_nfs_mounts.yml b/tasks/configure_nfs_mounts.yml
index d62f545..e307af2 100644
--- a/tasks/configure_nfs_mounts.yml
+++ b/tasks/configure_nfs_mounts.yml
@@ -1,11 +1,12 @@
 ---
 - name: Mountpoint fuer Logging anlegen
-  file:
+  ansible.builtin.file:
     path: "{{ vault_nfs_mounts.log.mountpoint }}{{ ansible_hostname }}"
     state: directory
+    mode: "0400"
 
 - name: NFS-Shares fuer Logging mounten
-  mount:
+  ansible.posix.mount:
     name: "{{ vault_nfs_mounts.log.mountpoint }}{{ ansible_hostname }}"
     src: "{{ vault_nfs_mounts.log.share }}/{{ ansible_hostname }}"
     state: mounted
diff --git a/tasks/create_users_groups.yml b/tasks/create_users_groups.yml
index f6b00ac..9edacd2 100644
--- a/tasks/create_users_groups.yml
+++ b/tasks/create_users_groups.yml
@@ -1,12 +1,12 @@
 ---
-- name: Gruppe exlibris erstellen
-  group:
+- name: Gruppen erstellen
+  ansible.builtin.group:
     name: "{{ item.name }}"
-    gid: "{{ item.gid }}"
+    gid: "{{ item.gid | default(omit) }}"
   loop: "{{ vault_groups | flatten(levels=1) }}"
 
 - name: User in Gruppen einfügen und primäre Gruppe setzen
-  user:
+  ansible.builtin.user:
     name: "{{ item.name }}"
     group: "{{ item.group }}"
     groups: "{{ item.groups }}"
diff --git a/tasks/install_callas_pdf_engine.yml b/tasks/install_callas_pdf_engine.yml
index 84a3fe1..3900a58 100644
--- a/tasks/install_callas_pdf_engine.yml
+++ b/tasks/install_callas_pdf_engine.yml
@@ -1,17 +1,22 @@
 ---
 - name: remove old version of callas PDFEngine
-  file:
+  ansible.builtin.file:
     path: "/usr/local/lib/callas_pdfEngine_SDK_x64"
     state: absent
+  changed_when: false    # Always remove existing versions. This canot be
+                         # idempotent, because we install a new version in
+                         # later tasks.
 
 - name: create directories for CGI-BIN and callas PDFEngine
-  file:
+  ansible.builtin.file:
     path: "{{ item }}"
     state: directory
     mode: "0755"
   loop:
     - "/usr/lib/cgi-bin/"
     - "/usr/local/lib/callas_pdfEngine_SDK_x64"
+  changed_when: false    # This will always change, because we just removed the
+                         # dir in the previous task to clean up.
 
 # get latest version of callas PDFEngine at
 # http://www.callassoftware.com/extranet/callas_pdfEngineSDK/callas_pdfEngineSDK_x64_Linux.tar.gz
@@ -19,33 +24,40 @@
 # !!! CAUTION: DO NOT AUTOMATICALLY INSTALL NEW SDK VERSIONS WITHOUT RECOMPILING
 #              CGI-BIN FILES!!!
 - name: copy callas PDFEngine
-  copy:
-    src: "pdf_treatment/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}.tar.gz"
-    dest: "/usr/local/lib/"
+  ansible.builtin.get_url:
+    url: "http://www.callassoftware.com/extranet/callas_pdfEngineSDK/callas_pdfEngineSDK_x64_Linux.tar.gz"
+    dest: "/usr/local/lib/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}.tar.gz"
+    mode: "0644"
+  changed_when: false    # This will always change, because we're installing a
+                         # fresh version of Callas into a cleaned directory
+                         # (see tasks below, where we delete the .tar.gz).
 
 # !!! CAUTION: DO NOT EXTRACT TO /tmp/
 # (https://github.com/ansible/ansible/issues/28569)
 - name: unpack callas PDFEngine
-  unarchive:
+  ansible.builtin.unarchive:
     src: "/usr/local/lib/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}.tar.gz"
     dest: "/usr/local/lib/callas_pdfEngine_SDK_x64"
     remote_src: true
-    follow: "yes"
+  changed_when: false    # This will always change.
 
 - name: ... and remove source (as there's no actual move module in Ansible).
-  file:
+  ansible.builtin.file:
     path: "/usr/local/lib/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}.tar.gz"
     state: absent
+  changed_when: false    # This will always change. We just downloaded a fresh
+                         # archive that now needs to be cleaned up.
 
 - name: set permissions for PDFEngine
-  file:
+  ansible.builtin.file:
     path: "/usr/local/lib/callas_pdfEngine_SDK_x64"
     group: "www-data"
     recurse: true
     state: directory
+  changed_when: false    # This will always change.
 
 - name: symlink callas PDFEngine from '/usr/local/lib/callas_pdfEngine_SDK_x64/*' to '/usr/lib/cgi-bin/*'
-  file:
+  ansible.builtin.file:
     src: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/{{ item }}"
     dest: "/usr/lib/cgi-bin/{{ item }}"
     state: link
@@ -53,27 +65,35 @@
     - "lib"
     - "lang"
 
+- name: install libstdc++
+  ansible.builtin.apt:
+    name: "libstdc++-10-dev"
+    state: present
+
 - name: remove symlinks to distro version of libstdc++, because we need to use what's provided with PDFEngine
-  file:
+  ansible.builtin.file:
     path: "{{ item }}"
     state: absent
   loop:
     - "/usr/local/lib/callas_pdfEngine_SDK_x64/lib/libstdc++.so"
     - "/usr/local/lib/callas_pdfEngine_SDK_x64/lib/libstdc++.so.6"
 
-- name: create symlinks to system libraries, because we need to use what's provided with PDFEngine
-  file:
-    src: "{{ item.src }}"
-    dest: "{{ item.dest }}"
-    state: link
-  loop:
-    - src: "/usr/lib/gcc/x86_64-linux-gnu/8/libstdc++.so"
-      dest: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/lib/libstdc++.so"
-    - src: "/usr/lib/x86_64-linux-gnu/libstdc++.so.6"
-      dest: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/lib/libstdc++.so.6"
-  notify: run ldconfig to configure dynamic linker run-time bindings
-
-- name: install packages
-  apt:
-    name: "libstdc++-8-dev"
-    state: present
+# THIS TASK DESTROYS THE SYSTEM!!! We will comment it out until the underlying
+# issue with the Callas PFDA Pilot SDK is solved.
+# The error message that we get is:
+# apt-get: /usr/lib/cgi-bin/lib/libstdc++.so.6: version `GLIBCXX_3.4.26' not
+# found (required by /lib/x86_64-linux-gnu/libapt-pkg.so.6.0)
+# - name: >
+#     create symlinks to system libraries, because we need to use what's
+#     provided with PDFEngine
+#   ansible.builtin.file:
+#     src: "{{ item.src }}"
+#     dest: "{{ item.dest }}"
+#     state: link
+#   loop:
+#     - src: "/usr/lib/gcc/x86_64-linux-gnu/10/libstdc++.so"
+#       dest: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/lib/libstdc++.so"
+#     - src: "/usr/lib/x86_64-linux-gnu/libstdc++.so.6"
+#       dest: "/usr/local/lib/callas_pdfEngine_SDK_x64/callas_pdfEngineSDK_x64_Linux_{{ vault_callas_sdk_version }}/lib/libstdc++.so.6"
+#   notify: run ldconfig to configure dynamic linker run-time bindings
+#   tags: [molecule-notest]
diff --git a/tasks/install_converters.yml b/tasks/install_converters.yml
index e29bab3..66c7a1a 100644
--- a/tasks/install_converters.yml
+++ b/tasks/install_converters.yml
@@ -1,6 +1,6 @@
 ---
 - name: "'convert_pdfa.bin' nach '/usr/lib/cgi-bin/convert_to_pdfa' kopieren & umbenennen, Rechte setzen"
-  copy:
+  ansible.builtin.copy:
     src: "/opt/pdfa_webservice/src/unix/convert_to_pdfa.bin"
     dest: "/usr/lib/cgi-bin/convert_to"
     remote_src: true
diff --git a/tasks/install_fonts.yml b/tasks/install_fonts.yml
index 83f96a1..6316f71 100644
--- a/tasks/install_fonts.yml
+++ b/tasks/install_fonts.yml
@@ -1,7 +1,7 @@
 ---
 # provide fonts for callas
 - name: create font-cache folder
-  file:
+  ansible.builtin.file:
     path: "/usr/local/share/callas/cache/"
     state: directory
     mode: "0775"
@@ -10,9 +10,9 @@
 
 # install applefonts
 - name: copy applefonts
-  unarchive:
+  ansible.builtin.unarchive:
     src: "applefonts.zip"
     dest: "/usr/share/fonts/truetype/"
-    follow: "yes"
+    mode: "0644"
   notify:
     - run fc-cache to rebuild font information cache files and add applefonts
diff --git a/tasks/install_packages.yml b/tasks/install_packages.yml
index b216bd5..d1e2273 100644
--- a/tasks/install_packages.yml
+++ b/tasks/install_packages.yml
@@ -1,7 +1,7 @@
 ---
 - name: Install/Update Debian packages
-  apt:
-    update_cache: "yes"
+  ansible.builtin.apt:
+    update_cache: true
     state: present
     name: [
       'lynx',
@@ -30,6 +30,7 @@
       'perl',
       'perl-base',
       'perl-modules',
+      'python3-apt',
       'rsync',
       'samba',
       'samba-common',
@@ -39,7 +40,7 @@
     ]
 
 - name: remove unnecessary packages
-  apt:
+  ansible.builtin.apt:
     state: absent
     name: [
       'libtiff-tools'
diff --git a/tasks/install_validators.yml b/tasks/install_validators.yml
index dad097a..821c010 100644
--- a/tasks/install_validators.yml
+++ b/tasks/install_validators.yml
@@ -1,7 +1,7 @@
 ---
 ### INSTALLATION VALIDATOREN ###
 - name: "'validate_pdfa.bin' nach '/usr/lib/cgi-bin/is_valid' kopieren & umbenennen, Rechte setzen"
-  copy:
+  ansible.builtin.copy:
     src: "/opt/pdfa_webservice/src/unix/validate_pdfa.bin"
     dest: "/usr/lib/cgi-bin/is_valid"
     remote_src: true
diff --git a/tasks/main.yml b/tasks/main.yml
index c6079dc..95a9d68 100644
--- a/tasks/main.yml
+++ b/tasks/main.yml
@@ -1,54 +1,54 @@
 ---
 - name: include Ansible Vaults
-  include_vars: "{{ role_path }}/../ansible_vaults/{{ role_name }}/{{ item }}"
+  ansible.builtin.include_vars: "{{ role_path }}/../ansible_vaults/{{ role_name }}/{{ item }}"
   loop:
     - "validator.vault"
   tags: [always]
 
 - name: install packages
-  import_tasks: install-packages.yml
+  ansible.builtin.import_tasks: "install_packages.yml"
   tags: [apt]
 
 - name: create users and groups
-  import_tasks: create-users-groups.yml
+  ansible.builtin.import_tasks: "create_users_groups.yml"
   tags: [users]
 
 - name: configure local mounts
-  import_tasks: configure_local_mounts.yml
+  ansible.builtin.import_tasks: "configure_local_mounts.yml"
   tags: [mount]
 
 - name: configure nfs mounts
-  import_tasks: configure-nfs-mounts.yml
+  ansible.builtin.import_tasks: "configure_nfs_mounts.yml"
   tags: [nfs]
 
 - name: configure Apache webserver
-  import_tasks: configure-apache.yml
+  ansible.builtin.import_tasks: "configure_apache.yml"
   tags: [apache]
 
-#- name: install Callas PDF Engine SDK
-#  import_tasks: install-callas-pdf-engine.yml
-#  tags: [callaspdf]
+- name: install Callas PDF Engine SDK
+  ansible.builtin.import_tasks: "install_callas_pdf_engine.yml"
+  tags: [callaspdf]
 
 - name: compile Callas binaries
-  import_tasks: compile_callas_binaries.yml
+  ansible.builtin.import_tasks: "compile_callas_binaries.yml"
   tags: [callaspdf]
 
 - name: install fonts
-  import_tasks: install_fonts.yml
+  ansible.builtin.import_tasks: "install_fonts.yml"
   tags: [fonts]
 
 - name: install validators
-  import_tasks: install-validators.yml
+  ansible.builtin.import_tasks: "install_validators.yml"
   tags: [validators]
 
 - name: install converters
-  import_tasks: install-converters.yml
+  ansible.builtin.import_tasks: "install_converters.yml"
   tags: [converters]
 
 - name: configure Crontab
-  import_tasks: configure-crontab.yml
+  ansible.builtin.import_tasks: "configure_crontab.yml"
   tags: [cron]
 
 - name: configure iptables
-  import_tasks: configure-iptables.yml
+  ansible.builtin.import_tasks: "configure_iptables.yml"
   tags: [iptables]
-- 
GitLab