--- - name: Verify hosts: all any_errors_fatal: true tasks: - name: Reboot host become: true ansible.builtin.reboot: - name: Wait for the host and reconnect ansible.builtin.wait_for: port: 22 host: '{{ (ansible_ssh_host | default(ansible_host)) | default(inventory_hostname) }}' delay: 10 timeout: 120 - name: Include default vars ansible.builtin.include_vars: dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/" extensions: - 'yml' - name: Include host vars ansible.builtin.include_vars: file: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/inventory/host_vars/{{ ansible_hostname }}" - name: Set sysctl configuration directory as fact tags: - fact - sysctl block: - name: Stat /usr/lib/sysctl.d/ exists ansible.builtin.stat: path: "/usr/lib/sysctl.d/" register: usr_lib_sysctl_d - name: Set sysctl fact ansible.builtin.set_fact: sysctl_conf_dir: "{{ '/usr/lib/sysctl.d' if usr_lib_sysctl_d.stat.exists else '/etc/sysctl.d' }}" - name: Debug sysctl ansible.builtin.debug: msg: "{{ sysctl_conf_dir }}" - name: Get installed ssh version ansible.builtin.command: cmd: ssh -V changed_when: false failed_when: false register: ssh_version tags: - sshd - sshd_config - name: Set ssh version as fact ansible.builtin.set_fact: ssh_installed_version: "{{ ssh_version.stderr | regex_search('^OpenSSH_([0-9+].[0-9]+)', '\\1') | join('.') | float }}" # noqa jinja[spacing] tags: - sshd - sshd_config - name: Stat IPv6 status become: true ansible.builtin.stat: path: /proc/sys/net/ipv6 register: stat_ipv6 - name: Set IPv6 fact ansible.builtin.set_fact: system_has_ipv6: "{{ stat_ipv6.stat.exists }}" - name: Update current facts ansible.builtin.setup: ~ - name: Verify auditd configuration become: true ansible.builtin.lineinfile: dest: /etc/audit/auditd.conf line: "{{ item }}" state: present check_mode: true register: auditd_conf failed_when: auditd_conf is changed with_items: - action_mail_acct = {{ auditd_action_mail_acct }} - admin_space_left_action = {{ auditd_admin_space_left_action }} - disk_error_action = {{ auditd_disk_error_action }} - disk_full_action = {{ auditd_disk_full_action }} - max_log_file = {{ auditd_max_log_file }} - max_log_file_action = {{ auditd_max_log_file_action }} - name_format = hostname - num_logs = {{ auditd_num_logs }} - space_left = {{ auditd_space_left }} - space_left_action = {{ auditd_space_left_action }} - name: Verify systemd system.conf become: true ansible.builtin.lineinfile: dest: /etc/systemd/system.conf line: "{{ item }}" state: present check_mode: true register: systemd_system_conf failed_when: systemd_system_conf is changed with_items: - DumpCore=no - CrashShell=no - CtrlAltDelBurstAction=none - DefaultLimitCORE=0 - DefaultLimitNOFILE={{ limit_nofile_hard }} - DefaultLimitNPROC={{ limit_nproc_hard }} - name: Verify systemd user.conf become: true ansible.builtin.lineinfile: dest: /etc/systemd/user.conf line: "{{ item }}" state: present check_mode: true register: systemd_user_conf failed_when: systemd_user_conf is changed with_items: - DefaultLimitCORE=0 - DefaultLimitNOFILE={{ limit_nofile_hard }} - DefaultLimitNPROC={{ limit_nproc_hard }} - name: Verify postfix configuration become: true ansible.builtin.lineinfile: dest: /etc/postfix/main.cf line: "{{ item }}" state: present check_mode: true register: postfix_conf failed_when: postfix_conf is changed with_items: - disable_vrfy_command = yes - inet_interfaces = loopback-only - smtpd_banner = \$myhostname - ESMTP - smtpd_client_restrictions = permit_mynetworks,reject - name: Stat /etc/ssh/sshd_config ansible.builtin.stat: path: '/etc/ssh/sshd_config' register: sshd_config_file - name: Stat /etc/ssh/ssh_config ansible.builtin.stat: path: '/etc/ssh/ssh_config' register: ssh_config_file - name: Stat /etc/ssh/sshd_config.d/01-hardening.conf become: true ansible.builtin.stat: path: '/etc/ssh/sshd_config.d/01-hardening.conf' register: sshd_config_directory - name: Assert /etc/ssh/ssh_config permissions ansible.builtin.assert: that: - ssh_config_file.stat.mode == "0644" success_msg: "{{ ssh_config_file.stat.path }} has correct permissions: {{ ssh_config_file.stat.mode }}" fail_msg: "{{ ssh_config_file.stat.path }} permissions are incorrect: {{ ssh_config_file.stat.mode }}" - name: Assert /etc/ssh/sshd_config permissions ansible.builtin.assert: that: - sshd_config_file.stat.mode == "0600" success_msg: "{{ sshd_config_file.stat.path }} has correct permissions: {{ sshd_config_file.stat.mode }}" fail_msg: "{{ sshd_config_file.stat.path }} permissions are incorrect: {{ sshd_config_file.stat.mode }}" - name: Assert /etc/ssh/sshd_config.d/01-hardening.conf permissions ansible.builtin.assert: that: - sshd_config_directory.stat.mode == "0600" success_msg: "{{ sshd_config_directory.stat.path }} has correct permissions: {{ sshd_config_directory.stat.mode }}" fail_msg: "{{ sshd_config_directory.stat.path }} permissions are incorrect: {{ sshd_config_directory.stat.mode }}" when: sshd_config_directory.stat.exists - name: Verify sshd_config PermitRootLogin configuration become: true ansible.builtin.lineinfile: dest: /etc/ssh/sshd_config line: "PermitRootLogin yes" state: absent check_mode: true register: sshd_config failed_when: sshd_config is changed - name: Verify sshd_config.d PermitRootLogin configuration become: true ansible.builtin.lineinfile: dest: /etc/ssh/sshd_config.d/01-hardening.conf line: "PermitRootLogin yes" state: absent check_mode: true register: sshd_config_d failed_when: sshd_config_d is changed when: sshd_config_directory.stat.exists - name: Verify sshd_config configuration become: true ansible.builtin.lineinfile: dest: /etc/ssh/sshd_config line: "{{ item }}" state: present check_mode: true register: sshd_config failed_when: sshd_config is changed with_items: - AllowGroups vagrant sudo - AllowAgentForwarding {{ sshd_allow_agent_forwarding }} - AllowTcpForwarding {{ sshd_allow_tcp_forwarding }} - AuthenticationMethods {{ sshd_authentication_methods }} - Banner {{ sshd_banner }} - ChallengeResponseAuthentication {{ sshd_challenge_response_authentication }} - Ciphers {{ sshd_ciphers }} - ClientAliveCountMax {{ sshd_client_alive_count_max | int }} - ClientAliveInterval {{ sshd_client_alive_interval | int }} - Compression {{ sshd_compression }} - GSSAPIAuthentication {{ sshd_gssapi_authentication }} - HostbasedAuthentication {{ sshd_hostbased_authentication }} - IgnoreUserKnownHosts {{ sshd_ignore_user_known_hosts }} - KerberosAuthentication {{ sshd_kerberos_authentication }} - HostKeyAlgorithms {{ sshd_host_key_algorithms }} - KexAlgorithms {{ sshd_kex_algorithms }} - LogLevel {{ sshd_log_level }} - LoginGraceTime {{ sshd_login_grace_time | int }} - Macs {{ sshd_macs }} - MaxAuthTries {{ sshd_max_auth_tries | int }} - MaxSessions {{ sshd_max_sessions | int }} - MaxStartups {{ sshd_max_startups }} - PasswordAuthentication {{ sshd_password_authentication }} - PermitEmptyPasswords {{ sshd_permit_empty_passwords }} - PermitRootLogin {{ sshd_permit_root_login }} - PermitUserEnvironment {{ sshd_permit_user_environment }} - Port {{ sshd_port | int }} - PrintLastLog {{ sshd_print_last_log }} - PrintMotd {{ sshd_print_motd }} - RekeyLimit {{ sshd_rekey_limit }} - StrictModes {{ sshd_strict_modes }} - TCPKeepAlive {{ sshd_tcp_keep_alive }} - UseDNS {{ sshd_use_dns }} - UsePAM {{ sshd_use_pam }} - X11Forwarding {{ sshd_x11_forwarding }} when: not sshd_config_directory.stat.exists - name: Verify sshd_config.d configuration become: true ansible.builtin.lineinfile: dest: /etc/ssh/sshd_config.d/01-hardening.conf line: "{{ item }}" state: present check_mode: true register: sshd_config failed_when: sshd_config is changed with_items: - AllowGroups vagrant sudo - AllowAgentForwarding {{ sshd_allow_agent_forwarding }} - AllowTcpForwarding {{ sshd_allow_tcp_forwarding }} - AuthenticationMethods {{ sshd_authentication_methods }} - Banner {{ sshd_banner }} - ChallengeResponseAuthentication {{ sshd_challenge_response_authentication }} - Ciphers {{ sshd_ciphers }} - ClientAliveCountMax {{ sshd_client_alive_count_max | int }} - ClientAliveInterval {{ sshd_client_alive_interval | int }} - Compression {{ sshd_compression }} - GSSAPIAuthentication {{ sshd_gssapi_authentication }} - HostbasedAuthentication {{ sshd_hostbased_authentication }} - IgnoreUserKnownHosts {{ sshd_ignore_user_known_hosts }} - KerberosAuthentication {{ sshd_kerberos_authentication }} - HostKeyAlgorithms {{ sshd_host_key_algorithms }} - KexAlgorithms {{ sshd_kex_algorithms }} - LogLevel {{ sshd_log_level }} - LoginGraceTime {{ sshd_login_grace_time | int }} - Macs {{ sshd_macs }} - MaxAuthTries {{ sshd_max_auth_tries | int }} - MaxSessions {{ sshd_max_sessions | int }} - MaxStartups {{ sshd_max_startups }} - PasswordAuthentication {{ sshd_password_authentication }} - PermitEmptyPasswords {{ sshd_permit_empty_passwords }} - PermitRootLogin {{ sshd_permit_root_login }} - PermitUserEnvironment {{ sshd_permit_user_environment }} - Port {{ sshd_port | int }} - PrintLastLog {{ sshd_print_last_log }} - PrintMotd {{ sshd_print_motd }} - RekeyLimit {{ sshd_rekey_limit }} - StrictModes {{ sshd_strict_modes }} - Subsystem {{ sshd_subsystem }} - TCPKeepAlive {{ sshd_tcp_keep_alive }} - UseDNS {{ sshd_use_dns }} - UsePAM {{ sshd_use_pam }} - X11Forwarding {{ sshd_x11_forwarding }} when: sshd_config_directory.stat.exists - name: Verify sshd RequiredRSASize become: true ansible.builtin.lineinfile: dest: /etc/ssh/sshd_config.d/01-hardening.conf line: "RequiredRSASize {{ sshd_required_rsa_size }}" state: present check_mode: true register: sshd_config_rsasize failed_when: sshd_config_rsasize is changed when: sshd_config_directory.stat.exists and ssh_installed_version is version('9.1', '>') - name: Verify sshd runtime configuration become: true environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.command: sshd -T register: sshd_config changed_when: false failed_when: item not in sshd_config.stdout_lines with_items: - allowgroups sudo - allowgroups vagrant - allowagentforwarding {{ sshd_allow_agent_forwarding }} - allowtcpforwarding {{ sshd_allow_tcp_forwarding }} - authenticationmethods {{ sshd_authentication_methods }} - banner {{ sshd_banner }} - clientalivecountmax {{ sshd_client_alive_count_max | int }} - clientaliveinterval {{ sshd_client_alive_interval | int }} - compression {{ sshd_compression }} - logingracetime {{ sshd_login_grace_time | int }} - macs {{ sshd_macs }} - maxauthtries {{ sshd_max_auth_tries | int }} - maxsessions {{ sshd_max_sessions | int }} - maxstartups {{ sshd_max_startups }} - permitrootlogin {{ sshd_permit_root_login }} - port {{ sshd_port | int }} - subsystem {{ sshd_subsystem }} - usedns {{ sshd_use_dns }} - usepam {{ sshd_use_pam }} - x11forwarding {{ sshd_x11_forwarding }} - name: Verify ssh_config configuration become: true ansible.builtin.lineinfile: dest: /etc/ssh/ssh_config line: " {{ item }}" state: present check_mode: true register: ssh_config failed_when: ssh_config is changed with_items: - Ciphers {{ sshd_ciphers }} - GSSAPIAuthentication {{ sshd_gssapi_authentication }} - HashKnownHosts yes - HostKeyAlgorithms {{ sshd_host_key_algorithms }} - KexAlgorithms {{ sshd_kex_algorithms }} - MACs {{ sshd_macs }} - RekeyLimit {{ sshd_rekey_limit }} - name: Merge sysctl settings ansible.builtin.set_fact: sysctl_settings: "{{ generic_sysctl_settings | combine(ipv4_sysctl_settings) }}" - name: Verify sysctl configuration become: true ansible.builtin.shell: | set -o pipefail grep -R "^{{ item.key }}.*{{ item.value | int }}$" "{{ sysctl_conf_dir }}/" args: executable: /bin/bash with_dict: "{{ sysctl_settings | dict2items | rejectattr('key', 'search', 'nf_conntrack') | items2dict }}" register: sysctl_file_config failed_when: sysctl_file_config.rc != 0 changed_when: sysctl_file_config.rc != 0 - name: Verify sysctl runtime configuration become: true ansible.builtin.shell: | set -o pipefail sysctl -a | grep "^{{ item.key }}.*{{ item.value | int }}$" args: executable: /bin/bash with_dict: "{{ sysctl_settings | dict2items | rejectattr('key', 'search', 'nf_conntrack') | items2dict }}" register: sysctl_runtime_config failed_when: sysctl_runtime_config.rc != 0 changed_when: sysctl_runtime_config.rc != 0 - name: Stat crypto-policies config become: true ansible.builtin.stat: path: /etc/crypto-policies/config register: crypto_policies_config - name: Get crypto-policies value environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.shell: grep "^{{ crypto_policy | upper }}$" /etc/crypto-policies/config register: crypto_policy_set when: crypto_policies_config.stat.exists failed_when: crypto_policy_set.rc != 0 changed_when: crypto_policy_set.rc != 0 - name: Verify noexec /dev/shm ansible.builtin.shell: grep '^tmpfs\s/dev/shm.*noexec' /proc/mounts register: dev_shm_noexec failed_when: dev_shm_noexec.rc != 0 changed_when: dev_shm_noexec.rc != 0 - name: Verify /proc ansible.builtin.shell: | set -o pipefail grep -E '^proc\s/proc proc rw,nosuid,nodev,noexec,relatime.*hidepid=({{ hide_pid }}|invisible)' /proc/mounts args: executable: /bin/bash register: proc_opts failed_when: proc_opts.rc != 0 changed_when: proc_opts.rc != 0 tags: - skip_ansible_lint - name: Verify tmp.mount become: true ansible.builtin.systemd: name: tmp.mount register: tmp_mount failed_when: not 'nosuid,nodev,noexec' in tmp_mount.status.Options changed_when: not 'nosuid,nodev,noexec' in tmp_mount.status.Options - name: Verify /tmp become: true ansible.builtin.shell: grep '^tmpfs\s/tmp\s.*nosuid,nodev,noexec' /proc/mounts register: mounts_tmp failed_when: mounts_tmp.rc != 0 changed_when: mounts_tmp.rc != 0 - name: Verify login.defs settings become: true ansible.builtin.shell: grep "^{{ item }}$" /etc/login.defs register: login_defs failed_when: login_defs.rc != 0 changed_when: login_defs.rc != 0 with_items: - ENCRYPT_METHOD SHA512 - FAILLOG_ENAB yes - LOG_OK_LOGINS yes - PASS_MAX_DAYS 60 - PASS_MIN_DAYS 1 - PASS_WARN_AGE 7 - SHA_CRYPT_MAX_ROUNDS 65536 - SHA_CRYPT_MIN_ROUNDS 10000 - SU_NAME su - SYSLOG_SG_ENAB yes - SYSLOG_SU_ENAB yes - UMASK {{ umask_value }} - name: Verify pwquality.conf settings become: true ansible.builtin.lineinfile: path: /etc/security/pwquality.conf line: "{{ item.key }} = {{ item.value }}" state: present mode: "0644" owner: root group: root check_mode: true register: pwquality_conf failed_when: pwquality_conf is changed with_dict: "{{ pwquality_config }}" - name: Verify RedHat GRUB audit settings become: true ansible.builtin.shell: | set -o pipefail grubby --info="/boot/vmlinuz-$(uname -r)" | grep "^args.*{{ grub_audit_cmdline }} {{ grub_audit_backlog_cmdline }}" register: audit_grubenv failed_when: audit_grubenv.rc != 0 changed_when: audit_grubenv.rc != 0 when: ansible_os_family == "RedHat" - name: Verify Debian audit GRUB settings become: true ansible.builtin.shell: grep "linux.*{{ grub_audit_cmdline }} {{ grub_audit_backlog_cmdline }}" /boot/grub/grub.cfg register: audit_grub_cfg failed_when: audit_grub_cfg.rc != 0 changed_when: audit_grub_cfg.rc != 0 when: ansible_os_family == "Debian" - name: Verify RedHat GRUB IPv6 settings become: true ansible.builtin.shell: cmd: | set -o pipefail grubby --info="/boot/vmlinuz-$(uname -r)" | grep "ipv6.disable=1" register: audit_grubenv failed_when: audit_grubenv.rc != 0 changed_when: audit_grubenv.rc != 0 when: ansible_os_family == "RedHat" and disable_ipv6 - name: Verify Debian GRUB IPv6 settings become: true ansible.builtin.shell: cmd: grep "linux.*ipv6.disable=1" /boot/grub/grub.cfg register: audit_grub_cfg failed_when: audit_grub_cfg.rc != 0 changed_when: audit_grub_cfg.rc != 0 when: ansible_os_family == "Debian" and disable_ipv6 - name: Verify IPv6 sysctl configuration become: true ansible.builtin.shell: grep -R "^{{ item }}$" /etc/sysctl.* /usr/lib/sysctl.d/* register: sysctl_ipv6_config failed_when: sysctl_ipv6_config.rc != 0 changed_when: sysctl_ipv6_config.rc != 0 with_items: - net.ipv6.conf.all.disable_ipv6=1 - net.ipv6.conf.default.disable_ipv6=1 when: disable_ipv6 - name: Verify IPv6 sysctl runtime configuration environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin become: true ansible.builtin.shell: | set -o pipefail sysctl -a | grep "^{{ item.key }}.*{{ item.value | int }}$" args: executable: /bin/bash with_dict: "{{ ipv6_disable_sysctl_settings }}" register: sysctl_ipv6 changed_when: sysctl_ipv6.rc != 0 failed_when: sysctl_ipv6.rc != 0 when: system_has_ipv6 and disable_ipv6 - name: Find GRUB config files become: true ansible.builtin.find: paths: /boot patterns: "*.cfg,grubenv" recurse: true register: perm_grub_cfg - name: Verify GRUB config files permission become: true ansible.builtin.stat: path: "{{ item.path }}" changed_when: item.mode | int > 644 failed_when: item.mode | int > 644 with_items: - "{{ perm_grub_cfg.files | reject('search', '/boot/efi/EFI/ubuntu/grub.cfg') | list }}" loop_control: label: "{{ item.path }}" - name: Verify systemd resolved.conf become: true ansible.builtin.shell: grep "^{{ item }}$" /etc/systemd/resolved.conf register: systemd_resolved_conf failed_when: systemd_resolved_conf.rc != 0 changed_when: systemd_resolved_conf.rc != 0 with_items: - DNS={{ dns }} - FallbackDNS={{ fallback_dns }} - DNSSEC={{ dnssec }} - DNSOverTLS={{ dns_over_tls }} - name: Verify systemd timesyncd.conf become: true ansible.builtin.shell: grep "^{{ item }}$" /etc/systemd/timesyncd.conf register: systemd_timesyncd_conf failed_when: systemd_timesyncd_conf.rc != 0 changed_when: systemd_timesyncd_conf.rc != 0 with_items: - NTP={{ ntp }} - FallbackNTP={{ fallback_ntp }} - name: Stat /etc/default/motd-news ansible.builtin.stat: path: /etc/default/motd-news register: motd_news - name: Verify masked motdnews service become: true ansible.builtin.systemd: name: motd-news.service masked: true enabled: false state: stopped check_mode: true register: motdnews_service failed_when: motdnews_service is changed when: ansible_os_family == "Debian" and motd_news.stat.exists - name: Stat /usr/bin/pro ansible.builtin.stat: path: /usr/bin/pro register: ubuntu_advantage_pro when: ansible_os_family == "Debian" - name: Verify that apt_news is disabled ansible.builtin.shell: | set -o pipefail pro config show | grep '^apt_news.*False' args: executable: /bin/bash register: ubuntu_advantage_pro_state changed_when: ubuntu_advantage_pro_state.rc != 0 failed_when: ubuntu_advantage_pro_state.rc != 0 when: ansible_os_family == "Debian" and ubuntu_advantage_pro.stat.exists - name: Efi fact ansible.builtin.set_fact: booted_with_efi: "{{ ansible_mounts | selectattr('mount', 'equalto', '/boot/efi') | list | length > 0 }}" - name: Verify cracklib password list environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.shell: | set -o pipefail grep '/usr/share/dict/passwords' /var/cache/cracklib/src-dicts args: executable: /bin/bash register: cracklib_passwords failed_when: cracklib_passwords.rc != 0 changed_when: cracklib_passwords.rc != 0 when: ansible_os_family == "Debian" - name: Index blacklisted kernel modules environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.shell: | set -o pipefail modprobe -c | grep -o '^blacklist .*' | awk '{print $2}' args: executable: /bin/bash changed_when: false failed_when: modprobe_blacklist.rc != 0 register: modprobe_blacklist when: block_blacklisted | bool - name: Verify blocked blacklisted kernel modules environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.shell: | set -o pipefail grep 'install {{ item }} /bin/true' /etc/modprobe.d/blockblacklisted.conf args: executable: /bin/bash register: modprobe_blockblacklisted failed_when: modprobe_blockblacklisted.rc != 0 changed_when: modprobe_blockblacklisted.rc != 0 with_items: - "{{ modprobe_blacklist.stdout_lines | sort }}" when: block_blacklisted | bool - name: Verify sudo settings ansible.builtin.shell: | set -o pipefail sudo -l | grep "{{ item }}" register: sudo_settings failed_when: sudo_settings.rc != 0 changed_when: sudo_settings.rc != 0 args: executable: /bin/bash with_items: - 'use_pty' - 'logfile="/var/log/sudo.log"' - '!pwfeedback' - '!visiblepw' - 'passwd_timeout=1' - 'timestamp_timeout=5' - 'timestamp_type=tty' - "!rootpw" - "!runaspw" - "!targetpw" - name: Stat faillock.conf become: true ansible.builtin.stat: path: /etc/security/faillock.conf register: faillockconf - name: Verify faillock.conf become: true ansible.builtin.lineinfile: dest: /etc/security/faillock.conf mode: "0644" state: present line: "{{ item }}" check_mode: true register: verify_faillock failed_when: verify_faillock is changed with_items: - "audit" - "local_users_only" - "deny = 5" - "fail_interval = 900" when: faillockconf.stat.exists - name: Verify wireless state environment: PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ansible.builtin.command: nmcli -t radio register: wireless_state changed_when: not 'disabled' in wireless_state.stdout failed_when: not 'disabled' in wireless_state.stdout when: disable_wireless - name: Stat firewall rules when UFW is enabled become: true ansible.builtin.shell: | set -o pipefail ufw show added | grep '^ufw' | grep -v "'ansible\smanaged'" | sed 's/ufw //g' args: executable: /bin/bash failed_when: ufw_not_managed.rc > 1 changed_when: false register: ufw_not_managed when: ufw_enable - name: Stat firewall rules when UFW is disabled become: true ansible.builtin.shell: | set -o pipefail ufw show added | grep '^ufw' | grep "'ansible\smanaged'" | sed 's/ufw //g' args: executable: /bin/bash failed_when: ufw_not_managed.rc > 1 changed_when: false register: ufw_not_managed when: not ufw_enable - name: Create test user become: true ansible.builtin.user: name: roletestuser password: "{{ 'Ansible Role Test User' | password_hash('sha512') }}" state: present shell: /bin/bash - name: Create test user salt ansible.builtin.set_fact: test_user_salt: "{{ lookup('password', '/dev/null chars=ascii_lowercase,ascii_uppercase,digits length=16') }}" - name: Change test user password become: true ansible.builtin.user: name: roletestuser password: "{{ 'roletestuser' | password_hash('sha512', test_user_salt, rounds=656000) }}" register: test_user_pass - name: Debug test user salt ansible.builtin.debug: msg: "{{ test_user_salt }}" - name: Debug test user password ansible.builtin.debug: msg: "{{ test_user_pass }}" ...