From fd0422637b7a9ee8008496868bc111b162d1f34a Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Wed, 15 Jul 2026 18:33:52 +0300 Subject: [PATCH 01/18] Fixed a race condition in `accounts` where Windows AD group membership modification was tried before account existed --- .../accounts/tasks/create_domain_groups.yml | 41 ----------------- .../roles/accounts/tasks/windows_create.yml | 44 ++++++++++++++++++- 2 files changed, 42 insertions(+), 43 deletions(-) delete mode 100644 nova/core/roles/accounts/tasks/create_domain_groups.yml diff --git a/nova/core/roles/accounts/tasks/create_domain_groups.yml b/nova/core/roles/accounts/tasks/create_domain_groups.yml deleted file mode 100644 index 3989b44d..00000000 --- a/nova/core/roles/accounts/tasks/create_domain_groups.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -- name: Creating Domain groups... - microsoft.ad.group: - name: "{{ item.name }}" - scope: "{{ item.scope | default(domainlocal) }}" - path: "{{ item.ou | default(omit) }}" - description: "{{ item.description | default(omit) }}" - loop: "{{ domain_groups }}" - loop_control: - label: "{{ item.name | default('') }}" - when: item.members is not defined - -- name: Configuring following domain groups and replacing members... - microsoft.ad.group: - name: "{{ item.name }}" - scope: "{{ item.scope | default(domainlocal) }}" - path: "{{ item.ou | default(omit) }}" - description: "{{ item.description | default(omit) }}" - members: - set: "{{ item.members }}" - loop: "{{ domain_groups }}" - loop_control: - label: "{{ item.members | default(['no_members']) | join(', ') }} to {{ item.name | default('') }}" - when: - - item.members is defined - - accounts_domain_groups_members_strategy == 'replace' - -- name: Configuring following domain groups and adding members... - microsoft.ad.group: - name: "{{ item.name }}" - scope: "{{ item.scope | default(domainlocal) }}" - path: "{{ item.ou | default(omit) }}" - description: "{{ item.description | default(omit) }}" - members: - add: "{{ item.members }}" - loop: "{{ domain_groups }}" - loop_control: - label: "{{ item.members | default(['no_members']) | join(', ') }} to {{ item.name | default('') }}" - when: - - item.members is defined - - accounts_domain_groups_members_strategy == 'add' diff --git a/nova/core/roles/accounts/tasks/windows_create.yml b/nova/core/roles/accounts/tasks/windows_create.yml index edf690b1..1130d051 100644 --- a/nova/core/roles/accounts/tasks/windows_create.yml +++ b/nova/core/roles/accounts/tasks/windows_create.yml @@ -54,8 +54,16 @@ - name: Creating OUs for {{ ad_domain_name | default('') }}... ansible.windows.win_shell: "{{ lookup('ansible.builtin.template', 'Create-OUs.ps1') }}" - - name: Configuring Domain groups... - ansible.builtin.include_tasks: create_domain_groups.yml + # Creating all domain groups without members first, since some members may not exist yet + - name: Creating following Domain groups... + microsoft.ad.group: + name: "{{ item.name }}" + scope: "{{ item.scope | default('domainlocal') }}" + path: "{{ item.ou | default(omit) }}" + description: "{{ item.description | default(omit) }}" + loop: "{{ domain_groups }}" + loop_control: + label: "{{ item.name | default('') }}" - name: Creating following domain accounts... microsoft.ad.user: @@ -87,6 +95,38 @@ win_domain_user.results | selectattr('changed', 'equalto', true) | map(attribute='item.username') | list | intersect([ansible_deployer_username]) | length > 0 + # Configuring all domains groups with members now that all domain accounts have been created with replace strategy + - name: Configuring following domain groups and replacing members... + microsoft.ad.group: + name: "{{ item.name }}" + scope: "{{ item.scope | default('domainlocal') }}" + path: "{{ item.ou | default(omit) }}" + description: "{{ item.description | default(omit) }}" + members: + set: "{{ item.members }}" + loop: "{{ domain_groups }}" + loop_control: + label: "{{ item.members | default(['no_members']) | join(', ') }} to {{ item.name | default('') }}" + when: + - item.members is defined + - accounts_domain_groups_members_strategy == 'replace' + + # Configuring all domains groups with members now that all domain accounts have been created with add strategy + - name: Configuring following domain groups and adding members... + microsoft.ad.group: + name: "{{ item.name }}" + scope: "{{ item.scope | default('domainlocal') }}" + path: "{{ item.ou | default(omit) }}" + description: "{{ item.description | default(omit) }}" + members: + add: "{{ item.members }}" + loop: "{{ domain_groups }}" + loop_control: + label: "{{ item.members | default(['no_members']) | join(', ') }} to {{ item.name | default('') }}" + when: + - item.members is defined + - accounts_domain_groups_members_strategy == 'add' + # To make sure that we are setting the password for the correct account on non-primary domain controller - name: Setting correct password for {{ ansible_deployer_username }}... ansible.builtin.set_fact: From a39ee3e585b6ba1642a6c9ba1dcedecca5c2f291 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 11:43:38 +0300 Subject: [PATCH 02/18] Set default GCP instance disk to SSD --- nova/core/roles/machine_operations/defaults/main.yml | 3 +++ nova/core/roles/machine_operations/tasks/google/create.yml | 2 +- .../machine_operations/templates/Configure-CloudWindows.ps1 | 1 + .../templates/google_cloud_linux_startup_script.yml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nova/core/roles/machine_operations/defaults/main.yml b/nova/core/roles/machine_operations/defaults/main.yml index 158787d7..3d736de9 100644 --- a/nova/core/roles/machine_operations/defaults/main.yml +++ b/nova/core/roles/machine_operations/defaults/main.yml @@ -256,6 +256,9 @@ machine_operations_google_assign_public_ip: true machine_operations_google_temp_sshd_config_path: /etc/ssh/sshd_config.d/00-{{ infra_env }}-first-run.conf +# The default disk type to use for the VM +google_cloud_disk_type: pd-ssd + # Will try and pre-create the network for a VM based on the interfaces variable # Set to false if network(s) are already pre-created by other means machine_operations_google_create_networks: true diff --git a/nova/core/roles/machine_operations/tasks/google/create.yml b/nova/core/roles/machine_operations/tasks/google/create.yml index a0c6d3e4..8e3975ed 100644 --- a/nova/core/roles/machine_operations/tasks/google/create.yml +++ b/nova/core/roles/machine_operations/tasks/google/create.yml @@ -43,7 +43,7 @@ boot: true initialize_params: disk_size_gb: "{{ os_disk_size | default(hardware_primary_disk_size) | default(omit) }}" - disk_type: "{{ google_cloud_disk_type | default(omit) }}" + disk_type: "{{ google_cloud_disk_type }}" source_image: "{{ google_cloud_vm_image.path ~ '/' ~ google_image_info.resources[-1].name | default(omit) }}" network_interfaces: "{{ omit if not fresh_deploy else lookup('ansible.builtin.template', 'google_cloud_interfaces.yml') | from_yaml }}" metadata: diff --git a/nova/core/roles/machine_operations/templates/Configure-CloudWindows.ps1 b/nova/core/roles/machine_operations/templates/Configure-CloudWindows.ps1 index 7a94326e..3668fb54 100644 --- a/nova/core/roles/machine_operations/templates/Configure-CloudWindows.ps1 +++ b/nova/core/roles/machine_operations/templates/Configure-CloudWindows.ps1 @@ -17,6 +17,7 @@ windows-startup-script-ps1: | Start-Transcript -Path $LogPath {% if infra_env in ["aws", "google"] %} + # This is a temporary password for the Administrator user, which will be changed as soon as the accounts role is reached. Write-Host "Setting Administrator password" $Password = "{{ template_password }}" $SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force diff --git a/nova/core/roles/machine_operations/templates/google_cloud_linux_startup_script.yml b/nova/core/roles/machine_operations/templates/google_cloud_linux_startup_script.yml index 76740bcc..b93393df 100644 --- a/nova/core/roles/machine_operations/templates/google_cloud_linux_startup_script.yml +++ b/nova/core/roles/machine_operations/templates/google_cloud_linux_startup_script.yml @@ -8,6 +8,7 @@ startup-script: | MaxAuthTries 30 EOF systemctl restart ssh + # This is a temporary password for the root user, which will be changed as soon as the accounts role is reached. echo root:{{ template_password }} | chpasswd # Needed during os_configuration role From 4a07870bca229cfb8a3526249a367e9327b096ce Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 11:44:04 +0300 Subject: [PATCH 03/18] Added a feature to define source IP ranges for GCP firewalls --- nova/core/galaxy.yml | 2 +- nova/core/roles/machine_operations/defaults/main.yml | 2 ++ .../machine_operations/tasks/google/create_firewall_rules.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 4d524f71..9a411949 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 11.1.0 +version: 11.2.0 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/machine_operations/defaults/main.yml b/nova/core/roles/machine_operations/defaults/main.yml index 3d736de9..08830dab 100644 --- a/nova/core/roles/machine_operations/defaults/main.yml +++ b/nova/core/roles/machine_operations/defaults/main.yml @@ -277,3 +277,5 @@ machine_operations_google_firewall_rule: name: "{{ item.cloud_id }}-allow-all" # The cloud_id comes from the interfaces list allowed: - ip_protocol: all + source_ranges: + - 0.0.0.0/0 diff --git a/nova/core/roles/machine_operations/tasks/google/create_firewall_rules.yml b/nova/core/roles/machine_operations/tasks/google/create_firewall_rules.yml index 0aab8fd2..1d30c1ba 100644 --- a/nova/core/roles/machine_operations/tasks/google/create_firewall_rules.yml +++ b/nova/core/roles/machine_operations/tasks/google/create_firewall_rules.yml @@ -3,6 +3,7 @@ google.cloud.gcp_compute_firewall: name: "{{ machine_operations_google_firewall_rule.name }}" allowed: "{{ machine_operations_google_firewall_rule.allowed }}" + source_ranges: "{{ machine_operations_google_firewall_rule.source_ranges }}" network: selfLink: "{{ create_network_result.results[loop_index].selfLink }}" state: present From ec4f29b034b4c7a147a6e3b506d898b75cf53dd5 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 11:45:04 +0300 Subject: [PATCH 04/18] Linted and refactored `win_configure_rdp` role --- .../secrets_to_vault/tasks/check_and_add.yml | 6 ++- .../roles/win_configure_rdp/tasks/disable.yml | 7 ---- .../roles/win_configure_rdp/tasks/enable.yml | 7 ---- .../roles/win_configure_rdp/tasks/main.yml | 40 ++++++++++++------- 4 files changed, 29 insertions(+), 31 deletions(-) delete mode 100644 nova/core/roles/win_configure_rdp/tasks/disable.yml delete mode 100644 nova/core/roles/win_configure_rdp/tasks/enable.yml diff --git a/nova/core/roles/secrets_to_vault/tasks/check_and_add.yml b/nova/core/roles/secrets_to_vault/tasks/check_and_add.yml index cc6f901f..25ce178b 100644 --- a/nova/core/roles/secrets_to_vault/tasks/check_and_add.yml +++ b/nova/core/roles/secrets_to_vault/tasks/check_and_add.yml @@ -38,7 +38,8 @@ X-Vault-Request: true X-Vault-Token: "{{ vault_access_token | default(auth.json.auth.client_token) }}" Content-Type: application/merge-patch+json - body: "{{ {'data': {account_key_name if sct.username is defined else sct.key: + body: + "{{ {'data': {account_key_name if sct.username is defined else sct.key: (sct.value | default(sct.password) | default(secrets_to_vault_autogenerated_key_secret))}} }}" body_format: json validate_certs: "{{ validate_vault_certs }}" @@ -56,7 +57,8 @@ X-Vault-Request: true X-Vault-Token: "{{ vault_access_token | default(auth.json.auth.client_token) }}" Content-Type: application/merge-patch+json - body: "{{ {'data': {account_key_name if sct.username is defined else sct.key: + body: + "{{ {'data': {account_key_name if sct.username is defined else sct.key: (sct.value | default(sct.password) | default(secrets_to_vault_autogenerated_key_secret))}} }}" body_format: json validate_certs: "{{ validate_vault_certs }}" diff --git a/nova/core/roles/win_configure_rdp/tasks/disable.yml b/nova/core/roles/win_configure_rdp/tasks/disable.yml deleted file mode 100644 index 0e3a4206..00000000 --- a/nova/core/roles/win_configure_rdp/tasks/disable.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- name: Disabling RDP... - ansible.windows.win_regedit: - path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ - name: fDenyTSConnections - data: 1 - type: dword diff --git a/nova/core/roles/win_configure_rdp/tasks/enable.yml b/nova/core/roles/win_configure_rdp/tasks/enable.yml deleted file mode 100644 index de050496..00000000 --- a/nova/core/roles/win_configure_rdp/tasks/enable.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- name: Enabling RDP... - ansible.windows.win_regedit: - path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ - name: fDenyTSConnections - data: 0 - type: dword diff --git a/nova/core/roles/win_configure_rdp/tasks/main.yml b/nova/core/roles/win_configure_rdp/tasks/main.yml index 09a44a4d..1205907b 100644 --- a/nova/core/roles/win_configure_rdp/tasks/main.yml +++ b/nova/core/roles/win_configure_rdp/tasks/main.yml @@ -1,19 +1,29 @@ --- -- name: Including enable RDP task... - include_tasks: enable.yml +- name: Enabling RDP... when: configuration == 'enable' + block: + - name: Enabling RDP from registry... + ansible.windows.win_regedit: + path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ + name: fDenyTSConnections + data: 0 + type: dword -- name: Including disable RDP task... - include_tasks: disable.yml - when: configuration == 'disable' + - name: Enabling Windows Firewall rules for RDP... + community.windows.win_firewall_rule: + name: "{{ item }}" + enabled: true + loop: + - Remote Desktop - Shadow (TCP-In) + - Remote Desktop - User Mode (TCP-In) + - Remote Desktop - User Mode (UDP-In) + - Remote Desktop - (TCP-WS-In) + - Remote Desktop - (TCP-WSS-In) -- name: Enabling Windows Firewall rules for RDP... - community.windows.win_firewall_rule: - name: "{{ item }}" - enabled: yes - loop: - - Remote Desktop - Shadow (TCP-In) - - Remote Desktop - User Mode (TCP-In) - - Remote Desktop - User Mode (UDP-In) - - Remote Desktop - (TCP-WS-In) - - Remote Desktop - (TCP-WSS-In) +- name: Disabling RDP... + ansible.windows.win_regedit: + path: HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\ + name: fDenyTSConnections + data: 1 + type: dword + when: configuration == 'disable' From 37354ec40ed1d71954afa94a8ba0845cf2ee635f Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 16:28:52 +0300 Subject: [PATCH 05/18] Enabling compression by default on `caddy` --- nova/core/roles/caddy/defaults/main.yml | 1 + nova/core/roles/caddy/templates/Caddyfile_jinja | 3 +++ 2 files changed, 4 insertions(+) diff --git a/nova/core/roles/caddy/defaults/main.yml b/nova/core/roles/caddy/defaults/main.yml index 749c80d2..39e59ca2 100644 --- a/nova/core/roles/caddy/defaults/main.yml +++ b/nova/core/roles/caddy/defaults/main.yml @@ -39,3 +39,4 @@ caddy_certificates_folder: /srv/certs/ caddy_auto_https: "disable_certs" # https://caddyserver.com/docs/caddyfile/options#auto-https caddy_ocsp_stapling: false caddy_use_tls: true # Set to false to disable TLS for all servers or add it as a variable per server +caddy_enable_compression: true # Set to false to disable compression for all servers or add it as a variable per server diff --git a/nova/core/roles/caddy/templates/Caddyfile_jinja b/nova/core/roles/caddy/templates/Caddyfile_jinja index 5feaa94c..c8eb0dde 100644 --- a/nova/core/roles/caddy/templates/Caddyfile_jinja +++ b/nova/core/roles/caddy/templates/Caddyfile_jinja @@ -22,6 +22,9 @@ } } + {% if caddy_enable_compression %} + encode + {% endif %} request_body { max_size {{ server.caddy_server_request_body_size | default("10MB") }} } From 9b536e941f055b04bd29999633f3fab483bace49 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 16:28:31 +0300 Subject: [PATCH 06/18] Refactored how MacOS users get created in `accounts` role to be more robust --- .../roles/accounts/tasks/macos_create.yml | 59 +++++++++++++++---- .../roles/accounts/tasks/macos_remove.yml | 26 +++++--- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/nova/core/roles/accounts/tasks/macos_create.yml b/nova/core/roles/accounts/tasks/macos_create.yml index ff1eaf81..f3fb2fbb 100644 --- a/nova/core/roles/accounts/tasks/macos_create.yml +++ b/nova/core/roles/accounts/tasks/macos_create.yml @@ -1,17 +1,43 @@ --- +- name: MISSING OR INCORRECT VARIABLE(S) + ansible.builtin.fail: + msg: | + The **ansible_deployer_password** variable is required and **connection_no_password** variable must be false. + MacOS requires a password as secure token to create and edit user accounts. + when: ansible_deployer_password | default(false) is ansible.builtin.falsy + or connection_no_password | default(false) is ansible.builtin.truthy + +# This required so this role can clean up the default cloud users if they are not in the local_accounts_with_password list +- name: Adding source creation flag for default cloud account... + when: lookup('vars', infra_env ~ '_template_username') | default('') not in [admin_account, ''] + block: + - name: Checking if {{ lookup('vars', infra_env ~ '_template_username') | default('') }} user exists... + ansible.builtin.raw: | + if dscl . -list /Users | grep -q "^{{ lookup('vars', infra_env ~ '_template_username') | default('') }}$"; then + echo "User exists" + else + echo "User does not exist" + fi + changed_when: false + register: template_user + + - name: Adding account creation source flag for {{ lookup('vars', infra_env ~ '_template_username') | default('') }}... + ansible.builtin.raw: | + echo "This account was created with {{ ansible_role_name + }} role" > /Users/{{ lookup('vars', infra_env ~ '_template_username') | default('') }}/.created + chown {{ lookup('vars', infra_env ~ '_template_username') | default('') + }}:staff /Users/{{ lookup('vars', infra_env ~ '_template_username') | default('') }}/.created + changed_when: true + when: template_user.stdout.find('User exists') != -1 + - name: Configuring following users... ansible.builtin.raw: | if dscl . -list /Users | grep -q "^{{ item.username }}$"; then - - echo Setting existing {{ item.username }} user password - sysadminctl -resetPasswordFor {{ item.username }} -newPassword {{ - item.password }} -adminUser {{ ansible_user }} "{{ '' if ansible_password is not defined else '-adminPassword ' ~ ansible_password }}" - + sysadminctl -resetPasswordFor {{ item.username }} -newPassword '{{ item.password }}' -adminUser {{ ansible_user }} -adminPassword '{{ ansible_password }}' else - - echo Creating new {{ item.username }} account - sysadminctl -addUser {{ item.username }} -password {{ item.password }} -admin -shell /bin/zsh - + echo "Creating new {{ item.username }} account" + sysadminctl -addUser {{ item.username }} -password {{ item.password }} -admin -shell /bin/zsh + sysadminctl -adminUser {{ ansible_user }} -adminPassword {{ ansible_password }} -secureTokenOn {{ item.username }} -password {{ item.password }} fi changed_when: true loop: "{{ admin_accounts_with_password }}" @@ -48,8 +74,7 @@ - name: Adding ssh keys for... ansible.builtin.raw: | line="$( echo {{ item.ssh_key }} )" - if [ "{{ admin_account }}" = "{{ item.username }}" ]; - then + if [ "{{ admin_account }}" = "{{ item.username }}" ]; then echo "Adding ssh key to root user" file=/var/root/.ssh/authorized_keys mkdir -p /var/root/.ssh/ @@ -66,3 +91,15 @@ loop_control: label: "{{ item.username }}" when: item.ssh_key is defined + +- name: Configuring SSHD in cloud environments... + when: accounts_configure_ssh_maxauthtries + block: + # This to avoid errors when someone has more that 6 SSH keys in their agent + - name: Setting SSHD MaxAuthTries to {{ accounts_ssh_maxauthtries }}... + ansible.builtin.raw: sed -i "" "s/.*MaxAuthTries.*/MaxAuthTries {{ accounts_ssh_maxauthtries }}/g" /etc/ssh/sshd_config + changed_when: true + + - name: Restarting SSHD... # noqa: no-handler + ansible.builtin.raw: launchctl kickstart -k system/com.openssh.sshd + changed_when: true diff --git a/nova/core/roles/accounts/tasks/macos_remove.yml b/nova/core/roles/accounts/tasks/macos_remove.yml index acc5cd3c..16c1983f 100644 --- a/nova/core/roles/accounts/tasks/macos_remove.yml +++ b/nova/core/roles/accounts/tasks/macos_remove.yml @@ -1,10 +1,11 @@ --- +# Ignoring symbolic links and only listing user accounts that have a .created file in their home directory, which indicates they were created by this role. - name: Listing user accounts created with {{ ansible_role_name }}... ansible.builtin.raw: | - for profile in /Users/*/; do - if [[ -f $profile/.created ]]; then - echo $(basename $profile) - fi + for profile in /Users/*; do + if [[ -f "$profile/.created" && ! -L "$profile" ]]; then + basename "$profile" + fi done register: home_profiles changed_when: false @@ -21,8 +22,10 @@ block: - name: Removing following accounts... ansible.builtin.raw: | - dscl . -delete /Users/{{ item }} - rm -rf /Users/{{ item }} + pkill -9 -u {{ item }} + sysadminctl -deleteUser {{ item }} + # rm -rf /Users/{{ item }} + # dscl . -delete /Users/{{ item }} loop: "{{ user_profiles_to_remove }}" changed_when: true @@ -37,10 +40,17 @@ # because the connection is lost before the reboot command completes. # To avoid this, we can ignore unreachable hosts after issuing the reboot command. ignore_unreachable: true + when: not darwin_with_python + + - name: Rebooting + ansible.builtin.reboot: + when: darwin_with_python - name: Removing following accounts... ansible.builtin.raw: | - dscl . -delete /Users/{{ item }} - rm -rf /Users/{{ item }} + pkill -9 -u {{ item }} + sysadminctl -deleteUser {{ item }} + # rm -rf /Users/{{ item }} + # dscl . -delete /Users/{{ item }} loop: "{{ user_profiles_to_remove }}" changed_when: true From cc79a34aa9edd050d24fb5931bd55868fb6d0ad5 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 17:39:14 +0300 Subject: [PATCH 07/18] Removed unmaintained ParrotOS support from `configure_package_mirrors` role --- .../tasks/parrot.yml | 43 ------------------- .../templates/parrot-sources-1.list | 3 -- .../templates/parrot-sources-2.list | 5 --- 3 files changed, 51 deletions(-) delete mode 100644 nova/core/roles/configure_package_mirrors/tasks/parrot.yml delete mode 100644 nova/core/roles/configure_package_mirrors/templates/parrot-sources-1.list delete mode 100644 nova/core/roles/configure_package_mirrors/templates/parrot-sources-2.list diff --git a/nova/core/roles/configure_package_mirrors/tasks/parrot.yml b/nova/core/roles/configure_package_mirrors/tasks/parrot.yml deleted file mode 100644 index 33ff0349..00000000 --- a/nova/core/roles/configure_package_mirrors/tasks/parrot.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -- name: Checking if sources.list backup exists... - ansible.builtin.stat: - path: /etc/apt/sources.list_backup - register: default_apt_backup_file - -- name: Backing up existing sources.list... - ansible.builtin.copy: - src: /etc/apt/sources.list - dest: /etc/apt/sources.list_backup - remote_src: true - mode: "0644" - when: not default_apt_backup_file.stat.exists - -- name: Checking if parrot.list (second file) backup exists... - ansible.builtin.stat: - path: /etc/apt/sources.list.d/parrot.list_backup - register: apt_backup_file - -- name: Backing up existing parrot.list - ansible.builtin.copy: - src: /etc/apt/sources.list.d/parrot.list - dest: /etc/apt/sources.list.d/parrot.list_backup - remote_src: true - mode: "0644" - when: not apt_backup_file.stat.exists - -- name: Templating custom parrot-sources.list to {{ inventory_hostname }}... - ansible.builtin.template: - src: "{{ item.src }}" - dest: "{{ item.dest }}" - mode: "0644" - register: apt_sources - loop: - - src: parrot-sources-1.list - dest: /etc/apt/sources.list - - src: parrot-sources-2.list - dest: /etc/apt/sources.list.d/parrot.list - -- name: Running apt update... - ansible.builtin.apt: - update_cache: true - when: apt_sources.results[0].changed or apt_sources.results[1].changed diff --git a/nova/core/roles/configure_package_mirrors/templates/parrot-sources-1.list b/nova/core/roles/configure_package_mirrors/templates/parrot-sources-1.list deleted file mode 100644 index 2a2282c5..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/parrot-sources-1.list +++ /dev/null @@ -1,3 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list_backup - -deb https://{{ nexus_fqdn }}/repository/parrot-lts/ lts main non-free contrib diff --git a/nova/core/roles/configure_package_mirrors/templates/parrot-sources-2.list b/nova/core/roles/configure_package_mirrors/templates/parrot-sources-2.list deleted file mode 100644 index 856127b6..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/parrot-sources-2.list +++ /dev/null @@ -1,5 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list.d/parrot.list_backup - -deb {{ package_mirror_parrot_uri.general }} parrot main contrib non-free -deb {{ package_mirror_parrot_uri.security }} parrot-security main contrib non-free -deb {{ package_mirror_parrot_uri.backports }} parrot-backports main contrib non-free From 53bf75933203719f9e65538dde7e658317c28444 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 16 Jul 2026 17:40:07 +0300 Subject: [PATCH 08/18] Refactored `configure_package_mirrors` role to use deb822 format --- .../tasks/debian.yml | 21 ++++++-- .../configure_package_mirrors/tasks/kali.yml | 37 +++++++------ .../configure_package_mirrors/tasks/main.yml | 53 ++++++++----------- .../tasks/ubuntu.yml | 47 +++++++++------- .../templates/debian-bookworm.list | 10 ---- .../templates/debian-bullseye.list | 10 ---- .../templates/debian-trixie.list | 10 ---- .../templates/debian.sources | 37 +++++++++++++ .../templates/kali-sources.list | 4 -- .../templates/kali.sources | 7 --- .../templates/ubuntu-sources.list | 15 ------ 11 files changed, 125 insertions(+), 126 deletions(-) delete mode 100644 nova/core/roles/configure_package_mirrors/templates/debian-bookworm.list delete mode 100644 nova/core/roles/configure_package_mirrors/templates/debian-bullseye.list delete mode 100644 nova/core/roles/configure_package_mirrors/templates/debian-trixie.list create mode 100644 nova/core/roles/configure_package_mirrors/templates/debian.sources delete mode 100644 nova/core/roles/configure_package_mirrors/templates/kali-sources.list delete mode 100644 nova/core/roles/configure_package_mirrors/templates/kali.sources delete mode 100644 nova/core/roles/configure_package_mirrors/templates/ubuntu-sources.list diff --git a/nova/core/roles/configure_package_mirrors/tasks/debian.yml b/nova/core/roles/configure_package_mirrors/tasks/debian.yml index 70c09d46..4c8172ff 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/debian.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/debian.yml @@ -12,14 +12,25 @@ mode: "0644" when: not apt_backup_file.stat.exists -- name: Templating custom debian-{{ ansible_facts.distribution_release }}.list to {{ inventory_hostname }}... +- name: Removing legacy /etc/apt/sources.list file... + ansible.builtin.file: + path: /etc/apt/sources.list + state: absent + +- name: Getting {{ inventory_hostname }} architecture... + ansible.builtin.command: dpkg --print-architecture + changed_when: false + register: dpkg_architecture + +- name: Templating debian.sources... ansible.builtin.template: - src: debian-{{ ansible_facts.distribution_release }}.list - dest: /etc/apt/sources.list + src: debian.sources + dest: /etc/apt/sources.list.d/debian.sources mode: "0644" - register: apt_sources + register: repos_changed - name: Running apt update... # noqa: no-handler ansible.builtin.apt: update_cache: true - when: apt_sources.changed + retries: 2 + when: repos_changed.changed diff --git a/nova/core/roles/configure_package_mirrors/tasks/kali.yml b/nova/core/roles/configure_package_mirrors/tasks/kali.yml index 287b75a6..f5aba132 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/kali.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/kali.yml @@ -20,13 +20,6 @@ mode: "0644" when: not kali_sources_backup.stat.exists - - name: Templating custom kali.sources to {{ inventory_hostname }}... - ansible.builtin.template: - src: kali.sources - dest: /etc/apt/sources.list.d/kali.sources - mode: "0644" - register: kali_sources_templated - - name: Configuring sources.list... when: not kali_sources.stat.exists block: @@ -43,16 +36,30 @@ mode: "0644" when: not apt_backup_file.stat.exists - - name: Templating custom kali.list to {{ inventory_hostname }}... - ansible.builtin.template: - src: kali-sources.list - dest: /etc/apt/sources.list - mode: "0644" - register: list_apt_sources +- name: Removing legacy /etc/apt/sources.list file... + ansible.builtin.file: + path: /etc/apt/sources.list + state: absent + +- name: Getting {{ inventory_hostname }} architecture... + ansible.builtin.command: dpkg --print-architecture + changed_when: false + register: dpkg_architecture + +- name: Adding Kali repository... + ansible.builtin.deb822_repository: + name: kali + architectures: + - "{{ dpkg_architecture.stdout }}" + types: [deb] + uris: "{{ package_mirror_kali_uri.general }}" + suites: [kali-rolling] + components: [main contrib non-free non-free-firmware] + signed_by: /usr/share/keyrings/kali-archive-keyring.gpg + register: repos_changed - name: Running apt update... # noqa: no-handler ansible.builtin.apt: update_cache: true retries: 2 - when: list_apt_sources.changed | default(false) - or kali_sources_templated.changed | default(false) + when: repos_changed.changed diff --git a/nova/core/roles/configure_package_mirrors/tasks/main.yml b/nova/core/roles/configure_package_mirrors/tasks/main.yml index 2678cecf..727701ac 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/main.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/main.yml @@ -1,35 +1,26 @@ --- -- name: Not including tasks for Ansible networks OS... - when: ansible_network_os is not defined - block: - - name: Including Windows Chocolatey mirror tasks... - ansible.builtin.include_tasks: choco.yml - when: ansible_facts.os_family | default('') == "Windows" - - - name: Configuring Debian family sources.list - when: ansible_facts.os_family | default('') == "Debian" - block: - - name: Including Kali mirror tasks... - ansible.builtin.include_tasks: kali.yml - when: - - ansible_facts.distribution | default('') == "Kali" - - package_mirror_kali_uri != {} +- name: Including Windows Chocolatey mirror tasks... + ansible.builtin.include_tasks: choco.yml + when: ansible_facts.os_family | default('') == "Windows" - - name: Including Ubuntu mirror tasks... - ansible.builtin.include_tasks: ubuntu.yml - when: - - ansible_facts.distribution | default('') == "Ubuntu" - - package_mirror_ubuntu_uri != {} +- name: Configuring Debian family sources.list + when: ansible_facts.os_family | default('') == "Debian" + block: + - name: Including Kali mirror tasks... + ansible.builtin.include_tasks: kali.yml + when: + - ansible_facts.distribution | default('') == "Kali" + - package_mirror_kali_uri != {} - - name: Including Debian mirror tasks... - ansible.builtin.include_tasks: debian.yml - when: - - ansible_facts.distribution | default('') == "Debian" - - customization_method != "proxmox" - - package_mirror_debian_uri != {} + - name: Including Ubuntu mirror tasks... + ansible.builtin.include_tasks: ubuntu.yml + when: + - ansible_facts.distribution | default('') == "Ubuntu" + - package_mirror_ubuntu_uri != {} - - name: Including Parrot mirror tasks... - ansible.builtin.include_tasks: parrot.yml - when: - - ansible_facts.distribution | default('') == "Parrot" - - package_mirror_parrot_uri != {} + - name: Including Debian mirror tasks... + ansible.builtin.include_tasks: debian.yml + when: + - ansible_facts.distribution | default('') == "Debian" + - customization_method != "proxmox" + - package_mirror_debian_uri != {} diff --git a/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml b/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml index 5610183d..2ad4531b 100644 --- a/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml +++ b/nova/core/roles/configure_package_mirrors/tasks/ubuntu.yml @@ -1,6 +1,11 @@ --- -- name: Configuring Ubuntu < 24.04 sources.list - when: ansible_facts.distribution_major_version | int < 24 +- name: Checking if ubuntu.sources is used... + ansible.builtin.stat: + path: /etc/apt/sources.list.d/ubuntu.sources + register: ubuntu_sources + +- name: Configuring ubuntu.sources... + when: not ubuntu_sources.stat.exists block: - name: Checking if sources.list backup exists... ansible.builtin.stat: @@ -15,15 +20,8 @@ mode: "0644" when: not legacy_apt_backup_file.stat.exists - - name: Templating custom ubuntu-sources.list to {{ inventory_hostname }}... - ansible.builtin.template: - src: ubuntu-sources.list - dest: /etc/apt/sources.list - mode: "0644" - register: legacy_apt_sources - -- name: Configuring Ubuntu 24.04+ sources.list - when: ansible_facts.distribution_major_version | int >= 24 +- name: Configuring ubuntu.sources... + when: ubuntu_sources.stat.exists block: - name: Checking if ubuntu.sources backup exists... ansible.builtin.stat: @@ -38,14 +36,25 @@ mode: "0644" when: not apt_backup_file.stat.exists - - name: Templating custom ubuntu-sources.list to {{ inventory_hostname }}... - ansible.builtin.template: - src: ubuntu.sources - dest: /etc/apt/sources.list.d/ubuntu.sources - mode: "0644" - register: apt_sources +- name: Removing legacy /etc/apt/sources.list file... + ansible.builtin.file: + path: /etc/apt/sources.list + state: absent + +- name: Getting {{ inventory_hostname }} architecture... + ansible.builtin.command: dpkg --print-architecture + changed_when: false + register: dpkg_architecture + +- name: Templating ubuntu.sources... + ansible.builtin.template: + src: ubuntu.sources + dest: /etc/apt/sources.list.d/ubuntu.sources + mode: "0644" + register: repos_changed -- name: Running apt update... +- name: Running apt update... # noqa: no-handler ansible.builtin.apt: update_cache: true - when: legacy_apt_sources.changed | default(false) or apt_sources.changed | default(false) + retries: 2 + when: repos_changed.changed diff --git a/nova/core/roles/configure_package_mirrors/templates/debian-bookworm.list b/nova/core/roles/configure_package_mirrors/templates/debian-bookworm.list deleted file mode 100644 index 130f6a87..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/debian-bookworm.list +++ /dev/null @@ -1,10 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list_backup - -deb {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main non-free-firmware -deb-src {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main non-free-firmware - -deb {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main non-free-firmware -deb-src {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main non-free-firmware - -deb {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main non-free-firmware -deb-src {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main non-free-firmware diff --git a/nova/core/roles/configure_package_mirrors/templates/debian-bullseye.list b/nova/core/roles/configure_package_mirrors/templates/debian-bullseye.list deleted file mode 100644 index f67e14a3..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/debian-bullseye.list +++ /dev/null @@ -1,10 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list_backup - -deb {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main -deb-src {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main - -deb {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main -deb-src {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main - -deb {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main -deb-src {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main diff --git a/nova/core/roles/configure_package_mirrors/templates/debian-trixie.list b/nova/core/roles/configure_package_mirrors/templates/debian-trixie.list deleted file mode 100644 index 130f6a87..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/debian-trixie.list +++ /dev/null @@ -1,10 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list_backup - -deb {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main non-free-firmware -deb-src {{ package_mirror_debian_uri.general }} {{ ansible_facts.lsb.codename }} main non-free-firmware - -deb {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main non-free-firmware -deb-src {{ package_mirror_debian_uri.security }} {{ ansible_facts.lsb.codename }}-security main non-free-firmware - -deb {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main non-free-firmware -deb-src {{ package_mirror_debian_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main non-free-firmware diff --git a/nova/core/roles/configure_package_mirrors/templates/debian.sources b/nova/core/roles/configure_package_mirrors/templates/debian.sources new file mode 100644 index 00000000..70797170 --- /dev/null +++ b/nova/core/roles/configure_package_mirrors/templates/debian.sources @@ -0,0 +1,37 @@ +# The original debian.sources is backed up as /etc/apt/sources.list_backup + +Types: deb +URIs: {{ package_mirror_debian_uri.general }} +Suites: {{ ansible_facts.lsb.codename }} +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb-src +URIs: {{ package_mirror_debian_uri.general }} +Suites: {{ ansible_facts.lsb.codename }} +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb +URIs: {{ package_mirror_debian_uri.security }} +Suites: {{ ansible_facts.lsb.codename }}-security +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb-src +URIs: {{ package_mirror_debian_uri.security }} +Suites: {{ ansible_facts.lsb.codename }}-security +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb +URIs: {{ package_mirror_debian_uri.updates }} +Suites: {{ ansible_facts.lsb.codename }}-updates +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg + +Types: deb-src +URIs: {{ package_mirror_debian_uri.updates }} +Suites: {{ ansible_facts.lsb.codename }}-updates +Components: main non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg \ No newline at end of file diff --git a/nova/core/roles/configure_package_mirrors/templates/kali-sources.list b/nova/core/roles/configure_package_mirrors/templates/kali-sources.list deleted file mode 100644 index 1b308877..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/kali-sources.list +++ /dev/null @@ -1,4 +0,0 @@ -# The original sources.list is backed up as /etc/apt/sources.list_backup - -deb {{ package_mirror_kali_uri.general }} kali-rolling main contrib non-free non-free-firmware -deb-src {{ package_mirror_kali_uri.general }} kali-rolling main contrib non-free non-free-firmware diff --git a/nova/core/roles/configure_package_mirrors/templates/kali.sources b/nova/core/roles/configure_package_mirrors/templates/kali.sources deleted file mode 100644 index c86f4cfd..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/kali.sources +++ /dev/null @@ -1,7 +0,0 @@ -# The original kali.sources is backed up as /etc/apt/kali.sources_backup - -Types: deb -URIs: {{ package_mirror_kali_uri.general }} -Suites: kali-rolling -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/kali-archive-keyring.gpg diff --git a/nova/core/roles/configure_package_mirrors/templates/ubuntu-sources.list b/nova/core/roles/configure_package_mirrors/templates/ubuntu-sources.list deleted file mode 100644 index 76b237bf..00000000 --- a/nova/core/roles/configure_package_mirrors/templates/ubuntu-sources.list +++ /dev/null @@ -1,15 +0,0 @@ -# The original sources.list is backed up as {{ '/etc/apt/sources.list_backup' if ansible_facts.distribution_release != 'noble' else '/etc/apt/ubuntu.sources_backup' }} - -deb {{ package_mirror_ubuntu_uri.general }} {{ ansible_facts.lsb.codename }} main restricted -deb {{ package_mirror_ubuntu_uri.general }} {{ ansible_facts.lsb.codename }} universe -deb {{ package_mirror_ubuntu_uri.general }} {{ ansible_facts.lsb.codename }} multiverse - -deb {{ package_mirror_ubuntu_uri.updates }} {{ ansible_facts.lsb.codename }}-updates main restricted -deb {{ package_mirror_ubuntu_uri.updates }} {{ ansible_facts.lsb.codename }}-updates universe -deb {{ package_mirror_ubuntu_uri.updates }} {{ ansible_facts.lsb.codename }}-updates multiverse - -deb {{ package_mirror_ubuntu_uri.security }} {{ ansible_facts.lsb.codename }}-security main restricted -deb {{ package_mirror_ubuntu_uri.security }} {{ ansible_facts.lsb.codename }}-security universe -deb {{ package_mirror_ubuntu_uri.security }} {{ ansible_facts.lsb.codename }}-security multiverse - -deb {{ package_mirror_ubuntu_uri.backports }} {{ ansible_facts.lsb.codename }}-backports main restricted universe multiverse From 48730cfeb0358d4d23813f937dcfd6d6b0cfe5c0 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Fri, 17 Jul 2026 00:59:03 +0300 Subject: [PATCH 09/18] Set `gitlab` version to 19.2.0 --- nova/core/roles/gitlab/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/core/roles/gitlab/defaults/main.yml b/nova/core/roles/gitlab/defaults/main.yml index 0a92e678..9e43f5be 100644 --- a/nova/core/roles/gitlab/defaults/main.yml +++ b/nova/core/roles/gitlab/defaults/main.yml @@ -1,6 +1,6 @@ --- ### gitlab general -gitlab_version: 19.1.2-ee.0 +gitlab_version: 19.2.0-ee.0 gitlab_ssh_port: 10022 # Increase gitlab ssh MaxAuthTries to avoid connection issues for users with more than 6 keys in their SSH agent set to {} to disable gitlab_ssh_max_auth_tries: 20 From 86f10aefaa2de565ff21e74e3839fb8fecf542ea Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Mon, 20 Jul 2026 12:17:11 +0300 Subject: [PATCH 10/18] Added a flag to toggle the disable of administrators_authorized_keys in `template_os_configuration` role --- nova/core/roles/template_os_configuration/defaults/main.yml | 3 +++ nova/core/roles/template_os_configuration/tasks/windows.yml | 1 + 2 files changed, 4 insertions(+) diff --git a/nova/core/roles/template_os_configuration/defaults/main.yml b/nova/core/roles/template_os_configuration/defaults/main.yml index 1c240c40..891528e3 100644 --- a/nova/core/roles/template_os_configuration/defaults/main.yml +++ b/nova/core/roles/template_os_configuration/defaults/main.yml @@ -8,6 +8,9 @@ template_os_configuration_update_system: true # Update OS during this role template_os_configuration_increase_maxauthtries: true template_os_configuration_increase_maxauthtries_value: 20 +# Will disable the default Windows SSHD config so that each user can use their own SSH key under $HOME/.ssh/authorized_keys +template_os_configuration_configure_administrators_authorized_keys: true + # Will create a fresh snapshot after all configuration is done on the template VM # Set to false to skip snapshot creation template_os_configuration_create_snapshot: true diff --git a/nova/core/roles/template_os_configuration/tasks/windows.yml b/nova/core/roles/template_os_configuration/tasks/windows.yml index 9b452a8e..fb2d8842 100644 --- a/nova/core/roles/template_os_configuration/tasks/windows.yml +++ b/nova/core/roles/template_os_configuration/tasks/windows.yml @@ -244,3 +244,4 @@ - regexp: ^.*AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys$ line: " #AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys" + when: template_os_configuration_configure_administrators_authorized_keys From c4612080dba95416358187765908129350030335 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Mon, 20 Jul 2026 12:19:20 +0300 Subject: [PATCH 11/18] Redesigned `accounts` role so all admin_accounts SSH keys are added to `administrators_authorized_keys` file --- .../roles/accounts/tasks/windows_create.yml | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/nova/core/roles/accounts/tasks/windows_create.yml b/nova/core/roles/accounts/tasks/windows_create.yml index 1130d051..95d646aa 100644 --- a/nova/core/roles/accounts/tasks/windows_create.yml +++ b/nova/core/roles/accounts/tasks/windows_create.yml @@ -189,22 +189,16 @@ - item.ssh_key is defined - not item.ssh_key_exclusive | default(accounts_exclusive_ssh_key) -- name: Creating administrators_authorized_keys file... - ansible.windows.win_file: - path: C:\ProgramData\ssh\administrators_authorized_keys - state: touch - -- name: Adding administrators_authorized_keys public keys for admins... - community.windows.win_lineinfile: - path: C:\ProgramData\ssh\administrators_authorized_keys - line: "{{ item.ssh_key }}" +# Adding keys to administrators_authorized_keys in case it's used. +# Careful with this feature since it allows any ssh key under administrators_authorized_keys to be used to login as any user in the Administrators group. +# This feature needs the following lines to be enabled in sshd_config: +# Match Group administrators +# AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys +- name: Adding admin_accounts ssh public keys to administrators_authorized_keys file... + ansible.windows.win_copy: + content: "{{ admin_accounts_with_password | selectattr('ssh_key', 'defined') | map(attribute='ssh_key') | list | unique | join('\n')}}" + dest: C:\ProgramData\ssh\administrators_authorized_keys create: true - loop: "{{ admin_accounts_with_password }}" - loop_control: - label: "{{ item.username }}" - when: - - item.username == ansible_deployer_username # Because C:\ProgramData\ssh\administrators_authorized_keys supports only one key - - item.ssh_key is defined - name: Configuring SSHD in cloud environments... when: accounts_configure_ssh_maxauthtries From 4b4bad2cbe7d066ebfb85b0b6993e25fbd84c454 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Thu, 23 Jul 2026 10:45:33 +0300 Subject: [PATCH 12/18] Set `outline` version to 1.9.2 --- nova/core/roles/outline/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/core/roles/outline/defaults/main.yml b/nova/core/roles/outline/defaults/main.yml index 23f36675..3bfb45bd 100644 --- a/nova/core/roles/outline/defaults/main.yml +++ b/nova/core/roles/outline/defaults/main.yml @@ -1,5 +1,5 @@ --- -outline_version: 1.9.1 +outline_version: 1.9.2 outline_postgres_version: 17 # POSTGRES_VERSION_TAG # Pre-generated secret key for Outline From 2db709dd4b9a1a30e456fd2d03b8a060b62f9b30 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Fri, 24 Jul 2026 12:25:00 +0300 Subject: [PATCH 13/18] Added a feature to update Debian OS firmware with `updates` role --- nova/core/galaxy.yml | 2 +- nova/core/roles/updates/defaults/main.yml | 3 +++ nova/core/roles/updates/tasks/debian_family.yml | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/nova/core/galaxy.yml b/nova/core/galaxy.yml index 9a411949..ec0b43c2 100644 --- a/nova/core/galaxy.yml +++ b/nova/core/galaxy.yml @@ -1,6 +1,6 @@ namespace: nova name: core -version: 11.2.0 +version: 11.3.0 readme: README.md authors: - https://github.com/novateams diff --git a/nova/core/roles/updates/defaults/main.yml b/nova/core/roles/updates/defaults/main.yml index 8cf7b00e..4bf3abcd 100644 --- a/nova/core/roles/updates/defaults/main.yml +++ b/nova/core/roles/updates/defaults/main.yml @@ -15,6 +15,9 @@ debian_family_unattended_upgrades_allow_reboot: false # Upgrade method for Debian family updates_debian_family_upgrade_method: dist +# Will try and update firmware on Debian family systems if fwupdmgr is installed +debian_family_updates_firmware: true + ################ # Linux RedHat # ################ diff --git a/nova/core/roles/updates/tasks/debian_family.yml b/nova/core/roles/updates/tasks/debian_family.yml index 2d9d0475..d9813331 100644 --- a/nova/core/roles/updates/tasks/debian_family.yml +++ b/nova/core/roles/updates/tasks/debian_family.yml @@ -12,6 +12,19 @@ retries: 3 delay: 3 +- name: Updating firmware... + ansible.builtin.shell: | + if [ -x "$(command -v fwupdmgr)" ]; then + fwupdmgr refresh --no-remote-check --assume-yes --force + fwupdmgr update --assume-yes --no-reboot-check + else + echo "fwupdmgr not found, skipping firmware update" + fi + register: debian_family_firmware_update + changed_when: debian_family_firmware_update.stdout is search("Successfully installed firmware") + failed_when: debian_family_firmware_update.rc == 1 + when: debian_family_updates_firmware + - name: Checking if reboot is required... ansible.builtin.stat: path: /var/run/reboot-required From 0897ba8dabab24812d24d7a9ae2c6f581226baa2 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Sun, 26 Jul 2026 19:35:55 +0300 Subject: [PATCH 14/18] Set `nexus` version to 3.94.1 --- nova/core/roles/nexus/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/core/roles/nexus/defaults/main.yml b/nova/core/roles/nexus/defaults/main.yml index 35193493..0f363a82 100644 --- a/nova/core/roles/nexus/defaults/main.yml +++ b/nova/core/roles/nexus/defaults/main.yml @@ -9,7 +9,7 @@ nexus_configure_ldap: false # Set to true to also configure LDAP after installat nexus_create_repos: false # Set to true to also create default repositories after installation nexus_allow_anonymous_access: true # Set to false to disable anonymous access nexus_active_encryption_key_id: Primary Encryption Key # Name of the active encryption key that comes with this role -nexus_version: 3.94.0 # Nexus version to install +nexus_version: 3.94.1 # Nexus version to install # Default is the built-in Nexus admin user. If set to a different value than admin, the role will create the user. nexus_admin_username: admin From 18bca908ddf0403e62d867a4ae2ce7660cc681ff Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Sun, 26 Jul 2026 19:36:50 +0300 Subject: [PATCH 15/18] Added a feature to upgrade OPNsense major version with the `updates` role --- .../machine_operations/tasks/aws/create.yml | 2 +- nova/core/roles/updates/defaults/main.yml | 7 ++++ nova/core/roles/updates/tasks/sense.yml | 39 ++++++++++++++++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/nova/core/roles/machine_operations/tasks/aws/create.yml b/nova/core/roles/machine_operations/tasks/aws/create.yml index fe32c6f4..85e6a589 100644 --- a/nova/core/roles/machine_operations/tasks/aws/create.yml +++ b/nova/core/roles/machine_operations/tasks/aws/create.yml @@ -150,7 +150,7 @@ - name: Setting connection address to {{ eip_info.public_ip | default(created_ec2_instance.instances[0].public_ip_address) - | default(existing_instances[-1].public_ip_address) | default('N/A') }}... + | default(existing_instances[-1].public_ip_address) | default('N/A') }} ansible.builtin.set_fact: connection_address: "{{ eip_info.public_ip | default(created_ec2_instance.instances[0].public_ip_address) diff --git a/nova/core/roles/updates/defaults/main.yml b/nova/core/roles/updates/defaults/main.yml index 4bf3abcd..29a96cd4 100644 --- a/nova/core/roles/updates/defaults/main.yml +++ b/nova/core/roles/updates/defaults/main.yml @@ -48,3 +48,10 @@ reboot_on_finalize: false # This variable can be set to true to always reboot after updates are applied # This is required because some updates will stop services like VMTools or QEMU Guest Agent from working properly until the system is rebooted updates_freebsd_reboot_after_updates: true + +######################## +# pfSense and OPNsense # +######################## + +# Whether to do a major and minor version upgrade for OPNsense systems +updates_opnsense_upgrade: false diff --git a/nova/core/roles/updates/tasks/sense.yml b/nova/core/roles/updates/tasks/sense.yml index e8efec97..365553f6 100644 --- a/nova/core/roles/updates/tasks/sense.yml +++ b/nova/core/roles/updates/tasks/sense.yml @@ -11,7 +11,42 @@ changed_when: pfsense_update.stdout is search("Installing.*") when: ansible_network_os | default('') == "pfsense" -- name: Rebooting... - ansible.builtin.reboot: +- name: Gathering facts with potentially updated Python version for {{ inventory_hostname }}... # noqa: no-handler + when: opnsense_update.changed or pfsense_update.changed + block: + - name: Clearing existing facts for {{ inventory_hostname }}... + ansible.builtin.meta: clear_facts + + # The Python might get updated with the previous task + - name: Looking up the current Python version... + ansible.builtin.raw: python3 --version | awk '{print $2}' | cut -d. -f1,2 + changed_when: false + register: current_python_version + + - name: Re-gathering facts for {{ inventory_hostname }}... + ansible.builtin.setup: + gather_timeout: 60 + vars: + ansible_python_interpreter: /usr/local/bin/python{{ current_python_version.stdout }} + +- name: Upgrading {{ inventory_hostname }}... + ansible.builtin.command: opnsense-update -u + register: opnsense_upgrade + changed_when: opnsense_upgrade.stdout is search("Please reboot.*") + failed_when: + - opnsense_upgrade.rc != 0 + - opnsense_upgrade.stdout is not search("No known packages set to fetch was specified.") + when: + - ansible_network_os | default('') == "opnsense" + - updates_opnsense_upgrade + +- name: Rebooting and re-including updates tasks... when: ("Please reboot." in opnsense_update.stdout_lines | default('')) + or ("Please reboot." in opnsense_upgrade.stdout_lines | default('')) or ("Please reboot." in pfsense_update.stdout_lines | default('')) + block: + - name: Rebooting... + ansible.builtin.reboot: + + - name: Re-including updates tasks in case there are more updates available... + ansible.builtin.include_tasks: sense.yml From 34df4cdec12c3d6273fa2111b206ada5a4b66058 Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Tue, 28 Jul 2026 14:07:08 +0300 Subject: [PATCH 16/18] Added nmcli network configuration method support for AWS EC2 hosts --- .../configure_networking/tasks/aws/nmcli.yml | 89 +++++++++++++++++++ .../tasks/aws/windows_cli.yml | 1 - .../configure_networking/templates/nmcli.sh | 4 +- 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 nova/core/roles/configure_networking/tasks/aws/nmcli.yml diff --git a/nova/core/roles/configure_networking/tasks/aws/nmcli.yml b/nova/core/roles/configure_networking/tasks/aws/nmcli.yml new file mode 100644 index 00000000..321acda0 --- /dev/null +++ b/nova/core/roles/configure_networking/tasks/aws/nmcli.yml @@ -0,0 +1,89 @@ +--- +- name: Purging existing networking packages... + ansible.builtin.package: + name: + - netplan.io + - cloud-init + state: absent + purge: true + become: true + +- name: Cleaning up old network configuration files... + ansible.builtin.shell: | + rm -rf /etc/systemd/network/* + rm -rf /etc/netplan/* + changed_when: true + become: true + +- name: Installing NetworkManager package... + ansible.builtin.package: + name: network-manager + state: present + update_cache: true + become: true + +- name: Templating nmcli configuration files... + ansible.builtin.template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: "0644" + lstrip_blocks: true + loop_control: + label: "{{ item.src }}" + loop: + - src: 70-persistent-net.rules + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + - src: nmcli.sh + dest: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_nmcli.sh + become: false + delegate_to: localhost + +- name: Uploading nmcli configuration files... + ansible.builtin.copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: "{{ item.mode }}" + loop_control: + label: "{{ item.src }}" + loop: + - src: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_70-persistent-net.rules + dest: /etc/udev/rules.d/70-persistent-net.rules + mode: "0644" + - src: /tmp/{{ project_fullname | default('') }}_{{ inventory_hostname }}_nmcli.sh + dest: /tmp/nmcli.sh + mode: "0755" + become: true + +- name: Running nmcli configuration script... + ansible.builtin.command: /tmp/nmcli.sh + changed_when: true + become: true + async: 3600 + poll: 0 + +- name: Waiting until SSH is down for {{ custom_vm_name | default(vm_name) }} over {{ connection_address }}... + ansible.builtin.wait_for: + host: "{{ connection_address }}" + port: 22 + state: stopped + timeout: 300 + become: false + delegate_to: localhost + +- name: Waiting until SSH is up for {{ custom_vm_name | default(vm_name) }} over {{ connection_address }}... + ansible.builtin.wait_for: + host: "{{ connection_address }}" + port: 22 + state: started + timeout: 300 + become: false + delegate_to: localhost +# - name: Removing temporary files... +# ansible.builtin.file: +# path: /var/tmp/nmcli.sh +# state: absent +# - name: Rebooting {{ custom_vm_name | default(vm_name) }} VM... +# ansible.builtin.include_role: +# name: nova.core.powerstate +# vars: +# restart: true diff --git a/nova/core/roles/configure_networking/tasks/aws/windows_cli.yml b/nova/core/roles/configure_networking/tasks/aws/windows_cli.yml index 99c04baa..1fa8ec5f 100644 --- a/nova/core/roles/configure_networking/tasks/aws/windows_cli.yml +++ b/nova/core/roles/configure_networking/tasks/aws/windows_cli.yml @@ -1,5 +1,4 @@ --- -# This step is not actually very useful for debugging - name: Templating network configuration script... ansible.builtin.template: src: Configure-Network.ps1 diff --git a/nova/core/roles/configure_networking/templates/nmcli.sh b/nova/core/roles/configure_networking/templates/nmcli.sh index 201508f2..03e9cc6f 100644 --- a/nova/core/roles/configure_networking/templates/nmcli.sh +++ b/nova/core/roles/configure_networking/templates/nmcli.sh @@ -85,4 +85,6 @@ fi {% endif %} {% endfor %} -nmcli networking off && nmcli networking on +{% if infra_env == "aws" %} +reboot +{% endif %} From 78938ca40e7e9e3205275b75691126d9a8da365d Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Tue, 28 Jul 2026 14:08:42 +0300 Subject: [PATCH 17/18] Removing cloud-init from Linux OSs in cloud env after initial config is done --- .../core/roles/os_configuration/tasks/linux_cloud.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nova/core/roles/os_configuration/tasks/linux_cloud.yml b/nova/core/roles/os_configuration/tasks/linux_cloud.yml index 2c48ed0d..8d12188b 100644 --- a/nova/core/roles/os_configuration/tasks/linux_cloud.yml +++ b/nova/core/roles/os_configuration/tasks/linux_cloud.yml @@ -1,9 +1,10 @@ --- -- name: Removing cloud managed hostname... - ansible.builtin.replace: - dest: /etc/cloud/cloud.cfg - regexp: ^ - update_etc_hosts - replace: "# - update_etc_hosts" +# After the initial configuration is done its not needed and will slow down boot in most cases +- name: Purging cloud-init... + ansible.builtin.package: + name: cloud-init + state: absent + purge: true - name: Installing Gnome for Ubuntu Desktop... when: group_names | select('match', '^os_ubuntu_desktop') | list | length > 0 From 04001c9ef8223c2fb88def63be6a47a02258142f Mon Sep 17 00:00:00 2001 From: Allar Viik Date: Tue, 28 Jul 2026 14:11:05 +0300 Subject: [PATCH 18/18] In `updates` role for FreeBSD setting the current Python version after update to avoid play failures --- nova/core/roles/updates/tasks/freebsd.yml | 26 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/nova/core/roles/updates/tasks/freebsd.yml b/nova/core/roles/updates/tasks/freebsd.yml index 45d2b743..38c4a0fd 100644 --- a/nova/core/roles/updates/tasks/freebsd.yml +++ b/nova/core/roles/updates/tasks/freebsd.yml @@ -6,8 +6,24 @@ autoremove: true register: pkgng_update -- name: Rebooting if required after package updates... # noqa: no-handler - ansible.builtin.reboot: - when: - - pkgng_update.changed - - updates_freebsd_reboot_after_updates | bool +- name: Gathering facts with potentially updated Python version for {{ inventory_hostname }}... # noqa: no-handler + when: pkgng_update.changed + block: + - name: Clearing existing facts for {{ inventory_hostname }}... + ansible.builtin.meta: clear_facts + + # The Python might get updated with the previous task + - name: Looking up the current Python version... + ansible.builtin.raw: python3 --version | awk '{print $2}' | cut -d. -f1,2 + changed_when: false + register: current_python_version + + - name: Re-gathering facts for {{ inventory_hostname }}... + ansible.builtin.setup: + gather_timeout: 60 + vars: + ansible_python_interpreter: /usr/local/bin/python{{ current_python_version.stdout }} + + - name: Rebooting if required after package updates... # noqa: no-handler + ansible.builtin.reboot: + when: updates_freebsd_reboot_after_updates | bool