diff --git a/.ansible-lint b/.ansible-lint
index 7aeb2ef52aa202f7f9d4d3fde005a26e6a0ef264..3fde5995a71bcbb80ac14562d3bf0f28cb9fb561 100644
--- a/.ansible-lint
+++ b/.ansible-lint
@@ -16,6 +16,11 @@ exclude_paths:
 kinds:
   - playbook: '**/tests/**/test_*.yml'
   - playbook: '**/playbooks/**/*.yml'
+  - playbook: '**/tests/ca-less/install_*_without_ca.yml'
+  - playbook: '**/tests/ca-less/clean_up_certificates.yml'
+  - playbook: '**/tests/external-signed-ca-with-automatic-copy/install-server-with-external-ca-with-automatic-copy.yml'
+  - playbook: '**/tests/external-signed-ca-with-manual-copy/install-server-with-external-ca-with-manual-copy.yml'
+  - playbook: '**/tests/user/create_users_json.yml'
   - tasks: '**/tasks_*.yml'
   - tasks: '**/env_*.yml'
 
diff --git a/roles/ipabackup/meta/main.yml b/roles/ipabackup/meta/main.yml
index 6131e0d73d422b01c8a309f386e4f1356b8e3689..f081e2a6b852cb767283d2a1e4a41ead97854fce 100644
--- a/roles/ipabackup/meta/main.yml
+++ b/roles/ipabackup/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to backup and restore an IPA server
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipabackup/tasks/backup.yml b/roles/ipabackup/tasks/backup.yml
index b2f778171f2e08405e1c3ba0e53695dcb7fb7ccf..fb4cb2497c9c84ffeac2889545cccd9a449c10da 100644
--- a/roles/ipabackup/tasks/backup.yml
+++ b/roles/ipabackup/tasks/backup.yml
@@ -5,15 +5,16 @@
   ansible.builtin.shell: >
     ipa-backup
     {{ "--gpg" if ipabackup_gpg | bool else "" }}
-    {{ "--gpg-keyring="+ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined else "" }}
+    {{ "--gpg-keyring=" + ipabackup_gpg_keyring if ipabackup_gpg_keyring is defined else "" }}
     {{ "--data" if ipabackup_data | bool else "" }}
     {{ "--logs" if ipabackup_logs | bool else "" }}
     {{ "--online" if ipabackup_online | bool else "" }}
     {{ "--disable-role-check" if ipabackup_disable_role_check | bool else "" }}
-    {{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
+    {{ "--log-file=" + ipabackup_log_file if ipabackup_log_file is defined else "" }}
   register: result_ipabackup
 
 - name: Handle backup
+  when: ipabackup_to_controller
   block:
   - name: Get ipabackup_item from stderr or stdout output
     ansible.builtin.set_fact:
@@ -26,7 +27,8 @@
       label: ""
 
   - name: Fail on missing ipabackup_item
-    ansible.builtin.fail: msg="Failed to get ipabackup_item"
+    ansible.builtin.fail:
+      msg: "Failed to get ipabackup_item"
     when: ipabackup_item is not defined
 
   - name: Copy backup to controller
@@ -36,5 +38,3 @@
   - name: Remove backup on server
     ansible.builtin.include_tasks: "{{ role_path }}/tasks/remove_backup_from_server.yml"
     when: not ipabackup_keep_on_server
-
-  when: ipabackup_to_controller
diff --git a/roles/ipabackup/tasks/copy_backup_from_server.yml b/roles/ipabackup/tasks/copy_backup_from_server.yml
index 516814b1316a98253cf6a3b0f51a00ae7d5339b3..c64c3c46c6dde87a8e683de3cf3e9d830129c0ef 100644
--- a/roles/ipabackup/tasks/copy_backup_from_server.yml
+++ b/roles/ipabackup/tasks/copy_backup_from_server.yml
@@ -1,6 +1,7 @@
 ---
 - name: Fail on invalid ipabackup_item
-  ansible.builtin.fail: msg="ipabackup_item {{ ipabackup_item }} is not valid"
+  ansible.builtin.fail:
+    msg: "ipabackup_item {{ ipabackup_item }} is not valid"
   when: ipabackup_item is not defined or
         ipabackup_item | length < 1 or
         (ipabackup_item.find("ipa-full-") == -1 and
@@ -8,8 +9,8 @@
 
 - name: Set controller destination directory
   ansible.builtin.set_fact:
-    ipabackup_controller_dir:
-        "{{ ipabackup_controller_path | default(lookup('env','PWD')) }}/{{
+    __derived_controller_dir:
+        "{{ ipabackup_controller_path | default(lookup('env', 'PWD')) }}/{{
          ipabackup_name_prefix | default(ansible_facts['fqdn']) }}_{{
          ipabackup_item }}/"
 
@@ -19,7 +20,8 @@
   register: result_backup_stat
 
 - name: Fail on missing backup directory
-  ansible.builtin.fail: msg="Unable to find backup {{ ipabackup_item }}"
+  ansible.builtin.fail:
+    msg: "Unable to find backup {{ ipabackup_item }}"
   when: result_backup_stat.stat.isdir is not defined
 
 - name: Get backup files to copy for "{{ ipabackup_item }}"
@@ -33,13 +35,13 @@
   ansible.builtin.fetch:
     flat: yes
     src: "{{ ipabackup_dir }}/{{ ipabackup_item }}/{{ item }}"
-    dest: "{{ ipabackup_controller_dir }}"
+    dest: "{{ __derived_controller_dir }}"
   with_items:
   - "{{ result_find_backup_files.stdout_lines }}"
 
 - name: Fix file modes for backup on controller
   ansible.builtin.file:
-    dest: "{{ ipabackup_controller_dir }}"
+    dest: "{{ __derived_controller_dir }}"
     mode: u=rwX,go=
     recurse: yes
   delegate_to: localhost
diff --git a/roles/ipabackup/tasks/copy_backup_to_server.yml b/roles/ipabackup/tasks/copy_backup_to_server.yml
index 6fa206131e79b0b385110bc1d16636e8e36e7858..a487152865b74fd465cc4f1b52afc616c998f9af 100644
--- a/roles/ipabackup/tasks/copy_backup_to_server.yml
+++ b/roles/ipabackup/tasks/copy_backup_to_server.yml
@@ -1,6 +1,7 @@
 ---
 - name: Fail on invalid ipabackup_name
-  ansible.builtin.fail: msg="ipabackup_name {{ ipabackup_name }} is not valid"
+  ansible.builtin.fail:
+    msg: "ipabackup_name {{ ipabackup_name }} is not valid"
   when: ipabackup_name is not defined or
         ipabackup_name | length < 1 or
         (ipabackup_name.find("ipa-full-") == -1 and
@@ -8,35 +9,36 @@
 
 - name: Set controller source directory
   ansible.builtin.set_fact:
-    ipabackup_controller_dir:
-      "{{ ipabackup_controller_path | default(lookup('env','PWD')) }}"
+    __derived_controller_dir:
+      "{{ ipabackup_controller_path | default(lookup('env', 'PWD')) }}"
 
 - name: Set ipabackup_item
   ansible.builtin.set_fact:
-    ipabackup_item:
-      "{{ ipabackup_name | regex_search('.*_(ipa-.+)','\\1') | first }}"
+    __derived_item:
+      "{{ ipabackup_name | regex_search('.*_(ipa-.+)', '\\1') | first }}"
   when: "'_ipa-' in ipabackup_name"
 
 - name: Set ipabackup_item
   ansible.builtin.set_fact:
-    ipabackup_item: "{{ ipabackup_name }}"
+    __derived_item: "{{ ipabackup_name }}"
   when: "'_ipa-' not in ipabackup_name"
 
 - name: Stat backup to copy
   ansible.builtin.stat:
-    path: "{{ ipabackup_controller_dir }}/{{ ipabackup_name }}"
+    path: "{{ __derived_controller_dir }}/{{ ipabackup_name }}"
   register: result_backup_stat
   delegate_to: localhost
   become: no
 
 - name: Fail on missing backup to copy
-  ansible.builtin.fail: msg="Unable to find backup {{ ipabackup_name }}"
+  ansible.builtin.fail:
+    msg: "Unable to find backup {{ ipabackup_name }}"
   when: result_backup_stat.stat.isdir is not defined
 
-- name: Copy backup files to server for "{{ ipabackup_item }}"
+- name: Copy backup files to server for "{{ __derived_item }}"
   ansible.builtin.copy:
-    src: "{{ ipabackup_controller_dir }}/{{ ipabackup_name }}/"
-    dest: "{{ ipabackup_dir }}/{{ ipabackup_item }}"
+    src: "{{ __derived_controller_dir }}/{{ ipabackup_name }}/"
+    dest: "{{ ipabackup_dir }}/{{ __derived_item }}"
     owner: root
     group: root
     mode: u=rw,go=r
diff --git a/roles/ipabackup/tasks/main.yml b/roles/ipabackup/tasks/main.yml
index 25d8c25c225f2aab99b9ccb713760a465883298f..01fb10b82738d9668c1ffe3f4d6c257479f3974d 100644
--- a/roles/ipabackup/tasks/main.yml
+++ b/roles/ipabackup/tasks/main.yml
@@ -2,7 +2,8 @@
 # tasks file for ipabackup
 
 - name: Check for empty vars
-  ansible.builtin.fail: msg="Variable {{ item }} is empty"
+  ansible.builtin.fail:
+    msg: "Variable {{ item }} is empty"
   when: "item in vars and not vars[item]"
   with_items: "{{ ipabackup_empty_var_checks }}"
   vars:
@@ -23,11 +24,13 @@
   when: ipabackup_online | bool and not ipabackup_data | bool
 
 - name: Fail if ipabackup_from_controller and ipabackup_to_controller are set
-  ansible.builtin.fail: msg="ipabackup_from_controller and ipabackup_to_controller are set"
+  ansible.builtin.fail:
+    msg: "ipabackup_from_controller and ipabackup_to_controller are set"
   when: ipabackup_from_controller | bool and ipabackup_to_controller | bool
 
 - name: Fail for given ipabackup_name if state is not copied, restored or absent
-  ansible.builtin.fail: msg="ipabackup_name is given and state is not copied, restored or absent"
+  ansible.builtin.fail:
+    msg: "ipabackup_name is given and state is not copied, restored or absent"
   when: state is not defined or
         (state != "copied" and state != "restored" and state != "absent") and
         ipabackup_name is defined
@@ -40,12 +43,17 @@
   when: state|default("present") == "present"
 
 - name: Fail on missing ipabackup_name
-  ansible.builtin.fail: msg="ipabackup_name is not set"
+  ansible.builtin.fail:
+    msg: "ipabackup_name is not set"
   when: (ipabackup_name is not defined or not ipabackup_name) and
         state is defined and
         (state == "copied" or state == "restored" or state == "absent")
 
 - name: Get all backup names for copy to controller
+  when: state is defined and
+        ((state == "copied" and ipabackup_to_controller) or
+         state == "absent") and
+        ipabackup_name is defined and ipabackup_name == "all"
   block:
   - name: Get list of all backups on IPA server
     ansible.builtin.shell:
@@ -58,15 +66,12 @@
     ansible.builtin.set_fact:
       ipabackup_names: "{{ result_backup_find_backup_files.stdout_lines }}"
 
-  when: state is defined and
-        ((state == "copied" and ipabackup_to_controller) or
-         state == "absent") and
-        ipabackup_name is defined and ipabackup_name == "all"
-
 - name: Set ipabackup_names from ipabackup_name
+  when: ipabackup_names is not defined and ipabackup_name is defined
   block:
   - name: Fail on ipabackup_name all
-    ansible.builtin.fail: msg="ipabackup_name can not be all in this case"
+    ansible.builtin.fail:
+      msg: "ipabackup_name can not be all in this case"
     when: ipabackup_name is defined and ipabackup_name == "all"
 
   - name: Set ipabackup_names from ipabackup_name string
@@ -78,7 +83,6 @@
     ansible.builtin.set_fact:
       ipabackup_names: "{{ ipabackup_name }}"
     when: ipabackup_name | type_debug == "list"
-  when: ipabackup_names is not defined and ipabackup_name is defined
 
 - name: Set empty ipabackup_names if ipabackup_name is not defined
   ansible.builtin.set_fact:
@@ -86,6 +90,8 @@
   when: ipabackup_names is not defined and ipabackup_name is not defined
 
 - name: Process "{{ ipabackup_names }}"
+  when: state is defined and
+        ((state == "copied" and ipabackup_to_controller) or state == "absent")
   block:
   - name: Copy backup from IPA server
     ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_backup_from_server.yml"
@@ -107,20 +113,22 @@
       loop_var: main_item
     when: state is defined and state == "absent"
 
-  when: state is defined and
-        ((state == "copied" and ipabackup_to_controller) or state == "absent")
-
 # Fail with more than one entry in ipabackup_names for copy to sever and
 # restore.
 
 - name: Fail to copy or restore more than one backup on the server
-  ansible.builtin.fail: msg="Only one backup can be copied to the server or restored"
+  ansible.builtin.fail:
+    msg: "Only one backup can be copied to the server or restored"
   when: state is defined and (state == "copied" or state == "restored") and
         ipabackup_from_controller | bool and ipabackup_names | length != 1
 
 # Use only first item in ipabackup_names for copy to server and for restore.
 
 - name: Process "{{ ipabackup_names[0] }}"
+  when: ipabackup_from_controller or
+        (state|default("present") == "copied" and not ipabackup_to_controller)
+  vars:
+    ipabackup_name: "{{ ipabackup_names[0] }}"
   block:
   - name: Copy backup to server
     ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_backup_to_server.yml"
@@ -129,11 +137,6 @@
     ansible.builtin.include_tasks: "{{ role_path }}/tasks/restore.yml"
     when: state|default("present") == "restored"
 
-  vars:
-    ipabackup_name: "{{ ipabackup_names[0] }}"
-  when: ipabackup_from_controller or
-        (state|default("present") == "copied" and not ipabackup_to_controller)
-
 - name: Restore IPA server
   ansible.builtin.include_tasks: "{{ role_path }}/tasks/restore.yml"
   vars:
diff --git a/roles/ipabackup/tasks/restore.yml b/roles/ipabackup/tasks/restore.yml
index 1a0794da556de59a507a1c9b6eec0caf9ff9d92e..27e3c7d636203d328ac08a41573853dfc78e52da 100644
--- a/roles/ipabackup/tasks/restore.yml
+++ b/roles/ipabackup/tasks/restore.yml
@@ -26,7 +26,8 @@
   register: result_backup_stat
 
 - name: Fail on missing backup directory
-  ansible.builtin.fail: msg="Unable to find backup {{ ipabackup_item }}"
+  ansible.builtin.fail:
+    msg: "Unable to find backup {{ ipabackup_item }}"
   when: result_backup_stat.stat.isdir is not defined
 
 - name: Stat header file in backup "{{ ipabackup_item }}"
@@ -35,7 +36,8 @@
   register: result_backup_header_stat
 
 - name: Fail on missing header file in backup
-  ansible.builtin.fail: msg="Unable to find backup {{ ipabackup_item }} header file"
+  ansible.builtin.fail:
+    msg: "Unable to find backup {{ ipabackup_item }} header file"
   when: result_backup_header_stat.stat.isreg is not defined
 
 - name: Get services from backup
@@ -53,6 +55,7 @@
 ### INSTALL PACKAGES
 
 - name: Package installation
+  when: ipabackup_install_packages | bool
   block:
   - name: Ensure that IPA server packages are installed
     ansible.builtin.package:
@@ -77,11 +80,10 @@
       state: present
     when: ipabackup_setup_firewalld | bool
 
-  when: ipabackup_install_packages | bool
-
 ### START FIREWALLD
 
 - name: Firewall configuration
+  when: ipabackup_setup_firewalld | bool
   block:
   - name: Ensure that firewalld is running
     ansible.builtin.systemd:
@@ -104,8 +106,6 @@
       >/dev/null
     when: ipabackup_firewalld_zone is defined
 
-  when: ipabackup_setup_firewalld | bool
-
 ### RESTORE
 
 - name: Restore backup
@@ -114,13 +114,13 @@
     ipa-restore
     {{ ipabackup_item }}
     --unattended
-    {{ "--password="+ipabackup_password if ipabackup_password is defined else "" }}
+    {{ "--password=" + ipabackup_password if ipabackup_password is defined else "" }}
     {{ "--data" if ipabackup_data | bool else "" }}
     {{ "--online" if ipabackup_online | bool else "" }}
-    {{ "--instance="+ipabackup_instance if ipabackup_instance is defined else "" }}
-    {{ "--backend="+ipabackup_backend if ipabackup_backend is defined else "" }}
+    {{ "--instance=" + ipabackup_instance if ipabackup_instance is defined else "" }}
+    {{ "--backend=" + ipabackup_backend if ipabackup_backend is defined else "" }}
     {{ "--no-logs" if ipabackup_no_logs | bool else "" }}
-    {{ "--log-file="+ipabackup_log_file if ipabackup_log_file is defined else "" }}
+    {{ "--log-file=" + ipabackup_log_file if ipabackup_log_file is defined else "" }}
   register: result_iparestore
   ignore_errors: yes
 
@@ -136,7 +136,7 @@
   ansible.builtin.command: >
     firewall-cmd
     --permanent
-    {{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
+    {{ "--zone=" + ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
     --add-service=freeipa-ldap
     --add-service=freeipa-ldaps
     {{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}
@@ -147,7 +147,7 @@
 - name: Configure firewalld runtime
   ansible.builtin.command: >
     firewall-cmd
-    {{ "--zone="+ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
+    {{ "--zone=" + ipabackup_firewalld_zone if ipabackup_firewalld_zone is defined else "" }}
     --add-service=freeipa-ldap
     --add-service=freeipa-ldaps
     {{ "--add-service=freeipa-trust" if ipabackup_service_adtrust in ipabackup_services else "" }}
diff --git a/roles/ipaclient/meta/main.yml b/roles/ipaclient/meta/main.yml
index 356b2db01ceadba6783d7305ca8e3d6085d5161e..7c759325b961dfa980f767682b68c80d67ae015d 100644
--- a/roles/ipaclient/meta/main.yml
+++ b/roles/ipaclient/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to join a machine to an IPA domain
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml
index 28998c5641dfc3b743bbba71f2835ddba927c03f..fa33f89a6c0b4c1d109664ac1c34702ee2d6ef93 100644
--- a/roles/ipaclient/tasks/install.yml
+++ b/roles/ipaclient/tasks/install.yml
@@ -19,7 +19,8 @@
         ipaclient_servers is not defined
 
 - name: Install - Check that either password or keytab is set
-  ansible.builtin.fail: msg="ipaadmin_password and ipaadmin_keytab cannot be used together"
+  ansible.builtin.fail:
+    msg: "ipaadmin_password and ipaadmin_keytab cannot be used together"
   when: ipaadmin_keytab is defined and ipaadmin_password is defined
 
 - name: Install - Set default principal if no keytab is given
@@ -28,14 +29,18 @@
   when: ipaadmin_principal is undefined and ipaclient_keytab is undefined
 
 - name: Install - DNS resolver configuration
+  when: ipaclient_configure_dns_resolver | bool
+        and not ipaclient_on_master | bool
   block:
 
   - name: Install - Fail on missing ipaclient_domain and ipaserver_domain
-    ansible.builtin.fail: msg="ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
+    ansible.builtin.fail:
+      msg: "ipaclient_domain or ipaserver_domain is required for ipaclient_configure_dns_resolver"
     when: ipaserver_domain is not defined and ipaclient_domain is not defined
 
   - name: Install - Fail on missing ipaclient_servers
-    ansible.builtin.fail: msg="ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
+    ansible.builtin.fail:
+      msg: "ipaclient_dns_servers is required for ipaclient_configure_dns_resolver"
     when: ipaclient_dns_servers is not defined
 
   - name: Install - Configure DNS resolver
@@ -44,9 +49,6 @@
       searchdomains: "{{ ipaserver_domain | default(ipaclient_domain) }}"
       state: present
 
-  when: ipaclient_configure_dns_resolver | bool
-        and not ipaclient_on_master | bool
-
 - name: Install - IPA client test
   ipaclient_test:
     ### basic ###
@@ -73,6 +75,9 @@
   register: result_ipaclient_test
 
 - name: Install - Client deployment
+  when: not ansible_check_mode and
+        not (result_ipaclient_test.client_already_configured and
+            not ipaclient_allow_repair | bool and not ipaclient_force_join | bool)
   block:
   - name: Install - Cleanup leftover ccache
     ansible.builtin.file:
@@ -127,10 +132,11 @@
   # If a keytab is specified in the hostent, then the hostent will be disabled
   # if ipaclient_use_otp is set.
   - name: Install - Obtain OTP
+    when: ipaclient_use_otp | bool and ipaclient_otp is not defined
     block:
     - name: Install - Keytab or password is required for getting otp
       ansible.builtin.fail:
-        msg: Keytab or password is required for getting otp
+        msg: "Keytab or password is required for getting otp"
       when: ipaadmin_keytab is undefined and ipaadmin_password is undefined
 
     - name: Install - Create temporary file for keytab
@@ -172,9 +178,6 @@
         ipaadmin_orig_password: "{{ ipaadmin_password | default(omit) }}"
         ipaadmin_password: "{{ result_ipaclient_get_otp.host.randompassword
                                if result_ipaclient_get_otp.host is defined }}"
-
-    when: ipaclient_use_otp | bool and ipaclient_otp is not defined
-
     always:
     - name: Install - Remove keytab temporary file
       ansible.builtin.file:
@@ -191,21 +194,30 @@
     when: ipaclient_otp is defined
 
   - name: Install - Check keytab, principal and keytab
+    when: not ipaclient_on_master | bool
     block:
+    # This block is executed only when
+    # not (not ipaclient_on_master | bool and
+    #      not result_ipaclient_join.changed and
+    #      not ipaclient_allow_repair | bool and
+    #      (result_ipaclient_test_keytab.krb5_keytab_ok or
+    #       (result_ipaclient_join.already_joined is defined and
+    #        result_ipaclient_join.already_joined)))
 
     - name: Install - Check if principal and keytab are set
-      ansible.builtin.fail: msg="Admin principal and client keytab cannot be used together"
+      ansible.builtin.fail:
+        msg: "Admin principal and client keytab cannot be used together"
       when: ipaadmin_principal is defined and ipaclient_keytab is defined
 
     - name: Install - Check if one of password or keytabs are set
-      ansible.builtin.fail: msg="At least one of password or keytabs must be specified"
+      ansible.builtin.fail:
+        msg: "At least one of password or keytabs must be specified"
       when: not result_ipaclient_test_keytab.krb5_keytab_ok
             and ipaadmin_password is undefined
             and ipaadmin_keytab is undefined
             and ipaclient_keytab is undefined
-    when: not ipaclient_on_master | bool
 
-  - name: Install - Purge {{ result_ipaclient_test.realm }} from host keytab
+  - name: "Install - From host keytab, purge {{ result_ipaclient_test.realm }}"
     ansible.builtin.command: >
       /usr/sbin/ipa-rmkeytab
       -k /etc/krb5.keytab
@@ -249,16 +261,14 @@
               ipaclient_force_join)
 
   - name: Install - Allow repair checks
+    when: not ipaclient_on_master | bool and
+          not result_ipaclient_join.changed and
+          not ipaclient_allow_repair | bool and
+          (result_ipaclient_test_keytab.krb5_keytab_ok or
+              (result_ipaclient_join.already_joined is defined and
+                  result_ipaclient_join.already_joined))
     block:
-    # This block is executed only when
-    # not (not ipaclient_on_master | bool and
-    #      not result_ipaclient_join.changed and
-    #      not ipaclient_allow_repair | bool and
-    #      (result_ipaclient_test_keytab.krb5_keytab_ok or
-    #       (result_ipaclient_join.already_joined is defined and
-    #        result_ipaclient_join.already_joined)))
-
-    - name: krb5 configuration not correct
+    - name: The krb5 configuration is not correct
       ansible.builtin.fail:
         msg: >
           The krb5 configuration is not correct, please enable allow_repair
@@ -268,19 +278,19 @@
       ansible.builtin.fail:
         msg: "The IPA test failed, please enable allow_repair to fix this."
       when: not result_ipaclient_test_keytab.ping_test_ok
-    - name: ca.crt file is missing
+    - name: Fail due to missing ca.crt file
       ansible.builtin.fail:
         msg: >
           The ca.crt file is missing, please enable allow_repair to fix this.
       when: not result_ipaclient_test_keytab.ca_crt_exists
-    when: not ipaclient_on_master | bool and
-          not result_ipaclient_join.changed and
-          not ipaclient_allow_repair | bool and
-          (result_ipaclient_test_keytab.krb5_keytab_ok or
-              (result_ipaclient_join.already_joined is defined and
-                  result_ipaclient_join.already_joined))
 
   - name: Install - Configuration
+    when: not (not ipaclient_on_master | bool and
+          not result_ipaclient_join.changed and
+          not ipaclient_allow_repair | bool
+              and (result_ipaclient_test_keytab.krb5_keytab_ok
+              or (result_ipaclient_join.already_joined is defined
+              and result_ipaclient_join.already_joined)))
     block:
     - name: Install - Configure IPA default.conf
       ipaclient_ipa_conf:
@@ -402,18 +412,6 @@
         domain: "{{ result_ipaclient_test.domain }}"
         nisdomain: "{{ ipaclient_nisdomain | default(omit) }}"
       when: not ipaclient_no_nisdomain | bool
-
-    when: not (not ipaclient_on_master | bool and
-          not result_ipaclient_join.changed and
-          not ipaclient_allow_repair | bool
-              and (result_ipaclient_test_keytab.krb5_keytab_ok
-              or (result_ipaclient_join.already_joined is defined
-              and result_ipaclient_join.already_joined)))
-
-  when: not ansible_check_mode and
-        not (result_ipaclient_test.client_already_configured and
-            not ipaclient_allow_repair | bool and not ipaclient_force_join | bool)
-
   always:
   - name: Install - Restore original admin password if overwritten by OTP
     no_log: yes
diff --git a/roles/ipareplica/meta/main.yml b/roles/ipareplica/meta/main.yml
index 6b1021ad4bd84f25555aa84ca76a72ec9915687d..3054787cd543e8624f18bde5193876fc1e1e21ed 100644
--- a/roles/ipareplica/meta/main.yml
+++ b/roles/ipareplica/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to setup an IPA domain replica
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipareplica/tasks/install.yml b/roles/ipareplica/tasks/install.yml
index 798484186da0d462c98ada10a923a680bc8e5510..d4fb2997ae680dc274561d132c42822610860993 100644
--- a/roles/ipareplica/tasks/install.yml
+++ b/roles/ipareplica/tasks/install.yml
@@ -2,6 +2,7 @@
 # tasks file for ipareplica
 
 - name: Package installation
+  when: ipareplica_install_packages | bool
   block:
 
   - name: Install - Ensure IPA replica packages are installed
@@ -27,9 +28,8 @@
       state: present
     when: ipareplica_setup_firewalld | bool
 
-  when: ipareplica_install_packages | bool
-
 - name: Firewall configuration
+  when: ipareplica_setup_firewalld | bool
   block:
   - name: Firewalld service - Ensure that firewalld is running
     ansible.builtin.systemd:
@@ -52,8 +52,6 @@
       >/dev/null
     when: ipareplica_firewalld_zone is defined
 
-  when: ipareplica_setup_firewalld | bool
-
 - name: Install - Set ipareplica_servers
   ansible.builtin.set_fact:
     ipareplica_servers: "{{ groups['ipaservers'] | list }}"
@@ -73,7 +71,7 @@
     domain: "{{ ipareplica_domain | default(ipaserver_domain) |
             default(omit) }}"
     servers: "{{ ipareplica_servers | default(omit) }}"
-    realm: "{{ ipareplica_realm | default(ipaserver_realm) |default(omit) }}"
+    realm: "{{ ipareplica_realm | default(ipaserver_realm) | default(omit) }}"
     hostname: "{{ ipareplica_hostname | default(ansible_facts['fqdn']) }}"
     ca_cert_files: "{{ ipareplica_ca_cert_files | default([]) }}"
     hidden_replica: "{{ ipareplica_hidden_replica }}"
@@ -104,6 +102,9 @@
   register: result_ipareplica_test
 
 - name: Install - Deploy replica
+  when: not ansible_check_mode and
+        not (result_ipareplica_test.client_already_configured is defined or
+             result_ipareplica_test.server_already_configured is defined)
   block:
   # This block is executed only when
   # not ansible_check_mode and
@@ -226,7 +227,7 @@
   - name: Install - Set dirman password
     no_log: yes
     ansible.builtin.set_fact:
-      ipareplica_dirman_password:
+      __derived_dirman_password:
         "{{ result_ipareplica_master_password.password }}"
 
   - name: Install - Setup certmonger
@@ -267,7 +268,7 @@
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
       config_master_host_name:
         "{{ result_ipareplica_prepare.config_master_host_name }}"
@@ -305,13 +306,13 @@
       ccache: "{{ result_ipareplica_prepare.ccache }}"
       installer_ccache: "{{ result_ipareplica_prepare.installer_ccache }}"
       _ca_enabled: "{{ result_ipareplica_prepare._ca_enabled }}"
-      _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info  if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
+      _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
       subject_base: "{{ result_ipareplica_prepare.subject_base }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
       config_master_host_name:
         "{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -355,7 +356,7 @@
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
 
   - name: Install - Setup KRB
@@ -370,9 +371,9 @@
       config_master_host_name:
         "{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
       ccache: "{{ result_ipareplica_prepare.ccache }}"
-      _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info  if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
+      _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   # We need to point to the master in ipa default conf when certmonger
   # asks for HTTP certificate in newer ipa versions. In these versions
@@ -413,7 +414,7 @@
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
       master:
         "{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -437,7 +438,7 @@
       _dirsrv_pkcs12_info: "{{ result_ipareplica_prepare._dirsrv_pkcs12_info if result_ipareplica_prepare._dirsrv_pkcs12_info != None else omit }}"
       _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
 
   - name: Install - Setup http
@@ -458,7 +459,7 @@
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _http_pkcs12_info: "{{ result_ipareplica_prepare._http_pkcs12_info if result_ipareplica_prepare._http_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   # Need to point back to ourself after the cert for HTTP is obtained
   - name: Install - Create original IPA conf again
@@ -497,7 +498,7 @@
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
     when: result_ipareplica_test.change_master_for_certmonger
 
@@ -516,7 +517,7 @@
       ccache: "{{ result_ipareplica_prepare.ccache }}"
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   - name: Install - Setup custodia
     ipareplica_setup_custodia:
@@ -537,7 +538,7 @@
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   - name: Install - Setup CA
     ipareplica_setup_ca:
@@ -560,7 +561,7 @@
       _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
       _random_serial_numbers: "{{ result_ipareplica_prepare._random_serial_numbers }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
       config_master_host_name:
         "{{ result_ipareplica_install_ca_certs.config_master_host_name }}"
@@ -585,7 +586,7 @@
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   - name: Install - DS apply updates
     ipareplica_ds_apply_updates:
@@ -605,7 +606,7 @@
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _pkinit_pkcs12_info: "{{ result_ipareplica_prepare._pkinit_pkcs12_info if result_ipareplica_prepare._pkinit_pkcs12_info != None else omit }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       ds_ca_subject: "{{ result_ipareplica_setup_ds.ds_ca_subject }}"
 
   - name: Install - Setup kra
@@ -645,7 +646,7 @@
       _add_to_ipaservers: "{{ result_ipareplica_prepare._add_to_ipaservers }}"
       _ca_subject: "{{ result_ipareplica_prepare._ca_subject }}"
       _subject_base: "{{ result_ipareplica_prepare._subject_base }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
     when: result_ipareplica_test.setup_kra
 
   - name: Install - Restart KDC
@@ -663,7 +664,7 @@
       ccache: "{{ result_ipareplica_prepare.ccache }}"
       _ca_file: "{{ result_ipareplica_prepare._ca_file }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
 
   - name: Install - Custodia import dm password
     ipareplica_custodia_import_dm_password:
@@ -684,7 +685,7 @@
       _kra_enabled: "{{ result_ipareplica_prepare._kra_enabled }}"
       _kra_host_name: "{{ result_ipareplica_prepare.config_kra_host_name }}"
       _top_dir: "{{ result_ipareplica_prepare._top_dir }}"
-      dirman_password: "{{ ipareplica_dirman_password }}"
+      dirman_password: "{{ __derived_dirman_password }}"
       config_setup_ca: "{{ result_ipareplica_prepare.config_setup_ca }}"
 
   - name: Install - Promote SSSD
@@ -793,7 +794,3 @@
     - "/etc/ipa/.tmp_pkcs12_dirsrv"
     - "/etc/ipa/.tmp_pkcs12_http"
     - "/etc/ipa/.tmp_pkcs12_pkinit"
-
-  when: not ansible_check_mode and
-        not (result_ipareplica_test.client_already_configured is defined or
-             result_ipareplica_test.server_already_configured is defined)
diff --git a/roles/ipaserver/meta/main.yml b/roles/ipaserver/meta/main.yml
index 86a05f9b73b9dcbd1fed6422a5fde26cefe50f41..89c7a3848a179bdd1d181c5eacc3a058f31b02c4 100644
--- a/roles/ipaserver/meta/main.yml
+++ b/roles/ipaserver/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to setup an iPA domain server
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipaserver/tasks/copy_external_cert.yml b/roles/ipaserver/tasks/copy_external_cert.yml
index 8d30aca4219a91d6972cd16f75b44a992ae1f890..6adcaa085fbe602ec82145e2472e72e33c663ad5 100644
--- a/roles/ipaserver/tasks/copy_external_cert.yml
+++ b/roles/ipaserver/tasks/copy_external_cert.yml
@@ -1,14 +1,18 @@
 ---
-- name: Install - Initialize ipaserver_external_cert_files
-  ansible.builtin.set_fact:
-     ipaserver_external_cert_files: []
-  when: ipaserver_external_cert_files is undefined
-- name: Install - Copy "{{ item }}" "{{ inventory_hostname }}':/root/'{{ item | basename }}"
-  ansible.builtin.copy:
-    src: "{{ item }}"
-    dest: "/root/{{ item | basename }}"
-    mode: preserve
-    force: yes
-- name: Install - Extend ipaserver_external_cert_files with "/root/{{ item | basename }}"
-  ansible.builtin.set_fact:
-    ipaserver_external_cert_files: "{{ ipaserver_external_cert_files + [ '/root/' + (item | basename) ] }}"
+- name: Copy external certificates
+  vars:
+    __item_basename: "{{ item | basename }}"
+  block:
+  - name: Install - Initialize ipaserver_external_cert_files
+    ansible.builtin.set_fact:
+      ipaserver_external_cert_files: []
+    when: ipaserver_external_cert_files is undefined
+  - name: Install - Copy "{{ item + " " + inventory_hostname + ':/root/' + __item_basename }}"
+    ansible.builtin.copy:
+      src: "{{ item }}"
+      dest: "/root/{{ __item_basename }}"
+      mode: preserve
+      force: yes
+  - name: Install - Extend ipaserver_external_cert_files with "/root/{{ __item_basename }}"
+    ansible.builtin.set_fact:
+      ipaserver_external_cert_files: "{{ ipaserver_external_cert_files + ['/root/' + (__item_basename)] }}"
diff --git a/roles/ipaserver/tasks/install.yml b/roles/ipaserver/tasks/install.yml
index bfb5b7403d83f7ab6b9069b88a6d337e0ef53ff4..f4906dc9d67b74380d0674c618051e3f1e6b730e 100644
--- a/roles/ipaserver/tasks/install.yml
+++ b/roles/ipaserver/tasks/install.yml
@@ -2,6 +2,7 @@
 # tasks file for ipaserver
 
 - name: Install - Package installation
+  when: ipaserver_install_packages | bool
   block:
   - name: Install - Ensure that IPA server packages are installed
     ansible.builtin.package:
@@ -26,9 +27,9 @@
       state: present
     when: ipaserver_setup_firewalld | bool
 
-  when: ipaserver_install_packages | bool
 
 - name: Install - Firewall configuration
+  when: ipaserver_setup_firewalld | bool
   block:
   - name: Firewalld service - Ensure that firewalld is running
     ansible.builtin.systemd:
@@ -51,9 +52,7 @@
       >/dev/null
     when: ipaserver_firewalld_zone is defined
 
-  when: ipaserver_setup_firewalld | bool
-
-- name: Include tasks "{{ role_path }}/tasks/copy_external_cert.yml"
+- name: Copy external certs
   ansible.builtin.include_tasks: "{{ role_path }}/tasks/copy_external_cert.yml"
   with_items: "{{ ipaserver_external_cert_files_from_controller }}"
   when: ipaserver_external_cert_files_from_controller is defined and
@@ -131,14 +130,13 @@
   register: result_ipaserver_test
 
 - name: Install - Deploy server
+  when: not ansible_check_mode and not
+        (not result_ipaserver_test.changed and
+         (result_ipaserver_test.client_already_configured is defined or
+          result_ipaserver_test.server_already_configured is defined))
   block:
-  # This block is executed only when
-  # not ansible_check_mode and
-  # not (not result_ipaserver_test.changed and
-  #      (result_ipaserver_test.client_already_configured is defined or
-  #       result_ipaserver_test.server_already_configured is defined)
-
   - name: Install - Obtain master password
+    when: ipaserver_master_password is undefined
     block:
     - name: Install - Master password creation
       no_log: yes
@@ -150,10 +148,14 @@
     - name: Install - Use new master password
       no_log: yes
       ansible.builtin.set_fact:
-        ipaserver_master_password:
+        __derived_master_password:
           "{{ result_ipaserver_master_password.password }}"
 
-    when: ipaserver_master_password is undefined
+  - name: Use user defined master password, if provided
+    when: ipaserver_master_password is defined
+    no_log: yes
+    ansible.builtin.set_fact:
+      __derived_master_password: "{{ ipaserver_master_password }}"
 
   - name: Install - Server preparation
     ipaserver_prepare:
@@ -212,7 +214,7 @@
     ipaserver_setup_ds:
       dm_password: "{{ ipadm_password }}"
       password: "{{ ipaadmin_password }}"
-      # master_password: "{{ ipaserver_master_password }}"
+      # master_password: "{{ __derived_master_password }}"
       domain: "{{ result_ipaserver_test.domain }}"
       realm: "{{ result_ipaserver_test.realm | default(omit) }}"
       hostname: "{{ result_ipaserver_test.hostname }}"
@@ -241,7 +243,7 @@
     ipaserver_setup_krb:
       dm_password: "{{ ipadm_password }}"
       password: "{{ ipaadmin_password }}"
-      master_password: "{{ ipaserver_master_password }}"
+      master_password: "{{ __derived_master_password }}"
       domain: "{{ result_ipaserver_test.domain }}"
       realm: "{{ result_ipaserver_test.realm }}"
       hostname: "{{ result_ipaserver_test.hostname }}"
@@ -274,7 +276,7 @@
     ipaserver_setup_ca:
       dm_password: "{{ ipadm_password }}"
       password: "{{ ipaadmin_password }}"
-      master_password: "{{ ipaserver_master_password }}"
+      master_password: "{{ __derived_master_password }}"
       # ip_addresses: "{{ result_ipaserver_prepare.ip_addresses }}"
       domain: "{{ result_ipaserver_test.domain }}"
       realm: "{{ result_ipaserver_test.realm }}"
@@ -312,7 +314,7 @@
       _http_ca_cert: "{{ result_ipaserver_test._http_ca_cert }}"
     register: result_ipaserver_setup_ca
 
-  - name: Copy /root/ipa.csr to "{{ inventory_hostname }}-ipa.csr"
+  - name: Copy /root/ipa.csr to "{{ inventory_hostname + '-ipa.csr' }}"
     ansible.builtin.fetch:
       src: /root/ipa.csr
       dest: "{{ inventory_hostname }}-ipa.csr"
@@ -321,6 +323,7 @@
           ipaserver_copy_csr_to_controller | bool
 
   - name: Install - Configure services
+    when: not result_ipaserver_setup_ca.csr_generated | bool
     block:
     - name: Install - Setup otpd
       ipaserver_setup_otpd:
@@ -332,7 +335,7 @@
       ipaserver_setup_http:
         dm_password: "{{ ipadm_password }}"
         password: "{{ ipaadmin_password }}"
-        master_password: "{{ ipaserver_master_password }}"
+        master_password: "{{ __derived_master_password }}"
         domain: "{{ result_ipaserver_test.domain }}"
         realm: "{{ result_ipaserver_test.realm }}"
         hostname: "{{ result_ipaserver_test.hostname }}"
@@ -476,8 +479,6 @@
         {{ "--add-service=ntp" if not ipaclient_no_ntp | bool else "" }}
       when: ipaserver_setup_firewalld | bool
 
-    when: not result_ipaserver_setup_ca.csr_generated | bool
-
   always:
   - name: Cleanup temporary files
     ansible.builtin.file:
@@ -487,8 +488,3 @@
     - "/etc/ipa/.tmp_pkcs12_dirsrv"
     - "/etc/ipa/.tmp_pkcs12_http"
     - "/etc/ipa/.tmp_pkcs12_pkinit"
-
-  when: not ansible_check_mode and not
-        (not result_ipaserver_test.changed and
-         (result_ipaserver_test.client_already_configured is defined or
-          result_ipaserver_test.server_already_configured is defined))
diff --git a/roles/ipasmartcard_client/meta/main.yml b/roles/ipasmartcard_client/meta/main.yml
index 26ef55df94f3c962e7aab8399895a141698cc019..73d3a9d1c330b102287220561d611ec5e329545a 100644
--- a/roles/ipasmartcard_client/meta/main.yml
+++ b/roles/ipasmartcard_client/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to setup IPA server(s) for Smart Card authentication
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipasmartcard_client/tasks/main.yml b/roles/ipasmartcard_client/tasks/main.yml
index 54ced7773c578fe5e3a5b942780820c6558b23b3..8c87a80fec1ed3c98e7f61d97472f72947f68313 100644
--- a/roles/ipasmartcard_client/tasks/main.yml
+++ b/roles/ipasmartcard_client/tasks/main.yml
@@ -2,7 +2,8 @@
 # tasks file for ipasmartcard_client role
 
 - name: Uninstall smartcard client
-  ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported"
+  ansible.builtin.fail:
+    msg: "Uninstalling smartcard for IPA is not supported"
   when: state|default('present') == 'absent'
 
 - name: Import variables specific to distribution
@@ -36,7 +37,8 @@
   # Fail on empty "ipasmartcard_client_ca_certs"
 
   - name: Fail on empty "ipasmartcard_client_ca_certs"
-    ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_client_ca_certs'"
+    ansible.builtin.fail:
+      msg: "No CA certs given in 'ipasmartcard_client_ca_certs'"
     when: ipasmartcard_client_ca_certs is not defined or
           ipasmartcard_client_ca_certs | length < 1
 
@@ -68,13 +70,13 @@
       ipaadmin_principal: admin
     when: ipaadmin_principal is undefined
 
-  - name: kinit using "{{ ipaadmin_principal }}" password
+  - name: Authenticate using kinit with password for "{{ ipaadmin_principal }}"
     ansible.builtin.command: kinit "{{ ipaadmin_principal }}"
     args:
       stdin: "{{ ipaadmin_password }}"
     when: ipaadmin_password is defined
 
-  - name: kinit using "{{ ipaadmin_principal }}" keytab
+  - name: Authenticate using kinit with keytab for "{{ ipaadmin_principal }}"
     ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}"
     when: ipaadmin_keytab is defined
 
@@ -101,6 +103,7 @@
   # Ensure /etc/sssd/pki exists
 
   - name: Prepare for authselect
+    when: ipasmartcard_client_vars.USE_AUTHSELECT
     block:
     - name: Ensure /etc/sssd/pki exists
       ansible.builtin.file:
@@ -113,8 +116,6 @@
         path: /etc/sssd/pki/sssd_auth_ca_db.pem
         state: absent
 
-    when: ipasmartcard_client_vars.USE_AUTHSELECT
-
   # Upload smartcard CA certificates to systemwide db
 
   - name: Upload smartcard CA certificates to systemwide db
@@ -171,5 +172,5 @@
   ### ALWAYS ###
 
   always:
-  - name: kdestroy
+  - name: Destroy Kerberos tickets
     ansible.builtin.command: kdestroy -A
diff --git a/roles/ipasmartcard_server/meta/main.yml b/roles/ipasmartcard_server/meta/main.yml
index 26ef55df94f3c962e7aab8399895a141698cc019..73d3a9d1c330b102287220561d611ec5e329545a 100644
--- a/roles/ipasmartcard_server/meta/main.yml
+++ b/roles/ipasmartcard_server/meta/main.yml
@@ -6,15 +6,15 @@ galaxy_info:
   description: A role to setup IPA server(s) for Smart Card authentication
   company: Red Hat, Inc
   license: GPLv3
-  min_ansible_version: 2.8
+  min_ansible_version: "2.8"
   platforms:
   - name: Fedora
     versions:
     - all
   - name: EL
     versions:
-    - 7
-    - 8
+    - "7"
+    - "8"
   galaxy_tags:
     - identity
     - ipa
diff --git a/roles/ipasmartcard_server/tasks/main.yml b/roles/ipasmartcard_server/tasks/main.yml
index 6f0fa28e4b1b16f4bb25c586634c26126b18431a..c650511eb8083d7012116fee7cdb02146e708d3d 100644
--- a/roles/ipasmartcard_server/tasks/main.yml
+++ b/roles/ipasmartcard_server/tasks/main.yml
@@ -2,7 +2,8 @@
 # tasks file for ipasmartcard_server role
 
 - name: Uninstall smartcard server
-  ansible.builtin.fail: msg="Uninstalling smartcard for IPA is not supported"
+  ansible.builtin.fail:
+    msg: "Uninstalling smartcard for IPA is not supported"
   when: state|default('present') == 'absent'
 
 - name: Import variables specific to distribution
@@ -27,7 +28,8 @@
 
   # Fail on empty "ipasmartcard_server_ca_certs"
   - name: Fail on empty "ipasmartcard_server_ca_certs"
-    ansible.builtin.fail: msg="No CA certs given in 'ipasmartcard_server_ca_certs'"
+    ansible.builtin.fail:
+      msg: "No CA certs given in 'ipasmartcard_server_ca_certs'"
     when: ipasmartcard_server_ca_certs is not defined or
           ipasmartcard_server_ca_certs | length < 1
 
@@ -40,7 +42,7 @@
 
   # INSTALL bind-utils
 
-  - name: Ensure {{ ipasmartcard_server_bindutils_packages }} are installed
+  - name: Ensure bind utilities packages are installed
     ansible.builtin.package:
       name: "{{ ipasmartcard_server_bindutils_packages }}"
       state: present
@@ -53,13 +55,13 @@
       ipaadmin_principal: admin
     when: ipaadmin_principal is undefined
 
-  - name: kinit using "{{ ipaadmin_principal }}" password
+  - name: Athenticate with kinit and password for "{{ ipaadmin_principal }}"
     ansible.builtin.command: kinit "{{ ipaadmin_principal }}"
     args:
       stdin: "{{ ipaadmin_password }}"
     when: ipaadmin_password is defined
 
-  - name: kinit using "{{ ipaadmin_principal }}" keytab
+  - name: Authenticate with kinit and keytab for "{{ ipaadmin_principal }}"
     ansible.builtin.command: kinit -kt "{{ ipaadmin_keytab }}" "{{ ipaadmin_principal }}"
     when: ipaadmin_keytab is defined
 
@@ -70,12 +72,13 @@
     register: result_ipa_server_show
 
   - name: Fail if not an IPA server
-    ansible.builtin.fail: msg="Not an IPA server"
+    ansible.builtin.fail:
+      msg: "Not an IPA server"
     when: result_ipa_server_show.failed
 
   - name: Get Domain from server-find server name
     ansible.builtin.set_fact:
-      ipaserver_domain: "{{ (result_ipa_server_show.stdout | regex_search('cn: (.+)', '\\1'))[0].split('.')[1:] | join ('.') }}"
+      ipaserver_domain: "{{ (result_ipa_server_show.stdout | regex_search('cn: (.+)', '\\1'))[0].split('.')[1:] | join('.') }}"
     when: ipaserver_domain is not defined
 
   - name: Get ipa-ca records
@@ -83,7 +86,8 @@
     register: result_get_ipaca_records
 
   - name: Fail if ipa-ca records are not resolvable
-    ansible.builtin.fail: msg="ipa-ca records are not resolvable"
+    ansible.builtin.fail:
+      msg: "ipa-ca records are not resolvable"
     when: result_get_ipaca_records.failed or
           result_get_ipaca_records.stdout | length == 0
 
@@ -164,10 +168,10 @@
   # HTTPD IFP
 
   - name: Allow HTTPD ifp
+    when: ipasmartcard_server_vars.allow_httpd_ifp
     block:
 
     # Allow Apache to access SSSD IFP
-
     - name: Allow Apache to access SSSD IFP
       ansible.builtin.command: "{{ ipasmartcard_server_vars.python_interpreter }}"
       args:
@@ -188,11 +192,10 @@
         name: sssd
         state: restarted
 
-    when: ipasmartcard_server_vars.allow_httpd_ifp
-
   # Ensure /etc/sssd/pki exists
 
   - name: Prepare for authselect
+    when: ipasmartcard_server_vars.USE_AUTHSELECT
     block:
     - name: Ensure /etc/sssd/pki exists
       ansible.builtin.file:
@@ -205,8 +208,6 @@
         path: /etc/sssd/pki/sssd_auth_ca_db.pem
         state: absent
 
-    when: ipasmartcard_server_vars.USE_AUTHSELECT
-
   # Upload smartcard CA certificates to systemwide db
 
   - name: Upload smartcard CA certificates to systemwide db
@@ -246,5 +247,5 @@
   ### ALWAYS ###
 
   always:
-  - name: kdestroy
+  - name: Destroy Kereberos tickets
     ansible.builtin.command: kdestroy -A